Add Monitor

master
Sebastian Serfling 2025-10-29 09:56:52 +01:00
parent 480d6e9a11
commit 610e7e8ca5
1 changed files with 168 additions and 0 deletions

168
Monitor.py Normal file
View File

@ -0,0 +1,168 @@
import customtkinter as ctk
import os
import threading
import datetime
from pystray import Icon as TrayIcon, Menu, MenuItem
from PIL import Image, ImageDraw
import sys
import subprocess
import webbrowser
import psutil
LOG_REFRESH_SECONDS = 10
MAIN_DIR = os.getcwd()
LOG_DIR = os.path.join(MAIN_DIR, 'Logs')
EXPORTER_LOG = os.path.join(LOG_DIR, f"MSSQL_exporter_log_{datetime.datetime.now().strftime('%Y-%m-%d')}.txt")
IMPORTER_LOG = os.path.join(LOG_DIR, f"SDF_importer_log_{datetime.datetime.now().strftime('%Y-%m-%d')}.txt")
MAIN_EXE = os.path.join(MAIN_DIR, 'main.exe')
ENV_FILE = os.path.join(MAIN_DIR, '.env')
def resource_path(relative_path):
"""Korrekt aufgelöst für PyInstaller und normale Ausführung"""
try:
base_path = sys._MEIPASS
except AttributeError:
base_path = os.path.abspath('.')
return os.path.join(base_path, relative_path)
class LogMonitorApp(ctk.CTk):
def __init__(self):
super().__init__()
self.title('SDF-Dienstüberwachung')
self.geometry('500x350')
ctk.set_appearance_mode('dark')
ctk.set_default_color_theme('blue')
self.title_label = ctk.CTkLabel(self, text='SDF Monitor', font=ctk.CTkFont(size=24, weight='bold'))
self.title_label.pack(pady=(10, 0))
self.status_label = ctk.CTkLabel(self, text='Letzte Ausführungen laden...')
self.status_label.pack(pady=(20, 0))
self.seconds_remaining = LOG_REFRESH_SECONDS
self.countdown_label = ctk.CTkLabel(self, text=f'Nächster Check in: {self.seconds_remaining}s')
self.countdown_label.pack(pady=5)
self.update_logs()
self.update_countdown()
self.folder_button = ctk.CTkButton(self, text='Logs-Ordner öffnen', command=self.open_log_folder)
self.folder_button.pack(pady=10)
self.env_button = ctk.CTkButton(self, text='.env Datei öffnen', command=self.open_env_file)
self.env_button.pack(pady=5)
self.restart_button = ctk.CTkButton(self, text='Services neu starten', command=self.restart_main_exe)
self.restart_button.pack(pady=10)
self.stop_button = ctk.CTkButton(self, text='Services beenden', command=self.stop_main_exe)
self.stop_button.pack(pady=5)
logo_path = resource_path('logo.png')
if os.path.exists(logo_path):
from PIL import Image as PILImage
logo_img = PILImage.open(logo_path).resize((120, 40))
self.logo_image = ctk.CTkImage(light_image=logo_img, dark_image=logo_img, size=(120, 40))
self.logo_label = ctk.CTkLabel(self, image=self.logo_image, text='', cursor='hand2')
else:
self.logo_label = ctk.CTkLabel(self, text='www.deine-website.de', text_color='skyblue', cursor='hand2')
self.logo_label.pack(pady=5)
self.logo_label.bind('<Button-1>', lambda e: webbrowser.open('https://www.deine-website.de'))
self.protocol('WM_DELETE_WINDOW', self.hide_to_tray)
self.bind('<Unmap>', self.on_minimize)
self.create_tray_icon()
def format_time(self, path):
if os.path.exists(path):
t = datetime.datetime.fromtimestamp(os.path.getmtime(path))
return t.strftime('%Y-%m-%d %H:%M:%S')
return 'Nie'
def update_logs(self):
exporter_time = self.format_time(EXPORTER_LOG)
importer_time = self.format_time(IMPORTER_LOG)
self.status_label.configure(text=f'Letzter Export: {exporter_time}\nLetzter Import: {importer_time}')
def update_countdown(self):
self.countdown_label.configure(text=f'Nächster Check in: {self.seconds_remaining}s')
self.seconds_remaining -= 1
if self.seconds_remaining <= 0:
self.update_logs()
self.seconds_remaining = LOG_REFRESH_SECONDS
self.after(1000, self.update_countdown)
def restart_main_exe(self):
try:
if os.path.exists(MAIN_EXE):
subprocess.Popen([MAIN_EXE])
self.write_status('main.exe wurde neu gestartet.')
else:
self.write_status('main.exe nicht gefunden.')
except Exception as e:
self.write_status(f'Fehler beim Starten von main.exe: {e}')
def write_status(self, msg):
now = datetime.datetime.now().strftime('%H:%M:%S')
self.status_label.configure(text=f'[{now}] {msg}')
def open_log_folder(self):
os.startfile(LOG_DIR)
def open_env_file(self):
if os.path.exists(ENV_FILE):
os.startfile(ENV_FILE)
else:
self.write_status('.env Datei nicht gefunden.')
def hide_to_tray(self):
self.withdraw()
self.tray_icon.visible = True
def show_from_tray(self, icon=None, item=None):
self.deiconify()
self.tray_icon.visible = False
def quit_app(self, icon, item):
self.tray_icon.stop()
self.destroy()
sys.exit()
def on_minimize(self, event):
if self.state() == 'iconic':
self.hide_to_tray()
def create_tray_icon(self):
icon_path = resource_path('icon.ico')
if os.path.exists(icon_path):
image = Image.open(icon_path).resize((64, 64))
else:
image = Image.new('RGB', (64, 64), 'black')
draw = ImageDraw.Draw(image)
draw.rectangle((16, 16, 48, 48), fill='white')
menu = Menu(MenuItem('Öffnen', self.show_from_tray), MenuItem('Beenden', self.quit_app))
self.tray_icon = TrayIcon('SDF-Monitor', image, 'SDF-Monitor', menu)
threading.Thread(target=self.tray_icon.run, daemon=True).start()
def stop_main_exe(self):
found = False
for proc in psutil.process_iter(['pid', 'name']):
try:
if proc.info['name'].lower() == 'main.exe':
proc.kill()
found = True
self.write_status('main.exe wurde beendet.')
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
if not found:
self.write_status('main.exe läuft nicht.')
if __name__ == '__main__':
app = LogMonitorApp()
app.mainloop()