From 6f831bc419883e63c5720507af8e764ad21e5764 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Thu, 15 May 2025 11:50:25 +0200 Subject: [PATCH 1/6] =?UTF-8?q?Umschalter=20f=C3=BCr=20Zeit=C3=BCbersicht?= =?UTF-8?q?=20Benutzer=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin.py | 4 ++-- homepage.py | 23 ++++++++++++++++++----- users/testuser1/2025-5.txt | 2 ++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/admin.py b/admin.py index f20a4d0..8d4f955 100644 --- a/admin.py +++ b/admin.py @@ -192,7 +192,7 @@ def page_admin(): # Alle Timestamps durchgehen und sie den Dictionaryeinträgen zuordnen: for stamp in timestamps: - day_of_month_of_timestamp = int(datetime.datetime.fromtimestamp(int(stamp)).strftime("%-d")) + day_of_month_of_timestamp = int(datetime.datetime.fromtimestamp(int(stamp)).day) timestamps_dict[day_of_month_of_timestamp].append(int(stamp)) general_saldo = 0 @@ -338,7 +338,7 @@ Dies kann nicht rückgängig gemacht werden!''') # Suche mir alle timestamps für diesen Tag for i in timestamps: actual_timestamp = datetime.datetime.fromtimestamp(int(i)) - timestamp_day = actual_timestamp.strftime('%-d') + timestamp_day = actual_timestamp.day if int(timestamp_day) == int(day): timestamps_of_this_day.append(i) diff --git a/homepage.py b/homepage.py index a26ca77..57b36f5 100644 --- a/homepage.py +++ b/homepage.py @@ -26,6 +26,12 @@ def homepage(): pageheader(f"Willkommen, {current_user.fullname}") today = datetime.datetime.now() + def yesterdays_overtime(): + last_months_overtime = current_user.get_last_months_overtime(today.year, today.month) + overtime_this_month = 0 + for i in range(1, today.day): + overtime_this_month += (int(current_user.get_worked_time(today.year, today.month, i)[0]) - int(current_user.get_day_workhours(today.year, today.month, i))) + return last_months_overtime + overtime_this_month @ui.refreshable def stamp_interface(): @@ -38,6 +44,16 @@ def homepage(): with ui.grid(columns='20% auto 20%').classes('w-full justify-center'): ui.space() + + def update_timer(): + additional_time = 0 + if time_toggle.value == "total": + additional_time = yesterdays_overtime() + if current_user.get_worked_time(today.year, today.month, today.day)[1] > 0: + time_in_total = additional_time + time_so_far + int((datetime.datetime.now().timestamp() - current_user.get_worked_time(today.year, today.month, today.day)[1])) + else: + time_in_total = additional_time + time_so_far + working_hours.set_content(convert_seconds_to_hours(time_in_total)) with ui.grid(columns='1fr 1fr'): if current_user.stamp_status() == status_in: bg_color = 'green' @@ -46,10 +62,8 @@ def homepage(): working_hours = ui.markdown(convert_seconds_to_hours(time_so_far)).classes(f'col-span-2 rounded-3xl text-center text-white text-bold text-2xl border-4 border-gray-600 bg-{bg_color}') in_button = ui.button("Einstempeln", on_click=stamp_and_refresh).classes('bg-green') out_button = ui.button("Ausstempeln", on_click=stamp_and_refresh).classes('bg-red') - - def update_timer(): - time_in_total = time_so_far + int((datetime.datetime.now().timestamp() - current_user.get_worked_time(today.year, today.month, today.day)[1])) - working_hours.set_content(convert_seconds_to_hours(time_in_total)) + time_toggle = ui.toggle({"day": "Tagesarbeitszeit", "total": "Gesamtzeit"}, value="day", + on_change=update_timer).classes('w-full justify-center col-span-2') working_timer = ui.timer(1.0, update_timer) working_timer.active = False @@ -93,7 +107,6 @@ def homepage(): month_month_select.enable() with ui.grid(columns='1fr auto 1fr').classes('w-full justify-center'): - ui.space() with ui.expansion("Tagesnotiz", icon='o_description'): with ui.grid(columns=2): diff --git a/users/testuser1/2025-5.txt b/users/testuser1/2025-5.txt index a5ae764..96885a2 100644 --- a/users/testuser1/2025-5.txt +++ b/users/testuser1/2025-5.txt @@ -12,3 +12,5 @@ 1747214813 1747216800 1747220619 +1747301302 +1747301459 From 8fcc2e1b4eb8ec7ea98b38f9cf3e23d1193cd0b8 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Thu, 15 May 2025 12:17:51 +0200 Subject: [PATCH 2/6] =?UTF-8?q?Tooltips=20in=20Abwesenheits=C3=BCbersicht?= =?UTF-8?q?=20Fehlerbehebung=20Homepage=20User?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api.py | 13 +++++++++++-- homepage.py | 11 +++++++---- users/testuser1/2025-5.txt | 4 ++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/api.py b/api.py index 5811694..ca2816b 100644 --- a/api.py +++ b/api.py @@ -387,17 +387,26 @@ def page_overview_absence(username: str, year: int): if str(column) in list(absences): bg_color = absence_entries[absences[str(column)]]['color'] text_color = absence_entries[absences[str(column)]]['text-color'] - ui.markdown(absences[str(column)]).classes(f'border px-{pad_x} py-{pad_y} bg-{bg_color} text-{text_color} text-center') + tooltip_text = absence_entries[absences[str(column)]]['name'] + with ui.element(): + ui.markdown(absences[str(column)]).classes(f'border px-{pad_x} py-{pad_y} bg-{bg_color} text-{text_color} align-middle text-center') + ui.tooltip(tooltip_text) else: + tooltip_text = "" if column > monthrange(year, month)[1]: bg_color = 'gray-500' + tooltip_text="Tag exisitiert nicht" elif int(current_user.get_day_workhours(year, month, column)) == 0: bg_color = 'gray-300' + tooltip_text = "Kein Arbeitstag" elif int(current_user.get_day_workhours(year, month, column)) == -1: bg_color = 'gray-400' + tooltip_text = "Kein Arbeitsverhältnis" else: bg_color = 'inherit' - ui.space().classes(f'border px-{pad_x} py-{pad_y} bg-{bg_color}') + with ui.label("").classes(f'border px-{pad_x} py-{pad_y} bg-{bg_color}'): + if tooltip_text != "": + ui.tooltip(tooltip_text) absence_calender() diff --git a/homepage.py b/homepage.py index 57b36f5..eb322a4 100644 --- a/homepage.py +++ b/homepage.py @@ -89,10 +89,10 @@ def homepage(): binder_available_years = ValueBinder() - binder_vacation = ValueBinder + binder_vacation = ValueBinder() binder_vacation.value = False - binder_absence = ValueBinder + binder_absence = ValueBinder() binder_absence.value = False def enable_month(): @@ -150,6 +150,9 @@ def homepage(): def activate_vacation(): binder_vacation.value = True + def activate_absence(): + binder_absence.value = True + with ui.grid(columns='1fr 1fr'): ui.markdown("**Monatsübersicht:**").classes('col-span-2') @@ -164,8 +167,8 @@ def homepage(): vacation_select = ui.select(list(reversed(available_years)), on_change=activate_vacation) vacation_button = ui.button("Anzeigen", on_click=lambda: ui.navigate.to(f"/api/vacation/{current_user.username}/{vacation_select.value}", new_tab=True)).bind_enabled_from(binder_vacation, 'value') ui.markdown("**Fehlzeitenübersicht**").classes('col-span-2') - absences_select = ui.select(list(reversed(available_years))) - absences_button = ui.button("Anzeigen").bind_enabled_from(binder_absence, 'value') + absences_select = ui.select(list(reversed(available_years)), on_change=activate_absence) + absences_button = ui.button("Anzeigen", on_click=lambda: ui.navigate.to(f"api/absence/{current_user.username}/{absences_select.value}", new_tab=True)).bind_enabled_from(binder_absence, 'value') ui.separator().classes('col-span-2') def logout(): diff --git a/users/testuser1/2025-5.txt b/users/testuser1/2025-5.txt index 96885a2..cdc85f6 100644 --- a/users/testuser1/2025-5.txt +++ b/users/testuser1/2025-5.txt @@ -14,3 +14,7 @@ 1747220619 1747301302 1747301459 +1747302876 +1747302887 +1747302889 +1747302897 From 67e68ffa2c6d756e50cc9e6e0d56335bc8a74fd1 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Fri, 16 May 2025 10:25:43 +0200 Subject: [PATCH 3/6] =?UTF-8?q?Pfadkonstruktionen=20f=C3=BCr=20Multiplattf?= =?UTF-8?q?orm=20umgestellt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- users.py | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/users.py b/users.py index 6dd0fec..67f934f 100644 --- a/users.py +++ b/users.py @@ -17,9 +17,9 @@ from definitions import userfolder, scriptpath, usersettingsfilename, photofilen class user: def __init__(self, name): - self.userfolder = f"{scriptpath}/{userfolder}/{name}" - self.settingsfile = f"{self.userfolder}/{usersettingsfilename}" - self.photofile = f"{self.userfolder}/{photofilename}" + self.userfolder = os.path.join(scriptpath, userfolder, name) + self.settingsfile = os.path.join(self.userfolder, usersettingsfilename) + self.photofile = os.path.join(self.userfolder, photofilename) # Stammdaten einlesen try: @@ -43,7 +43,7 @@ class user: else: year = str(datetime.datetime.fromtimestamp(time_stamp).year) month = str(datetime.datetime.fromtimestamp(time_stamp).month) - completepath = f"{self.userfolder}/{year}-{month}" + completepath = os.path.join(self.userfolder, f"{year}-{month}") return completepath def timestamp(self, stamptime=-1): @@ -87,7 +87,7 @@ class user: # Zähle die Zeilen lines = file.readlines() except FileNotFoundError: - print(f"Die Datei {self.get_stamp_file()} wurde nicht gefunden.") + print(f"Die Datei {self.get_stamp_file()}.txt wurde nicht gefunden.") print("Lege die Datei an.") with open(f'{self.get_stamp_file()}.txt', 'w') as file: file.write("") @@ -132,10 +132,10 @@ class user: outputfile.write(json_dict) pathcheck = self.userfolder - pathcheck = pathcheck.removeprefix(f"{scriptpath}/{userfolder}/") + pathcheck = pathcheck.removeprefix(os.path.join(scriptpath, userfolder)) if pathcheck != self.username: - os.rename(self.userfolder, f"{scriptpath}/{userfolder}/{self.username}") + os.rename(self.userfolder, os.path.join(scriptpath, userfolder, self.username)) def del_user(self): shutil.rmtree(self.userfolder) @@ -204,7 +204,7 @@ class user: def get_timestamps(self, year, month): try: - with open(f"{self.userfolder}/{year}-{month}.txt", "r") as file: + with open(os.path.join(self.userfolder, f"{year}-{month}.txt"), "r") as file: timestamps = file.readlines() timestamps.sort() return timestamps @@ -218,14 +218,14 @@ class user: def get_archive_status(self, year, month): try: - with open(f"{self.userfolder}/{year}-{month}.json", 'r') as json_file: + with open(os.path.join(self.userfolder, f"{year}-{month}.json"), 'r') as json_file: data = json.load(json_file) return data["archived"] except: return -1 def archiving_validity_check(self, year: int, month: int): - timestampfilename = f"{self.userfolder}/{year}-{month}.txt" + timestampfilename = os.path.join(self.userfolder, f"{year}-{month}.txt") try: with open(timestampfilename) as timestampfile: timestamps = timestampfile.readlines() @@ -246,7 +246,7 @@ class user: def archive_hours(self, year, month, overtime: int): - filename = f"{self.userfolder}/{year}-{month}.json" + filename = os.path.join(self.userfolder, f"{year}-{month}.json") with open(filename, 'r') as json_file: data = json.load(json_file) data["archived"] = 1 @@ -258,7 +258,7 @@ class user: outputfile.write(json_dict) # Dateien auf readonly setzen os.chmod(filename, S_IREAD) - filename_txt = f"{self.userfolder}/{year}-{month}.txt" + filename_txt = os.path.join(self.userfolder, f"{year}-{month}.txt") os.chmod(filename_txt, S_IREAD) def get_last_months_overtime(self, year, month): @@ -268,7 +268,7 @@ class user: month = str(12) else: month = str(int(month) - 1) - with open(f"{self.userfolder}/{year}-{month}.json", "r") as json_file: + with open(os.path.join(self.userfolder, f"{year}-{month}.json"), "r") as json_file: json_data = json.load(json_file) if json_data["archived"] == 1: @@ -281,7 +281,7 @@ class user: def get_absence(self, year, month): try: - with open(f"{self.userfolder}/{year}-{month}.json", "r") as json_file: + with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "r") as json_file: json_data = json.load(json_file) absence = json_data["absence"] return absence @@ -290,7 +290,7 @@ class user: def get_day_notes(self, year, month, day): try: - with open(f"{self.userfolder}/{int(year)}-{int(month)}.json", "r") as json_file: + with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "r") as json_file: json_data = json.load(json_file) day_note = json_data["notes"][str(day)] return day_note @@ -298,7 +298,7 @@ class user: return { } def write_notes(self, year, month, day, note_dict): - with open(f"{self.userfolder}/{int(year)}-{int(month)}.json", "r") as json_file: + with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "r") as json_file: json_data = json.load(json_file) if len(note_dict) == 1: json_data["notes"][str(day)] = { } @@ -309,16 +309,16 @@ class user: json_data["notes"][str(day)] = note_dict json_output = json.dumps(json_data, indent=4) - with open(f"{self.userfolder}/{int(year)}-{int(month)}.json", "w") as json_file: + with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "w") as json_file: json_file.write(json_output) def update_absence(self, year, month, day, absence_type): try: - with open(f"{self.userfolder}/{int(year)}-{int(month)}.json", "r") as json_file: + with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "r") as json_file: json_data = json.load(json_file) except: - with open(f"{self.userfolder}/{year}-{month}.json", "w") as json_file: + with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "w") as json_file: json_data = { } json_data["archived"] = 0 json_data["overtime"] = 0 @@ -330,17 +330,17 @@ class user: json_data.update({ "absence": { str(int(day)): absence_type}}) json_dict = json.dumps(json_data, indent=4) - with open(f"{self.userfolder}/{int(year)}-{int(month)}.json", "w") as json_file: + with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "w") as json_file: json_file.write(json_dict) def del_absence(self, year, month, day): - with open(f"{self.userfolder}/{int(year)}-{int(month)}.json", "r") as json_file: + with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "r") as json_file: json_data = json.load(json_file) del json_data["absence"][str(day)] json_dict = json.dumps(json_data, indent=4) - with open(f"{self.userfolder}/{int(year)}-{int(month)}.json", "w") as json_file: + with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "w") as json_file: json_file.write(json_dict) def get_day_workhours(self, year, month, day): @@ -482,7 +482,7 @@ def new_user(username: str): # Admineinstellungen auslesen def load_adminsettings(): # Settingsdatei einlesen - settings_filename = f"{scriptpath}/{usersettingsfilename}" + settings_filename = os.path.join(scriptpath, usersettingsfilename) if not os.path.exists(settings_filename): print("Keine Einstellungsdatei gefunden. Lege Standarddatei an.") with open(settings_filename, 'w') as json_file: From 859d55c99aec5c8d4f7380334dbf322a9b70de71 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Fri, 16 May 2025 13:32:33 +0200 Subject: [PATCH 4/6] =?UTF-8?q?Zus=C3=A4tzliche=20Anzeigefunktionen=20f?= =?UTF-8?q?=C3=BCr=20Touchscreen=20Einstellungen=20f=C3=BCr=20Admininterfa?= =?UTF-8?q?ce,=20f=C3=BCr=20Touchscreen=20Elemente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin.py | 129 +++++++++++++++++++++++------------ definitions.py | 4 ++ homepage.py | 20 +++++- settings.json | 4 ++ touchscreen.py | 50 ++++++++++---- users.py | 15 ++-- users/filler2/2025-5.txt | 0 users/filler2/settings.json | 18 +++++ users/filler3/2025-5.json | 4 ++ users/filler3/2025-5.txt | 2 + users/filler3/settings.json | 18 +++++ users/filler4/2025-5.txt | 0 users/filler4/settings.json | 18 +++++ users/filler5/2025-5.txt | 0 users/filler5/settings.json | 18 +++++ users/filler6/2025-5.txt | 0 users/filler6/settings.json | 18 +++++ users/testuser1/2025-5.txt | 12 ++++ users/testuser10/2025-5.json | 4 ++ users/testuser10/2025-5.txt | 4 ++ users/testuser3/2025-5.txt | 4 ++ 21 files changed, 279 insertions(+), 63 deletions(-) create mode 100644 users/filler2/2025-5.txt create mode 100644 users/filler2/settings.json create mode 100644 users/filler3/2025-5.json create mode 100644 users/filler3/2025-5.txt create mode 100644 users/filler3/settings.json create mode 100644 users/filler4/2025-5.txt create mode 100644 users/filler4/settings.json create mode 100644 users/filler5/2025-5.txt create mode 100644 users/filler5/settings.json create mode 100644 users/filler6/2025-5.txt create mode 100644 users/filler6/settings.json create mode 100644 users/testuser10/2025-5.json diff --git a/admin.py b/admin.py index 8d4f955..7b9bc23 100644 --- a/admin.py +++ b/admin.py @@ -1,9 +1,11 @@ from datetime import datetime import dateutil.easter +from PIL.SpiderImagePlugin import isInt from dateutil.easter import * from nicegui import ui, app, events +from nicegui.html import button from users import * from definitions import * @@ -596,47 +598,89 @@ Dies kann nicht rückgängig gemacht werden!''') button_update.move(timetable_header) with ui.tab_panel(settings): - with ui.card(): - ui.markdown("**Administrationsbenutzer:**") - with ui.grid(columns=2): - def save_admin_settings(): - output_dict = { } - output_dict["admin_user"] = admin_user.value - if admin_password.value != "": - output_dict["admin_password"] = hash_password(admin_password.value) - else: - output_dict["admin_password"] = data["admin_password"] - output_dict["port"] = port.value - output_dict["secret"] = secret - output_dict["holidays"] = data["holidays"] - json_dict = json.dumps(output_dict, indent=4) - with open(f"{scriptpath}/{usersettingsfilename}", "w") as outputfile: - outputfile.write(json_dict) - if int(old_port) != int(port.value): - with ui.dialog() as dialog, ui.card(): - ui.markdown("Damit die Porteinstellungen wirksam werden, muss der Server neu gestartet werden.") - ui.button("OK", on_click=lambda: dialog.close()) - dialog.open() - ui.notify("Einstellungen gespeichert") - timetable.refresh() + with ui.grid(columns='auto auto'): + with ui.card(): + ui.markdown("**Administrationsbenutzer:**") + with ui.grid(columns=2): + def save_admin_settings(): + output_dict = { } + output_dict["admin_user"] = admin_user.value + if admin_password.value != "": + output_dict["admin_password"] = hash_password(admin_password.value) + else: + output_dict["admin_password"] = data["admin_password"] + output_dict["port"] = port.value + output_dict["secret"] = secret + output_dict["times_on_touchscreen"] = timestamp_switch.value + output_dict["photos_on_touchscreen"] = photo_switch.value + output_dict["holidays"] = data["holidays"] + json_dict = json.dumps(output_dict, indent=4) + with open(f"{scriptpath}/{usersettingsfilename}", "w") as outputfile: + outputfile.write(json_dict) + if int(old_port) != int(port.value): + with ui.dialog() as dialog, ui.card(): + ui.markdown("Damit die Porteinstellungen wirksam werden, muss der Server neu gestartet werden.") + ui.button("OK", on_click=lambda: dialog.close()) + dialog.open() + ui.notify("Einstellungen gespeichert") + timetable.refresh() + + ui.markdown("Benutzername des Adminstrators") + admin_user = ui.input().tooltip("Geben Sie hier den Benutzernamen für den Adminstationsnutzer ein") + admin_user.value = data["admin_user"] + ui.markdown("Passwort des Administrators") + admin_password = ui.input(password=True).tooltip("Geben Sie hier das Passwort für den Administationsnutzer ein. Merken Sie sich dieses Passwort gut. Es kann nicht über das Webinterface zurückgesetzt werden.") + + secret = data["secret"] + + with ui.card(): + ui.markdown("**Systemeinstellungen:**") + with ui.grid(columns=2): + def check_is_number(number): + try: + number = int(number) + return True + except: + return False + + ui.markdown("Port:") + port = ui.input(validation={"Nur ganzzahlige Portnummern erlaubt": lambda value: check_is_number(value), + "Portnummer zu klein": lambda value: len(value)>=2}).tooltip("Geben Sie hier die Portnummer ein, unter der die Zeiterfassung erreichbar ist.").props('size=5') + old_port = data["port"] + port.value = old_port - ui.markdown("Benutzername des Adminstrators") - admin_user = ui.input() - admin_user.value = data["admin_user"] - ui.markdown("Passwort des Administrators") - admin_password = ui.input(password=True) - secret = data["secret"] + with ui.card(): + ui.markdown("**Einstellungen für das Stempelterminal:**") + with ui.column(): + timestamp_switch = ui.switch("Stempelzeiten anzeigen") + photo_switch = ui.switch("Fotos anzeigen") + timestamp_switch.value = bool(data["times_on_touchscreen"]) + with ui.row(): + photo_switch.value = bool(data["photos_on_touchscreen"]) + with ui.row().bind_visibility_from(photo_switch, 'value'): + ui.markdown("Maximale Bilderöhe") + picture_height_input = ui.input(validation={"Größe muss eine Ganzzahl sein.": lambda value: check_is_number(value), + "Größe muss größer 0 sein": lambda value: int(value)>0}).props('size=5') + picture_height_input.value = data["picture_height"] + ui.markdown('px') + with ui.row(): + ui.markdown("Maximale Buttonhöhe") + def compare_button_height(height): + if not photo_switch.value: + return True + elif int(height) < int(picture_height_input.value): + return False + else: + return True - with ui.card(): - ui.markdown("**Systemeinstellungen:**") - with ui.grid(columns=2): - - ui.markdown("Port:") - port = ui.input() - old_port = data["port"] - port.value = old_port + button_height_input = ui.input(validation={"Größe muss eine Ganzzahl sein.": lambda value: check_is_number(value), + "Größe muss größer 0 sein": lambda value: int(value)>0, + "Buttons dürfen nicht kleiner als die Fotos sein": lambda value: compare_button_height(value)}).props('size=5') + button_height_input.value = data["button_height"] + photo_switch.on_value_change(button_height_input.validate) + ui.markdown('px') def holiday_section(): with ui.card(): @@ -825,9 +869,9 @@ Dies kann nicht rückgängig gemacht werden!''') with ui.grid(columns='auto auto'): ui.space() with ui.row(): - ui.button("Gesetzliche Feiertage eintragen", on_click=defined_holidays) - ui.button("Eigener Eintrag", on_click=new_holiday_entry) - ui.button("Zurücksetzen", icon="undo", on_click=reset_holidays).bind_visibility_from(reset_visibility, 'value').classes('bg-red') + ui.button("Gesetzliche Feiertage eintragen", on_click=defined_holidays).tooltip("Hier können Sie automatisiert gesetzliche Feiertage in Deutschland eintragen.") + ui.button("Eigener Eintrag", on_click=new_holiday_entry).tooltip("Hier können Sie einen eigenen Feiertag definieren.") + ui.button("Zurücksetzen", icon="undo", on_click=reset_holidays).bind_visibility_from(reset_visibility, 'value').classes('bg-red').tooltip("Hier können Sie ungespeicherte Änderungen zurücknehmen.") ui.separator().classes('col-span-2') for year_entry in year_list: @@ -840,7 +884,7 @@ Dies kann nicht rückgängig gemacht werden!''') holiday_section() - ui.button("Speichern", on_click=save_admin_settings) + ui.button("Speichern", on_click=save_admin_settings).tooltip("Hiermit werden sämtliche oben gemachten Einstellungen gespeichert.") with ui.tab_panel(users): ui.markdown("###Benutzerverwaltung") @@ -1026,7 +1070,8 @@ Dies kann nicht rückgängig gemacht werden!''') with ui.dialog() as dialog, ui.card(): ui.markdown("Geben Sie den Benutzernamen für das neue Konto an:") user_name_input = ui.input(label="Benutzername", validation={'Leerer Benutzername nicht erlaubt': lambda value: len(value) != 0, - 'Leerzeichen im Benutzername nicht erlaubt': lambda value: " " not in value}) + 'Leerzeichen im Benutzername nicht erlaubt': lambda value: " " not in value, + 'Benutzername schon vergeben': lambda value: value not in userlist}).on('keypress.enter', create_new_user) with ui.row(): ui.button("OK", on_click=create_new_user) ui.button("Abbrechen", on_click=dialog.close) diff --git a/definitions.py b/definitions.py index 83260ad..fe8c4e7 100644 --- a/definitions.py +++ b/definitions.py @@ -26,6 +26,10 @@ standard_adminsettings = { "admin_user": "admin", "admin_password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918", "port": "8090", "secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise", + "times_on_touchscreen": True, + "photos_on_touchscreen": True, + "picure_height": 200, + "button_height": 300, "holidays": { } } diff --git a/homepage.py b/homepage.py index eb322a4..2636be5 100644 --- a/homepage.py +++ b/homepage.py @@ -1,7 +1,9 @@ # Zeiterfassung import datetime -from nicegui import ui, app +from nicegui import ui, app, Client +from nicegui.page import page + from users import * from definitions import * @@ -179,4 +181,18 @@ def homepage(): ui.space() else: - login_mask() \ No newline at end of file + login_mask() + +# 404 Fehlerseite +@app.exception_handler(404) +async def exception_handler_404(request, exception: Exception): + with Client(page(''), request=request) as client: + pageheader("Fehler 404") + ui.label("Diese Seite existiert nicht.") + ui.label("Was möchten Sie tun?") + with ui.list().props('dense'): + with ui.item(): + ui.link("zur Startseite", "/") + with ui.item(): + ui.link("zum Administratrionsbereich", "/admin") + return client.build_response(request, 404) \ No newline at end of file diff --git a/settings.json b/settings.json index 2add415..d02a2c2 100644 --- a/settings.json +++ b/settings.json @@ -3,6 +3,10 @@ "admin_password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918", "port": "8090", "secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise", + "times_on_touchscreen": true, + "photos_on_touchscreen": true, + "button_height": 300, + "picture_height": 200, "holidays": { "2025-01-01": "Neujahr", "2025-04-18": "Karfreitag", diff --git a/touchscreen.py b/touchscreen.py index f2e73c7..7f3ee85 100644 --- a/touchscreen.py +++ b/touchscreen.py @@ -26,8 +26,10 @@ def page_touchscreen(): # ui.notify(status_out) user_buttons.refresh() - pageheader("Bitte User auswählen:") + pageheader("Stempeluhr") + ui.page_title("Stempeluhr") + admin_settings = load_adminsettings() userlist = list_users() number_of_users = len(userlist) buttons = { } @@ -39,22 +41,40 @@ def page_touchscreen(): else: number_of_columns = number_of_users - with ui.grid(columns=number_of_columns): + with ui.grid(columns=number_of_columns).classes('w-full center'): for name in userlist: current_user = user(name) - current_button = ui.button(on_click=lambda name=name: button_click(name)) + current_button = ui.button(on_click=lambda name=name: button_click(name)).classes('w-md h-full min-h-[250px]') with current_button: - try: - with open(current_user.photofile, 'r') as file: + if admin_settings["photos_on_touchscreen"]: + try: + with open(current_user.photofile, 'r') as file: + pass + file.close() + ui.image(current_user.photofile).classes('max-h-[200px]').props('fit=scale-down') + except: pass - file.close() - ui.image(current_user.photofile) - except: - pass - ui.label(current_user.fullname) - if current_user.stamp_status() == status_in: - current_button.props('color=green') - else: - current_button.props('color=red') - buttons[name] = current_button + column_classes = "w-full items-center" + if admin_settings["times_on_touchscreen"] or admin_settings["photos_on_touchscreen"]: + column_classes += " self-end" + with ui.column().classes(column_classes): + if admin_settings["times_on_touchscreen"]: + todays_timestamps = current_user.get_day_timestamps() + # Wenn wir Einträge haben + if len(todays_timestamps) > 0 and admin_settings["times_on_touchscreen"]: + table_string = "" + for i in range(0, len(todays_timestamps), 2): + try: + table_string += f"{datetime.datetime.fromtimestamp(todays_timestamps[i]).strftime('%H:%M')} - {datetime.datetime.fromtimestamp(todays_timestamps[i+1]).strftime('%H:%M')}" + except IndexError: + table_string += f"{datetime.datetime.fromtimestamp(todays_timestamps[i]).strftime('%H:%M')} -" + if i < len(todays_timestamps) - 2: + table_string += ", " + ui.markdown(table_string) + ui.label(current_user.fullname).classes('text-center') + if current_user.stamp_status() == status_in: + current_button.props('color=green') + else: + current_button.props('color=red') + buttons[name] = current_button user_buttons() \ No newline at end of file diff --git a/users.py b/users.py index 67f934f..bce4f90 100644 --- a/users.py +++ b/users.py @@ -416,10 +416,10 @@ class user: def delete_photo(self): os.remove(self.photofile) - def get_worked_time(self, year, month, day): + def get_day_timestamps(self, year=datetime.datetime.now().year, month=datetime.datetime.now().month, day=datetime.datetime.now().day): timestamps = self.get_timestamps(year, month) check_day_dt = datetime.datetime(year, month, day) - todays_timestamps = [ ] + todays_timestamps = [] for i in timestamps: i_dt = datetime.datetime.fromtimestamp(int(i)) @@ -427,6 +427,13 @@ class user: todays_timestamps.append(int(i)) todays_timestamps.sort() + + return todays_timestamps + + def get_worked_time(self, year=datetime.datetime.now().year, month=datetime.datetime.now().month, day=datetime.datetime.now().day): + + todays_timestamps = self.get_day_timestamps(year, month, day) + if len(todays_timestamps) % 2 == 0: workrange = len(todays_timestamps) in_time_stamp = -1 @@ -460,8 +467,8 @@ def list_users(): def new_user(username: str): if not os.path.exists(userfolder): os.makedirs(userfolder) - if not os.path.exists(f"{userfolder}/{username}"): - os.makedirs(f"{userfolder}/{username}") + if not os.path.exists(os.path.join(userfolder, username)): + os.makedirs(os.path.join(userfolder, username)) start_date_dt = datetime.datetime.now() start_date = start_date_dt.strftime("%Y-%m-%d") settings_to_write = standard_usersettings diff --git a/users/filler2/2025-5.txt b/users/filler2/2025-5.txt new file mode 100644 index 0000000..e69de29 diff --git a/users/filler2/settings.json b/users/filler2/settings.json new file mode 100644 index 0000000..00f7712 --- /dev/null +++ b/users/filler2/settings.json @@ -0,0 +1,18 @@ +{ + "username": "filler2", + "fullname": "filler2", + "password": "37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f", + "api_key": "0f36286bf8c96de1922ab41e2682ba5a81793525", + "workhours": { + "2025-05-16": { + "1": 0, + "2": 0, + "3": 0, + "4": 0, + "5": 0, + "6": 0, + "7": 0, + "vacation": 0 + } + } +} \ No newline at end of file diff --git a/users/filler3/2025-5.json b/users/filler3/2025-5.json new file mode 100644 index 0000000..b7881be --- /dev/null +++ b/users/filler3/2025-5.json @@ -0,0 +1,4 @@ +{ + "archived": 0, + "total_hours": 0 +} \ No newline at end of file diff --git a/users/filler3/2025-5.txt b/users/filler3/2025-5.txt new file mode 100644 index 0000000..e831471 --- /dev/null +++ b/users/filler3/2025-5.txt @@ -0,0 +1,2 @@ +1747391900 +1747391907 diff --git a/users/filler3/settings.json b/users/filler3/settings.json new file mode 100644 index 0000000..b3fb959 --- /dev/null +++ b/users/filler3/settings.json @@ -0,0 +1,18 @@ +{ + "username": "filler3", + "fullname": "filler3", + "password": "37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f", + "api_key": "9e3f37809cd898a3db340c453df53bd0793a99fa", + "workhours": { + "2025-05-16": { + "1": 0, + "2": 0, + "3": 0, + "4": 0, + "5": 0, + "6": 0, + "7": 0, + "vacation": 0 + } + } +} \ No newline at end of file diff --git a/users/filler4/2025-5.txt b/users/filler4/2025-5.txt new file mode 100644 index 0000000..e69de29 diff --git a/users/filler4/settings.json b/users/filler4/settings.json new file mode 100644 index 0000000..a657bde --- /dev/null +++ b/users/filler4/settings.json @@ -0,0 +1,18 @@ +{ + "username": "filler4", + "fullname": "filler4", + "password": "37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f", + "api_key": "614e31aab9fcf1373558f100cb2c7a9918349eec", + "workhours": { + "2025-05-16": { + "1": 0, + "2": 0, + "3": 0, + "4": 0, + "5": 0, + "6": 0, + "7": 0, + "vacation": 0 + } + } +} \ No newline at end of file diff --git a/users/filler5/2025-5.txt b/users/filler5/2025-5.txt new file mode 100644 index 0000000..e69de29 diff --git a/users/filler5/settings.json b/users/filler5/settings.json new file mode 100644 index 0000000..3b45fe5 --- /dev/null +++ b/users/filler5/settings.json @@ -0,0 +1,18 @@ +{ + "username": "filler5", + "fullname": "filler5", + "password": "37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f", + "api_key": "ad32682beb4e19f78efc1bdae259aee3ccbf9883", + "workhours": { + "2025-05-16": { + "1": 0, + "2": 0, + "3": 0, + "4": 0, + "5": 0, + "6": 0, + "7": 0, + "vacation": 0 + } + } +} \ No newline at end of file diff --git a/users/filler6/2025-5.txt b/users/filler6/2025-5.txt new file mode 100644 index 0000000..e69de29 diff --git a/users/filler6/settings.json b/users/filler6/settings.json new file mode 100644 index 0000000..50c28c8 --- /dev/null +++ b/users/filler6/settings.json @@ -0,0 +1,18 @@ +{ + "username": "filler6", + "fullname": "filler6", + "password": "37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f", + "api_key": "68d974e4ed516795d48d5cb8b7dc8b8ca4144a9b", + "workhours": { + "2025-05-16": { + "1": 0, + "2": 0, + "3": 0, + "4": 0, + "5": 0, + "6": 0, + "7": 0, + "vacation": 0 + } + } +} \ No newline at end of file diff --git a/users/testuser1/2025-5.txt b/users/testuser1/2025-5.txt index cdc85f6..4425bef 100644 --- a/users/testuser1/2025-5.txt +++ b/users/testuser1/2025-5.txt @@ -18,3 +18,15 @@ 1747302887 1747302889 1747302897 +1747386098 +1747386110 +1747387148 +1747387150 +1747387501 +1747387508 +1747387633 +1747387635 +1747387761 +1747388239 +1747388242 +1747388615 diff --git a/users/testuser10/2025-5.json b/users/testuser10/2025-5.json new file mode 100644 index 0000000..b7881be --- /dev/null +++ b/users/testuser10/2025-5.json @@ -0,0 +1,4 @@ +{ + "archived": 0, + "total_hours": 0 +} \ No newline at end of file diff --git a/users/testuser10/2025-5.txt b/users/testuser10/2025-5.txt index e69de29..2ed4771 100644 --- a/users/testuser10/2025-5.txt +++ b/users/testuser10/2025-5.txt @@ -0,0 +1,4 @@ +1747387168 +1747387171 +1747388261 +1747388617 diff --git a/users/testuser3/2025-5.txt b/users/testuser3/2025-5.txt index 4eb306a..4eff463 100644 --- a/users/testuser3/2025-5.txt +++ b/users/testuser3/2025-5.txt @@ -1,2 +1,6 @@ 1746385111 1746385118 +1747388255 +1747388619 +1747391536 +1747391567 From 4def6c50fa1afa800f15f9246478ff9ce0faf1c2 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Sun, 18 May 2025 17:44:41 +0200 Subject: [PATCH 5/6] =?UTF-8?q?Kommandozeilenparameter=20f=C3=BCr=20Adminz?= =?UTF-8?q?ugang=20eingef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin.py | 2 +- main.py | 53 +++++++++++++++++++++++++++++++++++++++++++++------ settings.json | 2 +- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/admin.py b/admin.py index 7b9bc23..dfc6e2d 100644 --- a/admin.py +++ b/admin.py @@ -615,7 +615,7 @@ Dies kann nicht rückgängig gemacht werden!''') output_dict["photos_on_touchscreen"] = photo_switch.value output_dict["holidays"] = data["holidays"] json_dict = json.dumps(output_dict, indent=4) - with open(f"{scriptpath}/{usersettingsfilename}", "w") as outputfile: + with open(os.path.join(scriptpath, usersettingsfilename), "w") as outputfile: outputfile.write(json_dict) if int(old_port) != int(port.value): with ui.dialog() as dialog, ui.card(): diff --git a/main.py b/main.py index 937b047..723636e 100644 --- a/main.py +++ b/main.py @@ -10,6 +10,18 @@ from api import * from homepage import * import json +import argparse + +from web_ui import hash_password + + +class Commandline_Header: + message_string = f"{app_title} {app_version}" + underline = "" + for i in range(len(message_string)): + underline += "-" + print(message_string) + print(underline) def main(): @@ -24,12 +36,7 @@ def main(): def startup_message(): - message_string = f"{app_title} {app_version}" - underline = "" - for i in range(len(message_string)): - underline += "-" - print(message_string) - print(underline) + Commandline_Header() url_string = "" for i in list(app.urls): @@ -41,4 +48,38 @@ def main(): ui.run(favicon="favicon.svg", port=port, storage_secret=secret, language='de-DE', show_welcome_message=False) if __name__ in ("__main__", "__mp_main__"): + parser = argparse.ArgumentParser(description=f'{app_title} {app_version}') + parser.add_argument('--admin-access', help='Zugangsdaten für Administrator einstellen', action="store_true") + args = parser.parse_args() + if args.admin_access: + Commandline_Header() + print("Lade Administrationseinstellungen") + admin_settings = load_adminsettings() + print("Geben Sie den neuen Benutzernamen für den Administrationsbenutzer an:") + admin_user = input() + if admin_user == "": + print("Ungültiger Benutzername. Breche ab.") + quit() + print("Geben Sie das neue Passwort für den Administrationsbenutzer ein:") + admin_password = input() + if admin_password == "": + print("Ungültiges Passwort. Breche ab.") + quit() + print("Sie haben folgende Informationen eingegeben.") + print(f"Benutzername: {admin_user}") + print(f"Passwort: {admin_password}") + print("Sollen diese Einstellungen übernommen werden? j=Ja") + question = input() + if question == "j": + admin_settings["admin_user"] = admin_user + admin_settings["admin_password"] = hash_password(admin_password) + json_dict = json.dumps(admin_settings, indent=4) + with open(os.path.join(scriptpath, usersettingsfilename), "w") as outputfile: + outputfile.write(json_dict) + print("Daten geschrieben") + quit() + else: + print("Breche ab.") + quit() + main() diff --git a/settings.json b/settings.json index d02a2c2..67834a7 100644 --- a/settings.json +++ b/settings.json @@ -5,8 +5,8 @@ "secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise", "times_on_touchscreen": true, "photos_on_touchscreen": true, - "button_height": 300, "picture_height": 200, + "button_height": 300, "holidays": { "2025-01-01": "Neujahr", "2025-04-18": "Karfreitag", From 78c8aab781bbbe7e3a5e72eccf44abf6eb262811 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Sun, 18 May 2025 22:05:30 +0200 Subject: [PATCH 6/6] Touchscreen einstellbar --- admin.py | 18 +++++--- definitions.py | 1 + main.py | 1 + settings.json | 5 +- touchscreen.py | 123 +++++++++++++++++++++++++------------------------ users.py | 2 +- 6 files changed, 82 insertions(+), 68 deletions(-) diff --git a/admin.py b/admin.py index dfc6e2d..b099d8b 100644 --- a/admin.py +++ b/admin.py @@ -611,8 +611,11 @@ Dies kann nicht rückgängig gemacht werden!''') output_dict["admin_password"] = data["admin_password"] output_dict["port"] = port.value output_dict["secret"] = secret + output_dict["touchscreen"] = touchscreen_switch.value output_dict["times_on_touchscreen"] = timestamp_switch.value output_dict["photos_on_touchscreen"] = photo_switch.value + output_dict["picture_height"] = picture_height_input.value + output_dict["button_height"] = button_height_input.value output_dict["holidays"] = data["holidays"] json_dict = json.dumps(output_dict, indent=4) with open(os.path.join(scriptpath, usersettingsfilename), "w") as outputfile: @@ -652,12 +655,14 @@ Dies kann nicht rückgängig gemacht werden!''') with ui.card(): - ui.markdown("**Einstellungen für das Stempelterminal:**") + ui.markdown("**Einstellungen für das Touchscreenterminal:**") with ui.column(): - timestamp_switch = ui.switch("Stempelzeiten anzeigen") - photo_switch = ui.switch("Fotos anzeigen") + touchscreen_switch = ui.switch("Touchscreenterminal aktivieren") + touchscreen_switch.value = data["touchscreen"] + timestamp_switch = ui.switch("Stempelzeiten anzeigen").bind_visibility_from(touchscreen_switch, 'value') + photo_switch = ui.switch("Fotos anzeigen").bind_visibility_from(touchscreen_switch, 'value') timestamp_switch.value = bool(data["times_on_touchscreen"]) - with ui.row(): + with ui.row().bind_visibility_from(touchscreen_switch, 'value'): photo_switch.value = bool(data["photos_on_touchscreen"]) with ui.row().bind_visibility_from(photo_switch, 'value'): ui.markdown("Maximale Bilderöhe") @@ -665,8 +670,8 @@ Dies kann nicht rückgängig gemacht werden!''') "Größe muss größer 0 sein": lambda value: int(value)>0}).props('size=5') picture_height_input.value = data["picture_height"] ui.markdown('px') - with ui.row(): - ui.markdown("Maximale Buttonhöhe") + with ui.row().bind_visibility_from(touchscreen_switch, 'value'): + ui.markdown("Minimale Buttonhöhe") def compare_button_height(height): if not photo_switch.value: return True @@ -680,6 +685,7 @@ Dies kann nicht rückgängig gemacht werden!''') "Buttons dürfen nicht kleiner als die Fotos sein": lambda value: compare_button_height(value)}).props('size=5') button_height_input.value = data["button_height"] photo_switch.on_value_change(button_height_input.validate) + picture_height_input.on_value_change(button_height_input.validate) ui.markdown('px') def holiday_section(): diff --git a/definitions.py b/definitions.py index fe8c4e7..4d8d025 100644 --- a/definitions.py +++ b/definitions.py @@ -28,6 +28,7 @@ standard_adminsettings = { "admin_user": "admin", "secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise", "times_on_touchscreen": True, "photos_on_touchscreen": True, + "touchscreen": True, "picure_height": 200, "button_height": 300, "holidays": { } diff --git a/main.py b/main.py index 723636e..879063a 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 # Zeiterfassung from web_ui import * diff --git a/settings.json b/settings.json index 67834a7..b41ac70 100644 --- a/settings.json +++ b/settings.json @@ -3,10 +3,11 @@ "admin_password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918", "port": "8090", "secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise", + "touchscreen": true, "times_on_touchscreen": true, "photos_on_touchscreen": true, - "picture_height": 200, - "button_height": 300, + "picture_height": "100", + "button_height": "120", "holidays": { "2025-01-01": "Neujahr", "2025-04-18": "Karfreitag", diff --git a/touchscreen.py b/touchscreen.py index 7f3ee85..11e9297 100644 --- a/touchscreen.py +++ b/touchscreen.py @@ -14,67 +14,72 @@ import locale @ui.page('/touchscreen') def page_touchscreen(): - def button_click(name): - #nonlocal buttons - current_user = user(name) - current_user.timestamp() - #if current_user.stamp_status() == status_in: - # buttons[name].props('color=green') - # ui.notify(status_in) - #else: - # buttons[name].props('color=red') - # ui.notify(status_out) - user_buttons.refresh() + if load_adminsettings()["touchscreen"]: - pageheader("Stempeluhr") - ui.page_title("Stempeluhr") + def button_click(name): + #nonlocal buttons + current_user = user(name) + current_user.timestamp() + #if current_user.stamp_status() == status_in: + # buttons[name].props('color=green') + # ui.notify(status_in) + #else: + # buttons[name].props('color=red') + # ui.notify(status_out) + user_buttons.refresh() - admin_settings = load_adminsettings() - userlist = list_users() - number_of_users = len(userlist) - buttons = { } + pageheader("Stempeluhr") + ui.page_title("Stempeluhr") - @ui.refreshable - def user_buttons(): - if number_of_users > 5: - number_of_columns = 5 - else: - number_of_columns = number_of_users + admin_settings = load_adminsettings() + userlist = list_users() + number_of_users = len(userlist) + buttons = { } - with ui.grid(columns=number_of_columns).classes('w-full center'): - for name in userlist: - current_user = user(name) - current_button = ui.button(on_click=lambda name=name: button_click(name)).classes('w-md h-full min-h-[250px]') - with current_button: - if admin_settings["photos_on_touchscreen"]: - try: - with open(current_user.photofile, 'r') as file: + @ui.refreshable + def user_buttons(): + if number_of_users > 5: + number_of_columns = 5 + else: + number_of_columns = number_of_users + + with ui.grid(columns=number_of_columns).classes('w-full center'): + for name in userlist: + current_user = user(name) + current_button = ui.button(on_click=lambda name=name: button_click(name)).classes(f'w-md h-full min-h-[{admin_settings["button_height"]}px]') + with current_button: + if admin_settings["photos_on_touchscreen"]: + try: + with open(current_user.photofile, 'r') as file: + pass + file.close() + ui.image(current_user.photofile).classes(f'max-h-[{admin_settings["picture_height"]}px]').props('fit=scale-down') + except: pass - file.close() - ui.image(current_user.photofile).classes('max-h-[200px]').props('fit=scale-down') - except: - pass - column_classes = "w-full items-center" - if admin_settings["times_on_touchscreen"] or admin_settings["photos_on_touchscreen"]: - column_classes += " self-end" - with ui.column().classes(column_classes): - if admin_settings["times_on_touchscreen"]: - todays_timestamps = current_user.get_day_timestamps() - # Wenn wir Einträge haben - if len(todays_timestamps) > 0 and admin_settings["times_on_touchscreen"]: - table_string = "" - for i in range(0, len(todays_timestamps), 2): - try: - table_string += f"{datetime.datetime.fromtimestamp(todays_timestamps[i]).strftime('%H:%M')} - {datetime.datetime.fromtimestamp(todays_timestamps[i+1]).strftime('%H:%M')}" - except IndexError: - table_string += f"{datetime.datetime.fromtimestamp(todays_timestamps[i]).strftime('%H:%M')} -" - if i < len(todays_timestamps) - 2: - table_string += ", " - ui.markdown(table_string) - ui.label(current_user.fullname).classes('text-center') - if current_user.stamp_status() == status_in: - current_button.props('color=green') - else: - current_button.props('color=red') - buttons[name] = current_button - user_buttons() \ No newline at end of file + column_classes = "w-full items-center" + if admin_settings["times_on_touchscreen"] or admin_settings["photos_on_touchscreen"]: + column_classes += " self-end" + with ui.column().classes(column_classes): + if admin_settings["times_on_touchscreen"]: + todays_timestamps = current_user.get_day_timestamps() + # Wenn wir Einträge haben + if len(todays_timestamps) > 0 and admin_settings["times_on_touchscreen"]: + table_string = "" + for i in range(0, len(todays_timestamps), 2): + try: + table_string += f"{datetime.datetime.fromtimestamp(todays_timestamps[i]).strftime('%H:%M')} - {datetime.datetime.fromtimestamp(todays_timestamps[i+1]).strftime('%H:%M')}" + except IndexError: + table_string += f"{datetime.datetime.fromtimestamp(todays_timestamps[i]).strftime('%H:%M')} -" + if i < len(todays_timestamps) - 2: + table_string += ", " + ui.markdown(table_string) + ui.label(current_user.fullname).classes('text-center') + if current_user.stamp_status() == status_in: + current_button.props('color=green') + else: + current_button.props('color=red') + buttons[name] = current_button + user_buttons() + + else: + pageheader("Interface deaktiviert") \ No newline at end of file diff --git a/users.py b/users.py index bce4f90..58a7955 100644 --- a/users.py +++ b/users.py @@ -95,7 +95,7 @@ class user: # Zähle die Zeilen lines = file.readlines() if len(lines)== 0: - print(f"Keine Einträge") + pass elif len(lines) % 2 == 0: return status_out else: