diff --git a/lib/admin.py b/lib/admin.py index 3291912..1f36c6d 100644 --- a/lib/admin.py +++ b/lib/admin.py @@ -41,9 +41,12 @@ def page_admin(): ui.button("Logout", on_click=admin_logout) + updates_available = ValueBinder() + updates_available.value = False + with ui.tabs() as tabs: - time_overview = ui.tab('Zeitübersichten') + time_overview = ui.tab('Zeitdaten') settings = ui.tab('Einstellungen') users = ui.tab('Benutzer') backups = ui.tab('Backups') @@ -59,551 +62,651 @@ def page_admin(): with ui.tab_panels(tabs, value=time_overview): with ui.tab_panel(time_overview): - ui.markdown("##Übersichten") + with ui.tabs() as overview_tabs: + user_month_overview = ui.tab('Monatsansicht') + user_summary = ui.tab("Zusammenfassung") + vacation_applications = ui.tab("Urlaubsanträge") - # Tabelle konstruieren - with ui.card().classes('w-full'): + with ui.tab_panels(overview_tabs, value = user_month_overview): + with ui.tab_panel(user_month_overview).classes('w-full'): + ui.markdown("##Übersichten") - with ui.row() as timetable_header: - year_binder = ValueBinder() - month_binder = ValueBinder() + # Tabelle konstruieren + with ui.card().classes('w-full'): - def update_months(): - current_user = user(time_user.value) - available_months = current_user.get_months(year_binder.value) - available_months_dict = { } + with ui.row() as timetable_header: + year_binder = ValueBinder() + month_binder = ValueBinder() - for element in available_months: - available_months_dict[element] = calendar.month_name[int(element)] + def update_months(): + current_user = user(time_user.value) + available_months = current_user.get_months(year_binder.value) + available_months_dict = { } - select_month.clear() - select_month.set_options(available_months_dict) - select_month.value = list(available_months)[0] + for element in available_months: + available_months_dict[element] = calendar.month_name[int(element)] - def update_user(): - current_user = user(time_user.value) - available_years = current_user.get_years() - try: - select_year.clear() - select_year.set_options(available_years) - - try: - select_year.value = str(datetime.datetime.now().year) - except: - select_year.value = list(available_years)[0] - try: - select_month.value = datetime.datetime.now().month - except: + select_month.clear() + select_month.set_options(available_months_dict) select_month.value = list(available_months)[0] - except NameError: - pass - ui.markdown("Benutzer:") + def update_user(): + current_user = user(time_user.value) + available_years = current_user.get_years() - time_user = ui.select(options=userlist, on_change=update_user) - time_user.value = userlist[0] - user_to_select_for_start = userlist[0] + try: + select_year.clear() + select_year.set_options(available_years) - current_year = datetime.datetime.now().year - current_month = datetime.datetime.now().month - current_user = user(user_to_select_for_start) - available_years = current_user.get_years() - available_months = current_user.get_months(current_year) - available_months_dict = { } + try: + select_year.value = str(datetime.datetime.now().year) + except: + select_year.value = list(available_years)[0] + update_months() + try: + select_month.value = datetime.datetime.now().month + except: + select_month.value = list(available_months)[0] + except NameError: + pass - for element in available_months: - available_months_dict[element] = calendar.month_name[int(element)] + ui.markdown("Benutzer:") - if current_month in available_months: - set_month = current_month - else: - set_month = available_months[0] + time_user = ui.select(options=userlist, on_change=update_user) + time_user.value = userlist[0] + user_to_select_for_start = userlist[0] - if str(current_year) in available_years: - set_year = str(current_year) - else: - set_year = (available_years[0]) + current_year = datetime.datetime.now().year + current_month = datetime.datetime.now().month + current_user = user(user_to_select_for_start) + available_years = current_user.get_years() + available_months = current_user.get_months(current_year) + available_months_dict = { } - select_month = ui.select(options=available_months_dict, value=set_month).bind_value_to(month_binder, 'value') - select_year = ui.select(options=available_years, value=set_year, on_change=update_months).bind_value_to(year_binder, 'value') + for element in available_months: + available_months_dict[element] = calendar.month_name[int(element)] - month_header = ui.markdown(f"###Buchungen für **{current_user.fullname}** für **{calendar.month_name[int(select_month.value)]} {select_year.value}**") + if current_month in available_months: + set_month = current_month + else: + set_month = available_months[0] - # Tabelle aufbauen - @ui.refreshable - def timetable(): - current_user = user(time_user.value) - with ui.card().classes('w-full') as calendar_card: - 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').classes('w-full md:min-w-[600px] lg:min-w-[800px]') as table_grid: - if int(select_month.value) > 1: - archive_status = current_user.get_archive_status(int(select_year.value), - int(select_month.value)) - else: - archive_status = current_user.get_archive_status(int(select_year.value) - 1, 12) + if str(current_year) in available_years: + set_year = str(current_year) + else: + set_year = (available_years[0]) - def revoke_archive_status(): - def revoke_status(): - 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 + select_month = ui.select(options=available_months_dict, value=set_month).bind_value_to(month_binder, 'value') + select_year = ui.select(options=available_years, value=set_year, on_change=update_months).bind_value_to(year_binder, 'value') - json_dict = json.dumps(data) + month_header = ui.markdown(f"###Buchungen für **{current_user.fullname}** für **{calendar.month_name[int(select_month.value)]} {select_year.value}**") - with open(filename, "w") as outputfile: - outputfile.write(json_dict) - timetable.refresh() - dialog.close() + # Tabelle aufbauen + @ui.refreshable + def timetable(): + current_user = user(time_user.value) + with ui.card().classes('w-full') as calendar_card: + 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').classes('w-full md:min-w-[600px] lg:min-w-[800px]') as table_grid: + if int(select_month.value) > 1: + archive_status = current_user.get_archive_status(int(select_year.value), + int(select_month.value)) + else: + archive_status = current_user.get_archive_status(int(select_year.value) - 1, 12) - with ui.dialog() as dialog, ui.card(): - ui.label("Soll der Archivstatus für den aktuellen Monat aufgehoben werden, damit Änderungen vorgenommen werden können?") - with ui.grid(columns=2): - ui.button("Ja", on_click=revoke_status) - ui.button("Nein", on_click=dialog.close) - dialog.open() + def revoke_archive_status(): + def revoke_status(): + 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 - if archive_status == True: - 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') - else: - calendar_card.classes('bg-white') - # Überschriften - ui.markdown("**Datum**") - ui.markdown("**Buchungen**") - ui.space() - ui.markdown("**Ist**") - ui.markdown("**Soll**") - ui.markdown("**Saldo**") - ui.space() + json_dict = json.dumps(data) - timestamps = current_user.get_timestamps(year=select_year.value, month=select_month.value) - user_absent = current_user.get_absence(year=select_year.value, month=select_month.value) - # Dictionary für sortierte Timestamps - timestamps_dict = { } - # Dictionary mit zunächst leeren Tageinträgen befüllen - for day in range(1, monthrange(int(select_year.value), int(select_month.value))[1] + 1): - # Jeder Tag bekommt eine leere Liste - timestamps_dict[day] = [ ] - - # Alle Timestamps durchgehen und sie den Dictionaryeinträgen zuordnen: - for stamp in timestamps: - 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 - - for day in list(timestamps_dict): - # Datum für Tabelle konstruieren - day_in_list = datetime.datetime(int(select_year.value), int(select_month.value), day) - class_content = "" - if day_in_list.date() == datetime.datetime.now().date(): - class_content = 'font-bold text-red-700 uppercase' - ui.markdown(f"{day_in_list.strftime('%a')}., {day}. {calendar.month_name[int(select_month.value)]}").classes(class_content) - - # 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() + with open(filename, "w") as outputfile: + outputfile.write(json_dict) + timetable.refresh() 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ängig gemacht werden!''') - with ui.grid(columns=3): - ui.button("Ja", on_click=execute_deletion) - ui.space() + ui.label("Soll der Archivstatus für den aktuellen Monat aufgehoben werden, damit Änderungen vorgenommen werden können?") + with ui.grid(columns=2): + ui.button("Ja", on_click=revoke_status) ui.button("Nein", on_click=dialog.close) dialog.open() - try: - for i in list(user_absent): - if int(i) == day: - absence_button = 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"]}') - if archive_status: - absence_button.disable() - 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() - - def edit_entry(t_stamp, day): - - with ui.dialog() as edit_dialog, ui.card(): - ui.markdown("**Eintrag bearbeiten**") - timestamp = datetime.datetime.fromtimestamp(int(t_stamp)) - input_time = ui.time().props('format24h now-btn').classes('w-full justify-center') - - input_time.value = timestamp.strftime('%H:%M') - - def save_entry(day): - nonlocal t_stamp - t_stamp = f"{t_stamp}\n" - position = timestamps.index(t_stamp) - new_time_stamp = datetime.datetime(int(select_year.value), - int(select_month.value), day, - int(input_time.value[:2]), - int(input_time.value[-2:])) - timestamps[position] = str( - int(new_time_stamp.timestamp())) + "\n" - - current_user = user(time_user.value) - current_user.write_edited_timestamps(timestamps, - select_year.value, - select_month.value) - edit_dialog.close() - calendar_card.clear() - update_month_and_year() - month_header.set_content( - f"###Buchungen für {calendar.month_name[int(select_month.value)]} {select_year.value}") - ui.notify("Eintrag gespeichert") - - def del_entry(): - nonlocal t_stamp - t_stamp = f"{t_stamp}\n" - timestamps.remove(t_stamp) - timestamps.sort() - current_user = user(time_user.value) - current_user.write_edited_timestamps(timestamps, - select_year.value, - select_month.value) - edit_dialog.close() - calendar_card.clear() - update_month_and_year() - month_header.set_content( - f"###Buchungen für {calendar.month_name[int(select_month.value)]} {select_year.value}") - ui.notify("Eintrag gelöscht") - - with ui.row(): - ui.button("Speichern", - on_click=lambda day=day: save_entry(day)) - ui.button("Löschen", on_click=del_entry) - ui.button("Abbrechen", on_click=edit_dialog.close) - - edit_dialog.open() - for i in range(0, len(timestamps_dict[day]), 2): - try: - temp_pair = [ timestamps_dict[day][i] , timestamps_dict[day][i+1] ] - with ui.card().classes('bg-inherit'): - with ui.row(): - for j in temp_pair: - timestamp_button = ui.button(datetime.datetime.fromtimestamp(int(j)).strftime('%H:%M'), on_click=lambda t_stamp=j, day=day: edit_entry(t_stamp, day)) - if archive_status: - timestamp_button.disable() - except Exception as e: - if len(timestamps_dict[day]) % 2 != 0: - with ui.card().classes('bg-inherit'): - 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) + if archive_status == True: + 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') else: - ui.space() - - # Arbeitszeit Ist bestimmen - - timestamps_of_this_day = [] - - # Suche mir alle timestamps für diesen Tag - for i in timestamps: - actual_timestamp = datetime.datetime.fromtimestamp(int(i)) - timestamp_day = actual_timestamp.day - - if int(timestamp_day) == int(day): - timestamps_of_this_day.append(i) - - timestamps_of_this_day.sort() - - time_sum = 0 - if len(timestamps_of_this_day) > 1: - - if len(timestamps_of_this_day) % 2 == 0: - for i in range(0, len(timestamps_of_this_day), 2): - time_delta = int( - timestamps_of_this_day[i + 1]) - int( - timestamps_of_this_day[i]) - time_sum = time_sum + time_delta - else: - for i in range(0, len(timestamps_of_this_day) - 1, 2): - time_delta = int( - timestamps_of_this_day[i + 1]) - int( - timestamps_of_this_day[i]) - time_sum = time_sum + time_delta - - ui.markdown(convert_seconds_to_hours(time_sum)).classes('text-right') - else: - ui.markdown("Kein") - - # Arbeitszeitsoll bestimmen - - hours_to_work = int(current_user.get_day_workhours(select_year.value, select_month.value, day)) - if hours_to_work < 0: + calendar_card.classes('bg-white') + # Überschriften + ui.markdown("**Datum**") + ui.markdown("**Buchungen**") + ui.space() + ui.markdown("**Ist**") + ui.markdown("**Soll**") + ui.markdown("**Saldo**") ui.space() - day_type.content="Kein Arbeitsverhältnis" - day_type.set_visibility(True) - else: - ui.markdown(f"{convert_seconds_to_hours(int(hours_to_work) * 3600)}").classes('text-right') - if int(hours_to_work) == 0: - day_type.content = "**Kein Arbeitstag**" - day_type.set_visibility(True) - if day_in_list.strftime("%Y-%m-%d") in data["holidays"]: - day_type.content = f'**{data["holidays"][day_in_list.strftime("%Y-%m-%d")]}**' + timestamps = current_user.get_timestamps(year=select_year.value, month=select_month.value) + user_absent = current_user.get_absence(year=select_year.value, month=select_month.value) + # Dictionary für sortierte Timestamps + timestamps_dict = { } + # Dictionary mit zunächst leeren Tageinträgen befüllen + for day in range(1, monthrange(int(select_year.value), int(select_month.value))[1] + 1): + # Jeder Tag bekommt eine leere Liste + timestamps_dict[day] = [ ] - # Saldo für den Tag berechnen + # Alle Timestamps durchgehen und sie den Dictionaryeinträgen zuordnen: + for stamp in timestamps: + day_of_month_of_timestamp = int(datetime.datetime.fromtimestamp(int(stamp)).day) + timestamps_dict[day_of_month_of_timestamp].append(int(stamp)) - if time.time() > day_in_list.timestamp(): + general_saldo = 0 - time_duty = int(current_user.get_day_workhours(select_year.value, select_month.value, day)) * 3600 - if time_duty < 0: - ui.space() - else: - 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 + for day in list(timestamps_dict): + # Datum für Tabelle konstruieren + day_in_list = datetime.datetime(int(select_year.value), int(select_month.value), day) + class_content = "" + if day_in_list.date() == datetime.datetime.now().date(): + class_content = 'font-bold text-red-700 uppercase' + ui.markdown(f"{day_in_list.strftime('%a')}., {day}. {calendar.month_name[int(select_month.value)]}").classes(class_content) - general_saldo = general_saldo + saldo - ui.markdown(convert_seconds_to_hours(saldo)).classes('text-right') - else: - ui.markdown("-").classes('text-center') - - def add_entry(day): - with ui.dialog() as add_dialog, ui.card(): - ui.markdown("###Eintrag hinzufügen") - input_time = ui.time().classes('w-full justify-center') - - def add_entry_save(): - if input_time.value == None: - ui.notify("Bitte eine Uhrzeit auswählen.") - return - - 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) - current_user.timestamp(stamptime=int(new_time_stamp)) - calendar_card.clear() - update_month_and_year() - add_dialog.close() - ui.notify("Eintrag hinzugefügt") - with ui.grid(columns=3): - ui.button("Speichern", on_click=add_entry_save) - ui.space() - ui.button("Abbrechen", on_click=add_dialog.close) - add_dialog.open() - add_dialog.move(calendar_card) - - def add_absence(absence_type, day): - with ui.dialog() as dialog, ui.card().classes('w-[350px]'): - ui.markdown(f'Für welchen Zeitraum soll *{absence_entries[absence_type]["name"]}* eingetragen werden?').classes('w-full') - absence_dates = ui.date().props('range today-btn').classes('w-full justify-center') - absence_dates._props['locale'] = {'daysShort': ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'], - 'months': ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], - 'monthsShort': ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez']} - absence_dates._props['title'] = absence_entries[absence_type]["name"] - absence_dates._props['minimal'] = True - - 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) - current_sel_month = select_month.value - current_sel_year = select_year.value - update_user() - update_months() - select_year.value = current_sel_year - select_month.value = current_sel_month - calendar_card.clear() - update_month_and_year() - - # Bei Zeitbereich, aufteilen - elif isinstance(absence_dates.value, dict): - start_date = absence_dates.value["from"] - end_date = absence_dates.value["to"] - start_date = start_date.split("-") - end_date = end_date.split("-") - - start_year = int(start_date[0]) - end_year = int(end_date[0]) - start_month = int(start_date[1]) - end_month = int(end_date[1]) - start_day = int(start_date[2]) - end_day = int(end_date[2]) - - start_date = datetime.datetime(start_year, start_month, start_day) - end_date = datetime.datetime(end_year, end_month, end_day) - actual_date = start_date - - while actual_date <= end_date: - absences = current_user.get_absence(actual_date.year, actual_date.month) - - if str(actual_date.day) in list(absences): - current_user.del_absence(actual_date.year, actual_date.month, actual_date.day) - ui.notify(f"Eintrag {absence_entries[absences[str(actual_date.day)]]['name']} am {actual_date.day}.{actual_date.month}.{actual_date.year} überschrieben.") - current_user.update_absence(actual_date.year, actual_date.month, actual_date.day, absence_type) - - actual_date = actual_date + datetime.timedelta(days=1) - clear_card() - ui.notify("Einträge vorgenomomen") - dialog.close() - - - with ui.grid(columns=3).classes('w-full justify-center'): - ui.button("Speichern", on_click=add_absence_save) - ui.space() - ui.button("Abbrechen", on_click=dialog.close) - - dialog.open() - dialog.move(calendar_card) - - def edit_notes(day): - notes = current_user.get_day_notes(select_year.value, select_month.value, day) - def del_note_entry(user): - try: - del(notes[user]) - username_labels[user].delete() - note_labels[user].delete() - del_buttons[user].delete() - except KeyError: - ui.notify("Kann nicht gelöscht werden. Eintrag wurde noch nicht gespeichert.") - - def save_notes(): - if not note_labels["admin"].is_deleted: - notes["admin"] = note_labels["admin"].value - current_user.write_notes(select_year.value, select_month.value, day, notes) - timetable.refresh() - dialog.close() - - with ui.dialog() as dialog, ui.card(): - # Notizen - username_labels = { } - note_labels = { } - del_buttons = { } - - ui.markdown(f'**Notizen für {day}.{current_month}.{current_year}**') - with ui.grid(columns='auto auto auto'): - admin_settings = load_adminsettings() - # Beschreibungsfeld für Admin - username_labels["admin"] = ui.markdown("Administrator:") - # Textarea für Admin - note_labels["admin"] = ui.textarea() - del_buttons["admin"] = ui.button(icon='remove', on_click=lambda user="admin": del_note_entry(user)) - - for name, text in notes.items(): - if name != "admin": - username_labels["user"] = ui.markdown(current_user.fullname) - note_labels["user"] = ui.markdown(text) - del_buttons["user"] = ui.button(icon='remove', on_click=lambda user="user": del_note_entry(user)) - elif name == "admin": - note_labels["admin"].value = text + # Buchungen with ui.row(): - ui.button("OK", on_click=save_notes) - ui.button("Abbrechen", on_click=dialog.close) - dialog.open() - dialog.move(calendar_card) + 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ängig 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() - 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)) + try: + for i in list(user_absent): + if int(i) == day: + absence_button = 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"]}') + if archive_status: + absence_button.disable() + 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() + + def edit_entry(t_stamp, day): + + with ui.dialog() as edit_dialog, ui.card(): + ui.markdown("**Eintrag bearbeiten**") + timestamp = datetime.datetime.fromtimestamp(int(t_stamp)) + input_time = ui.time().props('format24h now-btn').classes('w-full justify-center') + + input_time.value = timestamp.strftime('%H:%M') + + def save_entry(day): + nonlocal t_stamp + t_stamp = f"{t_stamp}\n" + position = timestamps.index(t_stamp) + new_time_stamp = datetime.datetime(int(select_year.value), + int(select_month.value), day, + int(input_time.value[:2]), + int(input_time.value[-2:])) + timestamps[position] = str( + int(new_time_stamp.timestamp())) + "\n" + + current_user = user(time_user.value) + current_user.write_edited_timestamps(timestamps, + select_year.value, + select_month.value) + edit_dialog.close() + calendar_card.clear() + update_month_and_year() + month_header.set_content( + f"###Buchungen für {calendar.month_name[int(select_month.value)]} {select_year.value}") + ui.notify("Eintrag gespeichert") + + def del_entry(): + nonlocal t_stamp + t_stamp = f"{t_stamp}\n" + timestamps.remove(t_stamp) + timestamps.sort() + current_user = user(time_user.value) + current_user.write_edited_timestamps(timestamps, + select_year.value, + select_month.value) + edit_dialog.close() + calendar_card.clear() + update_month_and_year() + month_header.set_content( + f"###Buchungen für {calendar.month_name[int(select_month.value)]} {select_year.value}") + ui.notify("Eintrag gelöscht") + + with ui.row(): + ui.button("Speichern", + on_click=lambda day=day: save_entry(day)) + ui.button("Löschen", on_click=del_entry) + ui.button("Abbrechen", on_click=edit_dialog.close) + + edit_dialog.open() + for i in range(0, len(timestamps_dict[day]), 2): + try: + temp_pair = [ timestamps_dict[day][i] , timestamps_dict[day][i+1] ] + with ui.card().classes('bg-inherit'): + with ui.row(): + for j in temp_pair: + timestamp_button = ui.button(datetime.datetime.fromtimestamp(int(j)).strftime('%H:%M'), on_click=lambda t_stamp=j, day=day: edit_entry(t_stamp, day)) + if archive_status: + timestamp_button.disable() + except Exception as e: + if len(timestamps_dict[day]) % 2 != 0: + with ui.card().classes('bg-inherit'): + 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() + + # Arbeitszeit Ist bestimmen + + timestamps_of_this_day = [] + + # Suche mir alle timestamps für diesen Tag + for i in timestamps: + actual_timestamp = datetime.datetime.fromtimestamp(int(i)) + timestamp_day = actual_timestamp.day + + if int(timestamp_day) == int(day): + timestamps_of_this_day.append(i) + + timestamps_of_this_day.sort() + + time_sum = 0 + if len(timestamps_of_this_day) > 1: + + if len(timestamps_of_this_day) % 2 == 0: + for i in range(0, len(timestamps_of_this_day), 2): + time_delta = int( + timestamps_of_this_day[i + 1]) - int( + timestamps_of_this_day[i]) + time_sum = time_sum + time_delta + else: + for i in range(0, len(timestamps_of_this_day) - 1, 2): + time_delta = int( + timestamps_of_this_day[i + 1]) - int( + timestamps_of_this_day[i]) + time_sum = time_sum + time_delta + + ui.markdown(convert_seconds_to_hours(time_sum)).classes('text-right') + else: + ui.markdown("Kein") + + # Arbeitszeitsoll bestimmen + + hours_to_work = int(current_user.get_day_workhours(select_year.value, select_month.value, day)) + if hours_to_work < 0: + ui.space() + day_type.content="Kein Arbeitsverhältnis" + day_type.set_visibility(True) + else: + ui.markdown(f"{convert_seconds_to_hours(int(hours_to_work) * 3600)}").classes('text-right') + if int(hours_to_work) == 0: + day_type.content = "**Kein Arbeitstag**" + day_type.set_visibility(True) + + if day_in_list.strftime("%Y-%m-%d") in data["holidays"]: + day_type.content = f'**{data["holidays"][day_in_list.strftime("%Y-%m-%d")]}**' + + # Saldo für den Tag berechnen + + if time.time() > day_in_list.timestamp(): + + time_duty = int(current_user.get_day_workhours(select_year.value, select_month.value, day)) * 3600 + if time_duty < 0: + ui.space() + else: + 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)).classes('text-right') + else: + ui.markdown("-").classes('text-center') + + def add_entry(day): + with ui.dialog() as add_dialog, ui.card(): + ui.markdown("###Eintrag hinzufügen") + input_time = ui.time().classes('w-full justify-center') + + def add_entry_save(): + if input_time.value == None: + ui.notify("Bitte eine Uhrzeit auswählen.") + return + + 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) + current_user.timestamp(stamptime=int(new_time_stamp)) + calendar_card.clear() + update_month_and_year() + add_dialog.close() + ui.notify("Eintrag hinzugefügt") + with ui.grid(columns=3): + ui.button("Speichern", on_click=add_entry_save) + ui.space() + ui.button("Abbrechen", on_click=add_dialog.close) + add_dialog.open() + add_dialog.move(calendar_card) + + def add_absence(absence_type, day): + with ui.dialog() as dialog, ui.card().classes('w-[350px]'): + ui.markdown(f'Für welchen Zeitraum soll *{absence_entries[absence_type]["name"]}* eingetragen werden?').classes('w-full') + absence_dates = ui.date().props('range today-btn').classes('w-full justify-center') + absence_dates._props['locale'] = {'daysShort': ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'], + 'months': ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], + 'monthsShort': ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez']} + absence_dates._props['title'] = absence_entries[absence_type]["name"] + absence_dates._props['minimal'] = True + + 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) + current_sel_month = select_month.value + current_sel_year = select_year.value + update_user() + update_months() + select_year.value = current_sel_year + select_month.value = current_sel_month + calendar_card.clear() + update_month_and_year() + + # Bei Zeitbereich, aufteilen + elif isinstance(absence_dates.value, dict): + start_date = absence_dates.value["from"] + end_date = absence_dates.value["to"] + start_date = start_date.split("-") + end_date = end_date.split("-") + + start_year = int(start_date[0]) + end_year = int(end_date[0]) + start_month = int(start_date[1]) + end_month = int(end_date[1]) + start_day = int(start_date[2]) + end_day = int(end_date[2]) + + start_date = datetime.datetime(start_year, start_month, start_day) + end_date = datetime.datetime(end_year, end_month, end_day) + actual_date = start_date + + while actual_date <= end_date: + absences = current_user.get_absence(actual_date.year, actual_date.month) + + if str(actual_date.day) in list(absences): + current_user.del_absence(actual_date.year, actual_date.month, actual_date.day) + ui.notify(f"Eintrag {absence_entries[absences[str(actual_date.day)]]['name']} am {actual_date.day}.{actual_date.month}.{actual_date.year} überschrieben.") + current_user.update_absence(actual_date.year, actual_date.month, actual_date.day, absence_type) + + actual_date = actual_date + datetime.timedelta(days=1) + clear_card() + ui.notify("Einträge vorgenomomen") + dialog.close() + + + with ui.grid(columns=3).classes('w-full justify-center'): + ui.button("Speichern", on_click=add_absence_save) + ui.space() + ui.button("Abbrechen", on_click=dialog.close) + + dialog.open() + dialog.move(calendar_card) + + def edit_notes(day): + notes = current_user.get_day_notes(select_year.value, select_month.value, day) + def del_note_entry(user): + try: + del(notes[user]) + username_labels[user].delete() + note_labels[user].delete() + del_buttons[user].delete() + except KeyError: + ui.notify("Kann nicht gelöscht werden. Eintrag wurde noch nicht gespeichert.") + + def save_notes(): + if not note_labels["admin"].is_deleted: + notes["admin"] = note_labels["admin"].value + current_user.write_notes(select_year.value, select_month.value, day, notes) + timetable.refresh() + dialog.close() + + with ui.dialog() as dialog, ui.card(): + # Notizen + username_labels = { } + note_labels = { } + del_buttons = { } + + ui.markdown(f'**Notizen für {day}.{current_month}.{current_year}**') + with ui.grid(columns='auto auto auto'): + admin_settings = load_adminsettings() + # Beschreibungsfeld für Admin + username_labels["admin"] = ui.markdown("Administrator:") + # Textarea für Admin + note_labels["admin"] = ui.textarea() + del_buttons["admin"] = ui.button(icon='remove', on_click=lambda user="admin": del_note_entry(user)) + + for name, text in notes.items(): + if name != "admin": + username_labels["user"] = ui.markdown(current_user.fullname) + note_labels["user"] = ui.markdown(text) + del_buttons["user"] = ui.button(icon='remove', on_click=lambda user="user": del_note_entry(user)) + elif name == "admin": + note_labels["admin"].value = text + + with ui.row(): + ui.button("OK", on_click=save_notes) + ui.button("Abbrechen", on_click=dialog.close) + dialog.open() + dialog.move(calendar_card) + + 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() + if datetime.datetime.now().day < day: + menu_item.disable() + menu_item.tooltip("Kann keine Zeiteinträge für die Zukunft vornehmen.") + 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)) + if archive_status: + menu_item.disable() + if str(day) in list(user_absent): + menu_item.disable() if archive_status: - menu_item.disable() - if datetime.datetime.now().day < day: - menu_item.disable() - menu_item.tooltip("Kann keine Zeiteinträge für die Zukunft vornehmen.") - 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)) - if archive_status: - menu_item.disable() - if str(day) in list(user_absent): - menu_item.disable() - if archive_status: - menu_button.disable() + menu_button.disable() - #ui.button("Eintrag hinzufügen", on_click=lambda day=day: add_entry(day)) + #ui.button("Eintrag hinzufügen", on_click=lambda day=day: add_entry(day)) - #4x leer und dann Gesamtsaldo - ui.space().classes('col-span-5') - ui.markdown(f"{convert_seconds_to_hours(general_saldo)}").classes('text-right') - ui.markdown("Stunden aus Vormonat").classes('col-span-5 text-right') - last_months_overtime = current_user.get_last_months_overtime(select_year.value, select_month.value) - ui.markdown(f"{convert_seconds_to_hours(last_months_overtime)}").classes('text-right') - ui.markdown("Gesamtsaldo").classes('col-span-5 text-right') - ui.markdown(f"**{convert_seconds_to_hours(general_saldo + last_months_overtime)}**").classes('text-right') + #4x leer und dann Gesamtsaldo + ui.space().classes('col-span-5') + ui.markdown(f"{convert_seconds_to_hours(general_saldo)}").classes('text-right') + ui.markdown("Stunden aus Vormonat").classes('col-span-5 text-right') + last_months_overtime = current_user.get_last_months_overtime(select_year.value, select_month.value) + ui.markdown(f"{convert_seconds_to_hours(last_months_overtime)}").classes('text-right') + ui.markdown("Gesamtsaldo").classes('col-span-5 text-right') + ui.markdown(f"**{convert_seconds_to_hours(general_saldo + last_months_overtime)}**").classes('text-right') - table_grid.move(calendar_card) + table_grid.move(calendar_card) - update_month_and_year() + update_month_and_year() - def clear_card(): - calendar_card.clear() - button_update.delete() - update_month_and_year() - 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}**") + def clear_card(): + calendar_card.clear() + button_update.delete() + update_month_and_year() + 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) + 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) + with ui.tab_panel(user_summary): + + global overview_table + @ui.refreshable + def overview_table(): + ov_columns = [ {'label': 'Benutzername', 'name': 'username', 'field': 'username'}, + {'label': 'Name', 'name': 'name', 'field': 'name'}, + {'label': 'Zeitsaldo', 'name': 'time', 'field': 'time'}, + {'label': 'Urlaub', 'name': 'vacation', 'field': 'vacation'}, + {'label': 'Resturlaub', 'name': 'vacation_remaining', 'field': 'vacation_remaining'}, + {'label': 'Krankheit', 'name': 'illness', 'field': 'illness'}] + ov_rows = [ ] + for username in userlist: + actual_user = user(username) + ov_rows.append( + {'username': username, 'name': actual_user.fullname, 'time': f'{convert_seconds_to_hours(actual_user.get_last_months_overtime() + actual_user.get_worked_time()[0])} h', 'vacation': f'{actual_user.count_absence_days("U")} Tage', + 'vacation_remaining': f'{actual_user.get_vacation_claim() - actual_user.count_absence_days("U")} Tage', 'illness': f'{actual_user.count_absence_days("K")} Tage'}) + ui.table(columns=ov_columns, rows=ov_rows, row_key='username', column_defaults={'align': 'left', 'sortable': True}) + + overview_table() + ui.button("Aktualisieren", on_click=overview_table.refresh) + + with ui.tab_panel(vacation_applications): + date_string = '%d.%m.%Y' + + @ui.refreshable + def va_table(): + va_columns = [ {'label': 'Benutzername', 'name': 'username', 'field': 'username'}, + {'label': 'Name', 'name': 'name', 'field': 'name'}, + {'label': 'key', 'name': 'key', 'field': 'key', 'classes': 'hidden', 'headerClasses': 'hidden'}, + {'label': 'Anfang', 'name': 'start', 'field': 'start'}, + {'label': 'Ende', 'name': 'end', 'field': 'end'}, + {'label': 'Resturlaub', 'name': 'vacation_remaining', 'field': 'vacation_remaining'}, + {'label': 'Resturlaub nach Genehmigung', 'name': 'vacation_remaining_after_submission', 'field': 'vacation_remaining_after_submission'} + ] + va_rows = [ ] + + for username in userlist: + actual_user = user(username) + open_va = actual_user.get_open_vacation_applications() + for i, dates in open_va.items(): + startdate_dt = datetime.datetime.strptime(dates[0], '%Y-%m-%d') + enddate_dt = datetime.datetime.strptime(dates[1], '%Y-%m-%d') + vacation_start_to_end = (enddate_dt-startdate_dt).days + vacation_duration = 0 + for day_counter in range(0, vacation_start_to_end): + new_date = startdate_dt + datetime.timedelta(days=day_counter) + if int(actual_user.get_day_workhours(new_date.year, new_date.month, new_date.day)) > 0: + if not datetime.datetime(new_date.year, new_date.month, new_date.day).strftime('%Y-%m-%d') in list(load_adminsettings()["holidays"]): + vacation_duration += 1 + + va_rows.append({'username': username, + 'name': actual_user.fullname, + 'key': f'{username}-{i}', + 'start': startdate_dt.strftime(date_string), + 'end': enddate_dt.strftime(date_string), + 'vacation_remaining': f'{actual_user.get_vacation_claim() - actual_user.count_absence_days("U", startdate_dt.year)} Tage', + 'vacation_remaining_after_submission': f'{actual_user.get_vacation_claim() - actual_user.count_absence_days("U", startdate_dt.year) - vacation_duration } Tage'}) + global vacation_table + vacation_table = ui.table(columns=va_columns, rows=va_rows, row_key='key', selection='multiple', column_defaults={'align': 'left', 'sortable': True}) + va_table() + + def approve_vacation(): + global vacation_table + for selection in vacation_table.selected: + key = selection["key"] + username = key.split("-")[0] + index = key.split("-")[1] + actual_user = user(username) + startdate_dt = datetime.datetime.strptime(selection["start"], date_string) + enddate_dt = datetime.datetime.strptime(selection["end"], date_string) + delta = (enddate_dt - startdate_dt).days + for i in range(0, delta): + new_date = startdate_dt + datetime.timedelta(days=i) + if int(actual_user.get_day_workhours(new_date.year, new_date.month, new_date.day)) > 0: + if not datetime.datetime(new_date.year, new_date.month, new_date.day).strftime('%Y-%m-%d') in list(load_adminsettings()["holidays"]): + actual_user.update_absence(new_date.year, new_date.month, new_date.day, "U") + ui.notify(f"Urlaub vom {selection['start']} bis {selection['end']} für {actual_user.fullname} eingetragen") + try: + retract_result = actual_user.revoke_vacation_application(index) + except IndexError: + ui.notify("Fehler beim Entfernen des Urlaubsantrages nach dem Eintragen.") + va_table.refresh() + timetable.refresh() + + ui.button("Aktualisieren", on_click=va_table.refresh) + ui.separator() + with ui.grid(columns=2): + ui.button("Genehmigen", on_click=approve_vacation) + ui.button("Ablehnen") with ui.tab_panel(settings): with ui.grid(columns='auto auto'): @@ -1240,6 +1343,7 @@ Dies kann nicht rückgängig gemacht werden!''') ui.button("Speichern", on_click=save_workhours) ui.button("Löschen", on_click=delete_workhour_entry) user_selection_changed() + with ui.tab_panel(backups): try: diff --git a/lib/api.py b/lib/api.py index e047134..58676d9 100644 --- a/lib/api.py +++ b/lib/api.py @@ -525,7 +525,7 @@ def json_info(api_key: str): data["time"]["overall"] = time_saldo data["vacation"] = { } data["vacation"]["claim"] = current_user.get_vacation_claim(now_dt.year, now_dt.month, now_dt.day) - data["vacation"]["used"] = current_user.count_vacation_days(now_dt.year) + data["vacation"]["used"] = current_user.count_absence_days("U", now_dt.year) data["vacation"]["remaining"] = data["vacation"]["claim"] - data["vacation"]["used"] return data break diff --git a/lib/homepage.py b/lib/homepage.py index 8a455c7..23c07cb 100644 --- a/lib/homepage.py +++ b/lib/homepage.py @@ -262,9 +262,10 @@ def homepage(): va_table = ui.table(columns=va_columns, rows=va_rows, selection="single", row_key="index").classes('w-full') def retract_va(): try: - current_user.revoke_vacation_application(va_table.selected[0]["index"]) + retract_result = current_user.revoke_vacation_application(va_table.selected[0]["index"]) open_vacation_applications.refresh() - ui.notify("Urlaubsantrag zurückgezogen") + if retract_result == 0: + ui.notify("Urlaubsantrag zurückgezogen") except IndexError: ui.notify("Kein Urlaubsanstrag ausgewählt") ui.button("Zurückziehen", on_click=retract_va).tooltip("Hiermit wird der oben gewählte Urlaubsantrag zurückgezogen.").classes('w-full') diff --git a/lib/users.py b/lib/users.py index 06b2bb3..3d37674 100644 --- a/lib/users.py +++ b/lib/users.py @@ -5,6 +5,8 @@ import hashlib import os from calendar import monthrange from stat import S_IREAD, S_IWUSR +from nicegui import ui + import datetime import time import json @@ -61,7 +63,6 @@ class user: with open(filename, 'a') as file: # Schreibe den Timestamp in die Datei und füge einen Zeilenumbruch hinzu file.write(f"{timestamp}\n") - file.close() except FileNotFoundError: # Fehlende Verzeichnisse anlegen folder_path = os.path.dirname(filename) @@ -263,7 +264,7 @@ class user: 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): + def get_last_months_overtime(self, year=datetime.datetime.now().year, month=datetime.datetime.now().month): try: if int(month) == 1: year = str(int(year) - 1) @@ -387,7 +388,7 @@ class user: hours_to_work = -1 return hours_to_work - def get_vacation_claim(self, year, month, day): + def get_vacation_claim(self, year=datetime.datetime.now().year, month=datetime.datetime.now().month, day=datetime.datetime.now().day): workhour_entries = list(self.workhours) workhour_entries.sort() day_to_check = datetime.datetime(int(year), int(month), int(day)) @@ -405,18 +406,18 @@ class user: return int(claim) - def count_vacation_days(self, year): - vacation_used = 0 + def count_absence_days(self, absence_code: str, year=datetime.datetime.now().year): + absence_days = 0 for month in range(0, 13): try: absence_dict = self.get_absence(year, month) for entry, absence_type in absence_dict.items(): - if absence_type == "U": - vacation_used += 1 + if absence_type == absence_code: + absence_days += 1 except: pass - return vacation_used + return absence_days def delete_photo(self): os.remove(self.photofile) @@ -477,14 +478,19 @@ class user: application_file = os.path.join(self.userfolder, va_file) with open(application_file, 'r') as json_file: applications = json.load(json_file) - del(applications[index]) - new_applications = { } - new_index = 0 - for index, dates in applications.items(): - new_applications[new_index] = dates - new_index += 1 - with open(application_file, 'w') as json_file: - json_file.write(json.dumps(new_applications, indent=4)) + try: + del(applications[index]) + new_applications = { } + new_index = 0 + for index, dates in applications.items(): + new_applications[new_index] = dates + new_index += 1 + with open(application_file, 'w') as json_file: + json_file.write(json.dumps(new_applications, indent=4)) + return 0 + except KeyError: + ui.notify("Urlaubsantrag wurde schon bearbeitet") + return -1 # Benutzer auflisten def list_users(): diff --git a/settings.json b/settings.json index a900a48..084b85c 100644 --- a/settings.json +++ b/settings.json @@ -71,6 +71,7 @@ "2030-10-03": "Tag der deutschen Einheit", "2030-10-30": "Reformationstag", "2030-12-25": "1. Weihnachtsfeiertag", - "2030-12-26": "2. Weihnachtsfeiertag" + "2030-12-26": "2. Weihnachtsfeiertag", + "2025-06-11": "Testeintrag" } } \ No newline at end of file