from datetime import datetime from nicegui import ui, app, events from users import * from definitions import * from calendar import monthrange from web_ui import * import os.path import hashlib import calendar import locale @ui.page('/admin') def page_admin(): ui.page_title(f"{app_title} {app_version}") data = load_adminsettings() active_login = cookie_hash(data["admin_user"], data["admin_password"]) try: browser_cookie = app.storage.user['secret'] except: browser_cookie = "" # Adminseite if browser_cookie == active_login: pageheader("Administration") def admin_logout(): app.storage.user['secret'] = "" ui.navigate.to("/login") ui.button("Logout", on_click=admin_logout) with ui.tabs() as tabs: time_overview = ui.tab('Zeitübersichten') admin_user = ui.tab('Admin Benutzer') users = ui.tab('Benutzer') settings = ui.tab('Einstellungen') with ((ui.tab_panels(tabs, value=time_overview))): with ui.tab_panel(time_overview): ui.markdown("##Übersichten") # Tabelle konstruieren with ui.card(): with ui.row() as timetable_header: year_binder = ValueBinder() month_binder = ValueBinder() def update_months(): current_user = user(time_user.value) available_months = current_user.get_months(year_binder.value) available_months_dict = { } for element in available_months: available_months_dict[element] = calendar.month_name[int(element)] 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) 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.value = list(available_months)[0] userlist = list_users() ui.markdown("Benutzer:") time_user = ui.select(options=userlist, value=userlist[0], on_change=update_user) user_to_select_for_start = userlist[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 = { } for element in available_months: available_months_dict[element] = calendar.month_name[int(element)] if current_month in available_months: set_month = current_month else: set_month = available_months[0] if str(current_year) in available_years: set_year = str(current_year) else: set_year = (available_years[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') 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: def update_month_and_year(): with ui.grid(columns='auto auto 1fr 1fr 1fr 1fr') as table_grid: ui.markdown("**Datum**") ui.markdown("**Buchungen**") ui.markdown("**Ist**") ui.markdown("**Soll**") ui.markdown("**Saldo**") ui.space() current_user = user(time_user.value) 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)).strftime("%-d")) 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) ui.markdown(f"{day_in_list.strftime('%a')}., {day}. {calendar.month_name[int(select_month.value)]}") # 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"], 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() 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().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(len(timestamps_dict[day])): try: temp_pair = [ timestamps_dict[day][i] , timestamps_dict[day][i+1] ] with ui.card(): with ui.row(): for j in temp_pair: ui.button(datetime.datetime.fromtimestamp(int(j)).strftime('%H:%M'), on_click=lambda t_stamp=j, day=day: edit_entry(t_stamp, day)) except: if len(timestamps_dict[day]) % 2 != 0: with ui.card(): 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)) # 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.strftime('%-d') 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)) 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() else: 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) # 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)) else: ui.markdown("-") 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(): 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) 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: current_user.workhours 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): 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): ui.space() ui.markdown(f"{convert_seconds_to_hours(general_saldo)}") for i in range(4): ui.space() ui.markdown("Stunden aus Vormonat") 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)}") for i in range(4): ui.space() ui.markdown("Gesamtsaldo") ui.markdown(f"**{convert_seconds_to_hours(general_saldo + last_months_overtime)}**") table_grid.move(calendar_card) update_month_and_year() def clear_card(): calendar_card.clear() 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}**") button_update = ui.button("Aktualisieren", on_click=clear_card) button_update.move(timetable_header) with ui.tab_panel(admin_user): with ui.grid(columns=2): def save_admin_settings(): output_dict = { } output_dict["admin_user"] = admin_user.value output_dict["adnin_password"] = admin_password.value json_dict = json.dumps(output_dict, indent=4) with open(f"{scriptpath}/{usersettingsfilename}", "w") as outputfile: outputfile.write(json_dict) ui.notify("Einstellungen gespeichert") ui.label("Benutzername des Adminstrators") admin_user = ui.input() admin_user.value = data["admin_user"] ui.label("Passwort des Adminsistrators") admin_password = ui.input(password=True) admin_password.value = data["admin_password"] ui.button("Speichern", on_click=save_admin_settings) with ui.tab_panel(users): ui.markdown("###Benutzerverwaltung") userlist = list_users() userlist.sort() workhours = [ ] with ui.row(): def user_selection_changed(): try: if user_selection.value != None: current_user = user(user_selection.value) username_input.value = current_user.username fullname_input.value = current_user.fullname password_input.value = current_user.password usersettingscard.visible = True workhours_select.clear() workhour_list = list(current_user.workhours) workhour_list.sort() workhours_select.set_options(workhour_list) workhours_select.value = workhour_list[0] workinghourscard.visible = True except: pass # workhours_selection_changed(list(current_user.workhours)[0]) def workhours_selection_changed(): if workhours_select.value != None: current_user = user(user_selection.value) selected_workhours = current_user.workhours[workhours_select.value] for key, hours in selected_workhours.items(): try: days[int(key)-1].value = hours except: if key == 0: days[6].value = hours elif key == "vacation": vacation_input.value = hours def save_user_settings(): def save_settings(): current_user = user(user_selection.value) current_user.username = username_input.value current_user.fullname = fullname_input.value current_user.password = password_input.value current_user.write_settings() userlist = list_users() userlist.sort() user_selection.clear() user_selection.set_options(userlist) user_selection.value = current_user.username dialog.close() ui.notify("Einstellungen gespeichert") with ui.dialog() as dialog, ui.card(): if user_selection.value != username_input.value: ui.markdown("**Benutzername wurde geändert.**") ui.markdown(f"Benutzerdaten werden in den neuen Ordner {username_input.value}") ui.markdown("Sollen die Einstellungen gespeichert werden?") with ui.row(): ui.button("Speichern", on_click=save_settings) ui.button("Abbrechen", on_click=dialog.close) dialog.open() def del_user(): current_user = user(user_selection.value) def del_definitely(): current_user.del_user() userlist = list_users() userlist.sort() user_selection.clear() user_selection.set_options(userlist) user_selection.value = userlist[0] dialog.close() ui.notify("Benutzer gelöscht") with ui.dialog() as dialog, ui.card(): ui.markdown(f"Soll der Benutzer *{current_user.username}* gelöscht werden?") ui.markdown("**Dies kann nicht rückgängig gemacht werden?**") with ui.row(): ui.button("Löschen", on_click=del_definitely) ui.button("Abbrechen", on_click=dialog.close) dialog.open() def save_workhours(): def save_settings(): current_user = user(user_selection.value) construct_dict = { } for i in range(7): if i < 7: construct_dict[i+1] = days[i].value elif i == 7: conctruct_dict[0] = days[i].value construct_dict["vacation"] = vacation_input.value current_user.workhours[workhours_select.value] = construct_dict current_user.write_settings() dialog.close() ui.notify("Einstellungen gespeichert") with ui.dialog() as dialog, ui.card(): ui.markdown("Sollen die Änderungen an den Arbeitsstunden und/oder Urlaubstagen gespeichert werden?") with ui.row(): ui.button("Speichern", on_click=save_settings) ui.button("Abrrechen", on_click=dialog.close) dialog.open() def delete_workhour_entry(): def delete_entry(): current_user = user(user_selection.value) del current_user.workhours[workhours_select.value] current_user.write_settings() workhour_list = list(current_user.workhours) workhours_select.clear() workhours_select.set_options(workhour_list) workhours_select.set_value(workhour_list[-1]) #workhours_selection_changed(current_user.workhours[0]) dialog.close() ui.notify("Eintrag gelöscht" "") with ui.dialog() as dialog, ui.card(): current_user = user(user_selection.value) if len(current_user.workhours) > 1: ui.markdown(f"Soll der Eintrag *{workhours_select.value}* wirklich gelöscht werden?") ui.markdown("**Dies kann nicht rückgängig gemacht werden.**") with ui.row(): ui.button("Löschen", on_click=delete_entry) ui.button("Abbrechen", on_click=dialog.close) else: ui.markdown("Es gibt nur einen Eintrag. Dieser kann nicht gelöscht werden.") ui.button("OK", on_click=dialog.close) dialog.open() with ui.column(): user_selection = ui.select(options=userlist, with_input=True, on_change=user_selection_changed) user_selection.value = userlist[0] ui.button("Neu") with ui.column(): with ui.card() as usersettingscard: ui.markdown("**Benutzereinstellungen**") with ui.grid(columns=2): ui.markdown("Benutzername:") username_input = ui.input() ui.markdown("Voller Name:") fullname_input = ui.input() ui.markdown("Passwort") password_input = ui.input(password=True) with ui.grid(columns=2): ui.button("Speichern", on_click=save_user_settings) ui.button("Löschen", on_click=del_user) with ui.card() as workinghourscard: workhours = [] ui.markdown("**Arbeitszeiten**") with ui.card(): def calculate_weekhours(): sum = 0 for i in range(7): try: sum = float(days[i].value) + sum except: pass workhours_sum.set_content(str(sum)) with ui.grid(columns=2): ui.markdown("gültig ab:") workhours_select = ui.select(options=workhours, on_change=workhours_selection_changed) days = [ ] weekdays = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"] counter = 0 for day in weekdays: ui.markdown(f"{day}:") days.append(ui.input(on_change=calculate_weekhours)) counter = counter + 1 ui.separator().classes('col-span-full') ui.markdown("**Summe:**") workhours_sum = ui.markdown() with ui.card(): with ui.grid(columns=2): ui.markdown("Urlaubstage") vacation_input = ui.input() with ui.row(): ui.button("Speichern", on_click=save_workhours) ui.button("Löschen", on_click=delete_workhour_entry) def new_workhours_entry(): current_user = user(user_selection.value) def add_workhours_entry(): workhours_dict = { } for i in range(7): workhours_dict[i] = 0 workhours_dict["vacation"] = 0 current_user.workhours[date_picker.value] = workhours_dict current_user.write_settings() workhours_select.clear() workhours_list = list(current_user.workhours) workhours_list.sort() workhours_select.set_options(workhours_list) workhours_select.value = date_picker.value dialog.close() ui.notify("Eintrag angelegt") with ui.dialog() as dialog, ui.card(): ui.markdown("Geben Sie das Gültigkeitsdatum an, ab wann die Einträge gültig sein sollen.") date_picker = ui.date() with ui.row(): ui.button("OK", on_click=add_workhours_entry) ui.button("Abbrechen", on_click=dialog.close) dialog.open() ui.button("Neu", on_click=new_workhours_entry) user_selection_changed() # Alternativ zur Loginseite navigieren else: ui.navigate.to("/login")