diff --git a/admin.py b/admin.py index 8853de8..dd07a58 100644 --- a/admin.py +++ b/admin.py @@ -11,6 +11,8 @@ from calendar import monthrange from web_ui import * import os.path +import os +from stat import S_IREAD, S_IRWXU import hashlib import calendar import locale @@ -130,7 +132,7 @@ def page_admin(): def update_month_and_year(): current_user = user(time_user.value) # Archivstatus - + days_with_errors = current_user.archiving_validity_check(int(select_year.value), int(select_month.value)) with ui.grid(columns='auto auto auto 1fr 1fr 1fr 1fr') as table_grid: if int(select_month.value) > 1: archive_status = current_user.get_archive_status(int(select_year.value), @@ -140,7 +142,11 @@ def page_admin(): def revoke_archive_status(): def revoke_status(): - filename = f"{current_user.userfolder}/{int(select_year.value)}-{int(select_month.value)}.json" + filestring = f"{current_user.userfolder}/{int(select_year.value)}-{int(select_month.value)}" + filename = f"{filestring}.txt" + os.chmod(filename, S_IRWXU) + filename = f"{filestring}.json" + os.chmod(filename, S_IRWXU) with open(filename, 'r') as json_file: data = json.load(json_file) data["archived"] = 0 @@ -160,7 +166,7 @@ def page_admin(): dialog.open() if archive_status == True: - with ui.row().classes('text-right col-span-6 justify-center'): + with ui.row().classes('text-right col-span-7 justify-center'): ui.button("Archiviert", on_click=revoke_archive_status).classes('bg-transparent text-black') ui.separator() calendar_card.classes('bg-yellow') @@ -304,22 +310,26 @@ Dies kann nicht rückgängig gemacht werden!''') timestamp_button = ui.button(datetime.datetime.fromtimestamp(int(timestamps_dict[day][i])).strftime('%H:%M'), on_click=lambda t_stamp=timestamps_dict[day][i], day=day: edit_entry(t_stamp, day)) if archive_status: timestamp_button.disable() + with ui.row(): + # Fehlerhinweis + if day in days_with_errors: + ui.icon('warning', color='red').tooltip("Keine Schlussbuchung").classes('text-2xl') - # Notizen anzeigen - days_notes = current_user.get_day_notes(select_year.value, select_month.value, day) - if days_notes != { }: - with ui.icon('o_description').classes('text-2xl'): - with ui.tooltip(): - with ui.grid(columns='auto auto'): - for username, text in days_notes.items(): - admins_name = load_adminsettings()["admin_user"] - if username == admins_name: - ui.markdown('Administrator:') - else: - ui.markdown(current_user.fullname) - ui.markdown(text) - else: - ui.space() + # Notizen anzeigen + days_notes = current_user.get_day_notes(select_year.value, select_month.value, day) + if days_notes != { }: + with ui.icon('o_description').classes('text-2xl'): + with ui.tooltip(): + with ui.grid(columns='auto auto'): + for username, text in days_notes.items(): + admins_name = load_adminsettings()["admin_user"] + if username == admins_name: + ui.markdown('Administrator:') + else: + ui.markdown(current_user.fullname) + ui.markdown(text) + else: + ui.space() # Arbeitszeit Ist bestimmen @@ -539,13 +549,15 @@ Dies kann nicht rückgängig gemacht werden!''') dialog.open() dialog.move(calendar_card) - with ui.button(icon='menu'): + with ui.button(icon='menu') as menu_button: with ui.menu() as menu: menu_item = ui.menu_item("Zeiteintrag hinzufügen", lambda day=day: add_entry(day)) if archive_status: menu_item.disable() ui.separator() menu_item = ui.menu_item("Notizen bearbeiten", lambda day=day: edit_notes(day)) + if archive_status: + menu_item.disable() ui.separator() for i in list(absence_entries): menu_item = ui.menu_item(f"{absence_entries[i]['name']} eintragen", lambda absence_type=i, day=day: add_absence(absence_type, day)) @@ -553,6 +565,8 @@ Dies kann nicht rückgängig gemacht werden!''') menu_item.disable() if str(day) in list(user_absent): menu_item.disable() + if archive_status: + menu_button.disable() #ui.button("Eintrag hinzufügen", on_click=lambda day=day: add_entry(day)) @@ -576,6 +590,7 @@ Dies kann nicht rückgängig gemacht werden!''') current_user = user(time_user.value) month_header.set_content(f"###Buchungen für **{current_user.fullname}** für **{calendar.month_name[int(select_month.value)]} {select_year.value}**") + month_header.set_content(f"###Buchungen für **{current_user.fullname}** für **{calendar.month_name[int(select_month.value)]} {select_year.value}**") timetable() button_update = ui.button("Aktualisieren", on_click=timetable.refresh) button_update.move(timetable_header) diff --git a/api.py b/api.py index b636999..508c091 100644 --- a/api.py +++ b/api.py @@ -21,6 +21,7 @@ def page_overview_month(username: str, year: int, month: int): try: current_user = user(username) + days_with_errors = current_user.archiving_validity_check(year, month) ui.page_title(f"Bericht für {current_user.fullname} für {calendar.month_name[month]} {year}") if current_user.get_archive_status(year, month): with ui.column().classes('w-full items-end gap-0'): @@ -80,7 +81,8 @@ def page_overview_month(username: str, year: int, month: int): color_day = color_weekend current_day_date = f"{datetime(year, month, day).strftime('%a')}, {day}.{month}.{year}" - day_text_element = ui.markdown(current_day_date).classes(f'border px-{pad_x} py-{pad_y} bg-{color_day}') + with ui.link_target(day): + ui.markdown(current_day_date).classes(f'border px-{pad_x} py-{pad_y} bg-{color_day}') # Abwesenheitseinträge @@ -100,11 +102,11 @@ def page_overview_month(username: str, year: int, month: int): for i in range(0, len(timestamps_dict[day]), 2): try: temp_pair = [timestamps_dict[day][i], timestamps_dict[day][i + 1]] - booking_text = booking_text + str(datetime.fromtimestamp(temp_pair[0]).strftime('%H:%M')) + "-" + str(datetime.fromtimestamp(temp_pair[1]).strftime('%H:%M')) + "
" + booking_text = booking_text + str(datetime.fromtimestamp(temp_pair[0]).strftime('%H:%M')) + " - " + str(datetime.fromtimestamp(temp_pair[1]).strftime('%H:%M')) + "
" except: if len(timestamps_dict[day]) % 2 != 0: - booking_text += datetime.fromtimestamp(int(timestamps_dict[day][i])).strftime('%H:%M') + booking_text += datetime.fromtimestamp(int(timestamps_dict[day][i])).strftime('%H:%M') + " - ***Buchung fehlt!***" booking_text_element = ui.markdown(booking_text).classes(f'border px-{pad_x} py-{pad_y} bg-{booking_color} text-{booking_text_color}') @@ -258,7 +260,15 @@ def page_overview_month(username: str, year: int, month: int): dialog.open() if archivable == True: - ui.button("Archivieren", on_click=archive_dialog) + if len(days_with_errors) > 0: + ui.label("Es gibt Inkonsistenzen in den Buchungen. Folgende Tage müssen überprüft werden:") + with ui.grid(columns=len(days_with_errors)): + for i in days_with_errors: + ui.link(f"{i}.", f'#{i}') + + archive_button = ui.button("Archivieren", on_click=archive_dialog) + if len(days_with_errors) > 0: + archive_button.disable() archive() diff --git a/playgound.py b/playgound.py index 1800280..8d8184f 100644 --- a/playgound.py +++ b/playgound.py @@ -1,6 +1,17 @@ -from nicegui import ui +import json +import urllib.request + +from nicegui import ui, app + + import segno +@app.get("/data") +async def deliver_data(): + with open("settings.json") as json_file: + data = json.load(json_file) + return data + string = "" for i in range(1000): string += str(i) diff --git a/users.py b/users.py index fe9fce5..9d9f2ff 100644 --- a/users.py +++ b/users.py @@ -3,6 +3,8 @@ import hashlib # User bezogene Funktionen import os +from calendar import monthrange +from stat import S_IREAD, S_IWUSR import datetime import time import json @@ -222,6 +224,25 @@ class user: except: return -1 + def archiving_validity_check(self, year: int, month: int): + timestampfilename = f"{self.userfolder}/{year}-{month}.txt" + try: + with open(timestampfilename) as timestampfile: + timestamps = timestampfile.readlines() + timestamps.sort() + days_with_errors = [ ] + for day in range(1, monthrange(year, month)[1] + 1): + day_dt = datetime.datetime(year, month, day) + timestamps_of_this_day = [ ] + for i in timestamps: + i_dt = datetime.datetime.fromtimestamp(int(i)) + if day_dt.year == i_dt.year and day_dt.month == i_dt.month and day_dt.day == i_dt.day: + timestamps_of_this_day.append(i) + if len(timestamps_of_this_day) % 2 != 0: + days_with_errors.append(day) + return days_with_errors + except: + return [ ] def archive_hours(self, year, month, overtime: int): filename = f"{self.userfolder}/{year}-{month}.json" @@ -230,11 +251,14 @@ class user: data["archived"] = 1 data["overtime"] = overtime - json_dict = json.dumps(data) + json_dict = json.dumps(data, indent=4) with open(filename, "w") as outputfile: outputfile.write(json_dict) - + # Dateien auf readonly setzen + os.chmod(filename, S_IREAD) + filename_txt = f"{self.userfolder}/{year}-{month}.txt" + os.chmod(filename_txt, S_IREAD) def get_last_months_overtime(self, year, month): try: if int(month) == 1: diff --git a/users/testuser1/2025-3.json b/users/testuser1/2025-3.json old mode 100644 new mode 100755 index 27c5b37..d438ef3 --- a/users/testuser1/2025-3.json +++ b/users/testuser1/2025-3.json @@ -1 +1 @@ -{"archived": 1, "overtime": -528928} \ No newline at end of file +{"archived": 0, "overtime": -528928} \ No newline at end of file diff --git a/users/testuser1/2025-3.txt b/users/testuser1/2025-3.txt old mode 100644 new mode 100755 index 7350e03..da7ed21 --- a/users/testuser1/2025-3.txt +++ b/users/testuser1/2025-3.txt @@ -1,30 +1,4 @@ -1743965819 -1743965909 -1743966022 -1743966045 -1743966047 -1743966049 -1743967346 -1744889948 -1744889966 -1744989797 -1744989827 -1744989830 -1744989883 -1744989909 -1744989914 -1744989916 -1744991169 -1744991171 -1744991288 -1744991291 -1744991473 -1744991477 -1744991770 -1744991777 -1745181046 -1745181050 -1745240760 -1745240762 1740996000 -1740997800 +1742460540 +1741038540 +1742464500 diff --git a/users/testuser1/2025-4.json b/users/testuser1/2025-4.json index 1346aed..48da952 100644 --- a/users/testuser1/2025-4.json +++ b/users/testuser1/2025-4.json @@ -1,6 +1,6 @@ { - "archived": 0, - "overtime": 0, + "archived": 1, + "overtime": -877154, "absence": { "7": "U", "8": "K", diff --git a/users/testuser1/2025-4.txt b/users/testuser1/2025-4.txt index ad67cca..bd42eec 100644 --- a/users/testuser1/2025-4.txt +++ b/users/testuser1/2025-4.txt @@ -1,21 +1,5 @@ 1744889948 1744890300 -1744989797 -1744989827 -1744989830 -1744989883 -1744989909 -1744989914 -1744989916 -1744991169 -1744991171 -1744991288 -1744991291 -1744991473 -1744991477 -1744991770 -1745215200 -1745229600 1745390818 1745390894 1745390894 diff --git a/users/testuser1/2025-5.json b/users/testuser1/2025-5.json index d3b1f01..7ec0e06 100644 --- a/users/testuser1/2025-5.json +++ b/users/testuser1/2025-5.json @@ -2,7 +2,6 @@ "archived": 0, "overtime": 0, "absence": { - "14": "U", "2": "SO" }, "notes": { diff --git a/users/testuser1/2025-5.txt b/users/testuser1/2025-5.txt index 5098bdd..58cd3a9 100644 --- a/users/testuser1/2025-5.txt +++ b/users/testuser1/2025-5.txt @@ -6,3 +6,6 @@ 1746608922 1746609024 1746609037 +1747206908 +1747207022 +1747813500