Restructuring, Rangefunktion Abwesenheit

py-Dateien restrukturiert
Arbeitszeitenfunktion hinzugefügt
Rangefunktion für Abwesenheit hinzugefügt
This commit is contained in:
Alexander Malzkuhn 2025-04-25 23:24:22 +02:00
parent 9a4bf599dd
commit e86ddac778
7 changed files with 798 additions and 747 deletions

674
admin.py Normal file
View File

@ -0,0 +1,674 @@
from datetime import datetime
from nicegui import ui, app
from users import *
from definitions import *
from calendar import monthrange
from web_ui import *
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)
select_year.value = list(available_years)[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)
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
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"**<ins>{convert_seconds_to_hours(general_saldo + last_months_overtime)}</ins>**")
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.label("Benutzername:")
username_input = ui.input()
ui.label("Voller Name:")
fullname_input = ui.input()
ui.label("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")

41
login.py Normal file
View File

@ -0,0 +1,41 @@
from datetime import datetime
from nicegui import ui, app
from web_ui import *
from users import *
from definitions import *
from calendar import monthrange
import hashlib
import calendar
import locale
@ui.page('/login')
def page_login():
# Settingsdatei einlesen
data = load_adminsettings()
def login():
nonlocal data
if username.value == data["admin_user"]:
if password.value == data["admin_password"]:
active_login = cookie_hash(data["admin_user"], data["admin_password"])
app.storage.user['secret'] = active_login
ui.navigate.to("/admin")
else:
ui.notify("Login fehlgeschlagen")
#ui.markdown(f"## {app_title} {app_version}")
#ui.markdown("Bitte einloggen")
pageheader("Bitte einloggen:")
with ui.grid(columns=2):
ui.markdown("Benutzer:")
username = ui.input('Benutzername')
ui.markdown("Passwort:")
password = ui.input('Passwort', password=True)
ui.button(text="Login", on_click=lambda: login())

View File

@ -0,0 +1,7 @@
from web_ui import *
from admin import *
from login import *
from users import *
from touchscreen import *
ui.run(port=8090, storage_secret="test")

57
touchscreen.py Normal file
View File

@ -0,0 +1,57 @@
from datetime import datetime
from nicegui import ui, app
from users import *
from definitions import *
from web_ui import *
from calendar import monthrange
import hashlib
import calendar
import locale
@ui.page('/touchscreen')
def page_touchscreen():
def button_click(name):
nonlocal buttons
current_user = user(name)
current_user.timestamp()
if current_user.stamp_status() == status_in:
buttons[name].props('color=green')
ui.notify(status_in)
else:
buttons[name].props('color=red')
ui.notify(status_out)
pageheader("Bitte User auswählen:")
userlist = list_users()
number_of_users = len(userlist)
buttons = { }
if number_of_users > 5:
number_of_columns = 5
else:
number_of_columns = number_of_users
with ui.grid(columns=number_of_columns):
for name in userlist:
current_user = user(name)
current_button = ui.button(on_click=lambda name=name: button_click(name))
with current_button:
try:
with open(current_user.photofile, 'r') as file:
pass
file.close()
ui.image(current_user.photofile)
except:
pass
ui.label(current_user.fullname)
if current_user.stamp_status() == status_in:
current_button.props('color=green')
else:
current_button.props('color=red')
buttons[name] = current_button

View File

@ -270,6 +270,24 @@ class user:
with open(f"{self.userfolder}/{int(year)}-{int(month)}.json", "w") as json_file:
json_file.write(json_dict)
def get_day_workhours(self, year, month, day):
workhour_entries = list(self.workhours)
workhour_entries.sort()
day_to_check = datetime.datetime(int(year), int(month), int(day))
for entry in reversed(workhour_entries):
entry_split = entry.split("-")
entry_dt = datetime.datetime(int(entry_split[0]), int(entry_split[1]), int(entry_split[2]))
if entry_dt <= day_to_check:
weekday = day_to_check.strftime("%w")
if int(weekday) == 0:
weekday = str(7)
hours_to_work = self.workhours[entry][weekday]
else:
# Wenn vor Einstellungsdatum -1 ausgeben
hours_to_work = -1
return hours_to_work
# Benutzer auflisten
def list_users():
users = [d for d in os.listdir(userfolder) if os.path.isdir(os.path.join(userfolder, d))]

View File

@ -4,9 +4,6 @@
"absence": {
"7": "K",
"8": "UU",
"9": "KK",
"10": "U",
"11": "U",
"29": "U"
"9": "KK"
}
}

743
web_ui.py
View File

@ -1,4 +1,3 @@
import datetime
from datetime import datetime
from nicegui import ui, app
@ -63,746 +62,6 @@ def convert_seconds_to_hours(seconds):
else:
return(f"{hours}:{minutes}")
@ui.page('/login')
def page_login():
# Settingsdatei einlesen
data = load_adminsettings()
def login():
nonlocal data
if username.value == data["admin_user"]:
if password.value == data["admin_password"]:
active_login = cookie_hash(data["admin_user"], data["admin_password"])
app.storage.user['secret'] = active_login
ui.navigate.to("/admin")
else:
ui.notify("Login fehlgeschlagen")
#ui.markdown(f"## {app_title} {app_version}")
#ui.markdown("Bitte einloggen")
pageheader("Bitte einloggen:")
with ui.grid(columns=2):
ui.markdown("Benutzer:")
username = ui.input('Benutzername')
ui.markdown("Passwort:")
password = ui.input('Passwort', password=True)
ui.button(text="Login", on_click=lambda: login())
@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)
select_year.value = list(available_years)[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
workhour_entries = list(current_user.workhours)
workhour_entries.sort()
found_match = False
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)
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
# Saldo für den Tag berechnen
try:
if time.time() > day_in_list.timestamp():
time_duty = int(current_user.workhours[entry][str(weekday_index)]) * 3600
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():
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)
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):
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"**<ins>{convert_seconds_to_hours(general_saldo + last_months_overtime)}</ins>**")
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.label("Benutzername:")
username_input = ui.input()
ui.label("Voller Name:")
fullname_input = ui.input()
ui.label("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")
@ui.page('/stamping')
def page_stamping():
ui.label('Stempelsteite')
@ui.page('/touchscreen')
def page_touchscreen():
def button_click(name):
nonlocal buttons
current_user = user(name)
current_user.timestamp()
if current_user.stamp_status() == status_in:
buttons[name].props('color=green')
ui.notify(status_in)
else:
buttons[name].props('color=red')
ui.notify(status_out)
pageheader("Bitte User auswählen:")
userlist = list_users()
number_of_users = len(userlist)
buttons = { }
if number_of_users > 5:
number_of_columns = 5
else:
number_of_columns = number_of_users
with ui.grid(columns=number_of_columns):
for name in userlist:
current_user = user(name)
current_button = ui.button(on_click=lambda name=name: button_click(name))
with current_button:
try:
with open(current_user.photofile, 'r') as file:
pass
file.close()
ui.image(current_user.photofile)
except:
pass
ui.label(current_user.fullname)
if current_user.stamp_status() == status_in:
current_button.props('color=green')
else:
current_button.props('color=red')
buttons[name] = current_button
@ui.page('/userlist')
def page_userlist():
@ -818,6 +77,4 @@ def page_userlist():
button = ui.button(text=name, on_click=lambda name=name:click_button(name) )
buttons[name] = button
ui.run(port=8090, storage_secret="test")