From 9a4bf599dd692c5342f09f8c331a33f031801194 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Fri, 25 Apr 2025 13:55:35 +0200 Subject: [PATCH] Arbeitstagerkenung, Abwesenheitstage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Arbeitstagerkennung eingefügt Dialog für Abwesenheitstage braucht noch Logik für Rangeeintragungen --- users.py | 68 +++++++++++++-- users/testuser1/2025-3.txt | 2 + users/testuser1/2025-4.json | 8 +- users/testuser1/2025-4.txt | 6 -- users/testuser1/2025-5.json | 7 ++ users/testuser1/2026-4.json | 7 ++ users/testuser1/settings.json | 2 +- web_ui.py | 158 +++++++++++++++++++++++++++------- 8 files changed, 207 insertions(+), 51 deletions(-) create mode 100644 users/testuser1/2025-5.json create mode 100644 users/testuser1/2026-4.json diff --git a/users.py b/users.py index 976913a..1b5ad50 100644 --- a/users.py +++ b/users.py @@ -7,6 +7,7 @@ import datetime import time import json import shutil +import re from definitions import userfolder, scriptpath, usersettingsfilename, photofilename, status_in, status_out @@ -32,18 +33,24 @@ class user: self.username = data["username"] self.fullname = data["fullname"] - def get_stamp_file(self): - year = str(datetime.datetime.now().year) - month = str(datetime.datetime.now().month) + def get_stamp_file(self, time_stamp=None): + if time_stamp == None: + year = str(datetime.datetime.now().year) + month = str(datetime.datetime.now().month) + else: + year = str(datetime.datetime.fromtimestamp(time_stamp).year) + month = str(datetime.datetime.fromtimestamp(time_stamp).month) completepath = f"{self.userfolder}/{year}-{month}" return completepath def timestamp(self, stamptime=-1): - filename = f"{self.get_stamp_file()}.txt" + if stamptime == -1: stamptime = time.time() timestamp = int(stamptime) + filename = f"{self.get_stamp_file(time_stamp=stamptime)}.txt" + try: # Öffne die Datei im Anhang-Modus ('a') with open(filename, 'a') as file: @@ -142,6 +149,14 @@ class user: for i in range(int(self.get_starting_day()[0]), year_now + 1): years.append(str(i)) + for file in os.listdir(self.userfolder): + if re.match(r"\d{4}-\d{1,2}\.json", file): + year = file.split("-")[0] + if year not in years: + years.append(year) + + years.sort() + print(years) return years def get_months(self, year): @@ -154,7 +169,7 @@ class user: month_now = int(datetime.datetime.now().month) if start_year == int(year): - print("Start_year is year") + if start_year == year_now: for i in range(start_month, month_now + 1): available_months.append(i) @@ -168,7 +183,15 @@ class user: elif int(year) < year_now: for i in range(1, 13): available_months.append(i) + print(available_months) + for file in os.listdir(self.userfolder): + if re.match(r"\d{4}-\d{1,2}\.json", file): + if file.split("-")[0] == str(year): + month = int(file.split("-")[1].split(".")[0]) + if month not in available_months: + available_months.append(month) + available_months.sort() return(available_months) def get_timestamps(self, year, month): @@ -208,13 +231,44 @@ class user: def get_absence(self, year, month): try: - print(f"{self.userfolder}/{year}-{month}.json") with open(f"{self.userfolder}/{year}-{month}.json", "r") as json_file: json_data = json.load(json_file) absence = json_data["absence"] return absence except: - return 0 + return { } + + def update_absence(self, year, month, day, absence_type): + try: + with open(f"{self.userfolder}/{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: + json_data = { } + json_data["archived"] = 0 + json_data["overtime"] = 0 + json_dict = json.dumps(json_data, indent=4) + json_file.write(json_dict) + try: + json_data["absence"][str(int(day))] = absence_type + except: + json_data.update({ "absence": { str(int(day)): absence_type}}) + json_dict = json.dumps(json_data, indent=4) + print(json_dict) + print(f"{self.userfolder}/{year}-{month}.json") + + with open(f"{self.userfolder}/{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: + 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: + json_file.write(json_dict) # Benutzer auflisten def list_users(): diff --git a/users/testuser1/2025-3.txt b/users/testuser1/2025-3.txt index f25130e..7350e03 100644 --- a/users/testuser1/2025-3.txt +++ b/users/testuser1/2025-3.txt @@ -26,3 +26,5 @@ 1745181050 1745240760 1745240762 +1740996000 +1740997800 diff --git a/users/testuser1/2025-4.json b/users/testuser1/2025-4.json index ccd51db..a4c92bd 100644 --- a/users/testuser1/2025-4.json +++ b/users/testuser1/2025-4.json @@ -1,12 +1,12 @@ { "archived": 0, - "total_hours": 0, + "overtime": 0, "absence": { - "1": "U", - "4": "U", "7": "K", "8": "UU", "9": "KK", - "3": "K" + "10": "U", + "11": "U", + "29": "U" } } \ No newline at end of file diff --git a/users/testuser1/2025-4.txt b/users/testuser1/2025-4.txt index 5ed0156..0a59752 100644 --- a/users/testuser1/2025-4.txt +++ b/users/testuser1/2025-4.txt @@ -1,7 +1,3 @@ -1743566400 -1743606000 -1743652800 -1743660240 1744889948 1744890300 1744989797 @@ -18,8 +14,6 @@ 1744991473 1744991477 1744991770 -1745181046 -1745181050 1745215200 1745229600 1745390818 diff --git a/users/testuser1/2025-5.json b/users/testuser1/2025-5.json new file mode 100644 index 0000000..813396e --- /dev/null +++ b/users/testuser1/2025-5.json @@ -0,0 +1,7 @@ +{ + "archived": 0, + "overtime": 0, + "absence": { + "14": "U" + } +} \ No newline at end of file diff --git a/users/testuser1/2026-4.json b/users/testuser1/2026-4.json new file mode 100644 index 0000000..e24cac5 --- /dev/null +++ b/users/testuser1/2026-4.json @@ -0,0 +1,7 @@ +{ + "archived": 0, + "overtime": 0, + "absence": { + "14": "F" + } +} \ No newline at end of file diff --git a/users/testuser1/settings.json b/users/testuser1/settings.json index ddeac0f..83a6dee 100644 --- a/users/testuser1/settings.json +++ b/users/testuser1/settings.json @@ -3,7 +3,7 @@ "fullname": "Pia Paulina", "password": "123456789", "workhours": { - "2024-04-01": { + "2025-04-03": { "1": "4", "2": "8", "3": "8", diff --git a/web_ui.py b/web_ui.py index cf1ceaf..08c9aa6 100644 --- a/web_ui.py +++ b/web_ui.py @@ -136,21 +136,28 @@ def page_admin(): def update_months(): current_user = user(time_user.value) available_months = current_user.get_months(year_binder.value) - available_months_dict = {} + available_months_dict = { } + for element in available_months: available_months_dict[element] = calendar.month_name[int(element)] - print(available_months_dict) + select_month.clear() select_month.set_options(available_months_dict) select_month.value = list(available_months)[0] + def update_user(): + current_user = user(time_user.value) + available_years = current_user.get_years() + select_year.clear() + select_year.set_options(available_years) + select_year.value = list(available_years)[0] + userlist = list_users() ui.markdown("Benutzer:") - print("Time User wird konstruiert") - time_user = ui.select(options=userlist, value=userlist[0], on_change=update_months) + + time_user = ui.select(options=userlist, value=userlist[0], on_change=update_user) user_to_select_for_start = userlist[0] - current_user = user(user_to_select_for_start) current_year = datetime.datetime.now().year current_month = datetime.datetime.now().month @@ -176,7 +183,7 @@ def page_admin(): select_year = ui.select(options=available_years, value=set_year, on_change=update_months).bind_value_to(year_binder, 'value') - month_header = ui.markdown(f"###Buchungen für {current_user.fullname} für {calendar.month_name[int(select_month.value)]} {select_year.value}") + month_header = ui.markdown(f"###Buchungen für **{current_user.fullname}** für **{calendar.month_name[int(select_month.value)]} {select_year.value}**") # Tabelle aufbauen with ui.card() as calendar_card: @@ -215,13 +222,34 @@ def page_admin(): # Buchungen with ui.row(): + def delete_absence(day, absence_type): + def execute_deletion(): + current_user.del_absence(select_year.value, select_month.value, day) + calendar_card.clear() + update_month_and_year() + dialog.close() + ui.notify("Abwesenheitseintrag gelöscht") + with ui.dialog() as dialog, ui.card(): + ui.markdown(f'''Soll der Eintrag **{absence_type}** für den **{day}. {calendar.month_name[int(select_month.value)]} {select_year.value}** gelöscht werden? + +Dies kann nicht rückgägig gemacht werden!''') + with ui.grid(columns=3): + ui.button("Ja", on_click=execute_deletion) + ui.space() + ui.button("Nein", on_click=dialog.close) + dialog.open() + try: for i in list(user_absent): if int(i) == day: - ui.button(absence_entries[user_absent[i]]["name"]).props(f'color={absence_entries[user_absent[i]]["color"]}') + ui.button(absence_entries[user_absent[i]]["name"], on_click=lambda i=i, day=day: delete_absence(day, absence_entries[user_absent[i]]["name"])).props(f'color={absence_entries[user_absent[i]]["color"]}') except: pass + day_type = ui.markdown("Kein Arbeitstag") + day_type.set_visibility(False) + + # Hier werden nur die Tage mit Timestamps behandelt if len(timestamps_dict[day]) > 0: timestamps_dict[day].sort() @@ -279,7 +307,6 @@ def page_admin(): ui.button("Abbrechen", on_click=edit_dialog.close) edit_dialog.open() - for i in range(len(timestamps_dict[day])): try: temp_pair = [ timestamps_dict[day][i] , timestamps_dict[day][i+1] ] @@ -325,39 +352,57 @@ def page_admin(): ui.markdown("Kein") # Arbeitszeitsoll bestimmen + workhour_entries = list(current_user.workhours) workhour_entries.sort() found_match = False - for entry in reversed(workhour_entries): + if day_in_list.timestamp() < datetime.datetime.strptime(workhour_entries[0], + '%Y-%m-%d').timestamp(): + ui.markdown("n. a.") + day_type.set_content("*Noch kein Arbeitsverhältnis*") + day_type.set_visibility(True) - if datetime.datetime.strptime(entry, '%Y-%m-%d').timestamp() < day_in_list.timestamp() and found_match == False: + else: + for entry in reversed(workhour_entries): + + if datetime.datetime.strptime(entry, '%Y-%m-%d').timestamp() <= day_in_list.timestamp() and found_match == False: + + if int(day_in_list.strftime('%w')) == 0: + weekday_index = 7 + else: + weekday_index = int(day_in_list.strftime('%w')) + hours_to_work = current_user.workhours[entry][str(weekday_index)] + ui.markdown(f"{convert_seconds_to_hours(int(hours_to_work) * 3600)}") + if int(hours_to_work) == 0: + day_type.content = "**Kein Arbeitstag**" + day_type.set_visibility(True) + + found_match = True - if int(day_in_list.strftime('%w')) == 0: - weekday_index = 7 - else: - weekday_index = int(day_in_list.strftime('%w')) - ui.markdown(f"{current_user.workhours[entry][str(weekday_index)]} h") - found_match = True # Saldo für den Tag berechnen - if time.time() > day_in_list.timestamp(): - time_duty = int(current_user.workhours[entry][str(weekday_index)]) * 3600 + try: + if time.time() > day_in_list.timestamp(): - saldo = int(time_sum) - int(time_duty) - # Nach Abwesenheitseinträgen suchen - try: - for i in list(user_absent): - if int(i) == day: - saldo = 0 - except: - pass + time_duty = int(current_user.workhours[entry][str(weekday_index)]) * 3600 - general_saldo = general_saldo + saldo - ui.markdown(convert_seconds_to_hours(saldo)) - else: - ui.markdown("-") + saldo = int(time_sum) - int(time_duty) + # Nach Abwesenheitseinträgen suchen + try: + for i in list(user_absent): + if int(i) == day and user_absent[i] != "UU": + saldo = 0 + except: + pass + + general_saldo = general_saldo + saldo + ui.markdown(convert_seconds_to_hours(saldo)) + else: + ui.markdown("-") + except: + ui.markdown("n. a.") def add_entry(day): with ui.dialog() as add_dialog, ui.card(): @@ -369,8 +414,8 @@ def page_admin(): ui.notify("Bitte eine Uhrzeit auswählen.") return - new_time_stamp = datetime.datetime(int(select_year.value), - int(select_month.value), day, + new_time_stamp = datetime.datetime(int(year_binder.value), + int(month_binder.value), day, int(input_time.value[:2]), int(input_time.value[-2:])).timestamp() current_user = user(time_user.value) @@ -384,7 +429,54 @@ def page_admin(): ui.space() ui.button("Abbrechen", on_click=add_dialog.close) add_dialog.open() - ui.button("Eintrag hinzufügen", on_click=lambda day=day: add_entry(day)) + add_dialog.move(calendar_card) + + def add_absence(absence_type, day): + with ui.dialog() as dialog, ui.card(): + ui.markdown(f'Für welchen Zeitraum soll *{absence_entries[absence_type]["name"]}* eingetragen werden?') + absence_dates = ui.date().props('range') + if day < 10: + day = f"0{str(day)}" + else: + day = str(day) + if int(select_month.value) < 10: + month = f"0{select_month.value}" + else: + month = select_month.value + absence_dates.value = f"{select_year.value}-{month}-{day}" + + def add_absence_save(): + # Bei nur einem Datum, direkt schreiben + if isinstance(absence_dates.value, str): + absence_date = absence_dates.value.split("-") + current_user.update_absence(absence_date[0], absence_date[1], absence_date[2], absence_type) + calendar_card.clear() + update_month_and_year() + # Bei Zeitbereich, aufteilen + if isinstance(absence_dates, dict): + pass + + dialog.close() + + + with ui.grid(columns=3): + ui.button("Speichern", on_click=add_absence_save) + ui.space() + ui.button("Abbrechen", on_click=dialog.close) + + dialog.open() + dialog.move(calendar_card) + + with ui.button(icon='menu'): + with ui.menu() as menu: + ui.menu_item("Zeiteintrag hinzufügen", lambda day=day: add_entry(day)) + 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)) + if str(day) in list(user_absent): + menu_item.disable() + + #ui.button("Eintrag hinzufügen", on_click=lambda day=day: add_entry(day)) #4x leer und dann Gesamtsaldo for i in range(4):