1248 lines
82 KiB
Python
1248 lines
82 KiB
Python
from datetime import datetime
|
|
|
|
import dateutil.easter
|
|
from PIL.SpiderImagePlugin import isInt
|
|
from dateutil.easter import *
|
|
|
|
from nicegui import ui, app, events
|
|
from nicegui.html import button
|
|
|
|
from lib.users import *
|
|
from lib.definitions import *
|
|
from calendar import monthrange
|
|
from lib.web_ui import *
|
|
|
|
import os.path
|
|
import os
|
|
from stat import S_IREAD, S_IRWXU
|
|
import hashlib
|
|
import calendar
|
|
import locale
|
|
import segno
|
|
|
|
@ui.page('/admin')
|
|
def page_admin():
|
|
ui.page_title(f"{app_title} {app_version}")
|
|
data = load_adminsettings()
|
|
|
|
try:
|
|
browser_cookie = app.storage.user['admin_authenticated']
|
|
except:
|
|
browser_cookie = False
|
|
|
|
# Adminseite
|
|
if browser_cookie:
|
|
pageheader("Administration")
|
|
|
|
def admin_logout():
|
|
app.storage.user.pop("admin_authenticated", None)
|
|
ui.navigate.to("/")
|
|
|
|
ui.button("Logout", on_click=admin_logout)
|
|
|
|
with ui.tabs() as tabs:
|
|
|
|
time_overview = ui.tab('Zeitübersichten')
|
|
settings = ui.tab('Einstellungen')
|
|
users = ui.tab('Benutzer')
|
|
|
|
userlist = [ ]
|
|
|
|
def update_userlist():
|
|
nonlocal userlist
|
|
userlist = list_users()
|
|
|
|
update_userlist()
|
|
|
|
with (((ui.tab_panels(tabs, value=time_overview)))):
|
|
|
|
with ui.tab_panel(time_overview):
|
|
ui.markdown("##Übersichten")
|
|
|
|
# Tabelle konstruieren
|
|
with ui.card().classes('w-full'):
|
|
|
|
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()
|
|
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.value = list(available_months)[0]
|
|
except NameError:
|
|
pass
|
|
|
|
ui.markdown("Benutzer:")
|
|
|
|
time_user = ui.select(options=userlist, on_change=update_user)
|
|
time_user.value = userlist[0]
|
|
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
|
|
@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)
|
|
|
|
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
|
|
|
|
json_dict = json.dumps(data)
|
|
|
|
with open(filename, "w") as outputfile:
|
|
outputfile.write(json_dict)
|
|
timetable.refresh()
|
|
dialog.close()
|
|
|
|
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()
|
|
|
|
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()
|
|
|
|
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()
|
|
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()
|
|
|
|
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_button.disable()
|
|
|
|
#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"**<ins>{convert_seconds_to_hours(general_saldo + last_months_overtime)}</ins>**").classes('text-right')
|
|
|
|
table_grid.move(calendar_card)
|
|
|
|
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}**")
|
|
|
|
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(settings):
|
|
with ui.grid(columns='auto auto'):
|
|
with ui.card():
|
|
ui.markdown("**Administrationsbenutzer:**")
|
|
with ui.grid(columns=2):
|
|
def save_admin_settings():
|
|
output_dict = { }
|
|
output_dict["admin_user"] = admin_user.value
|
|
if admin_password.value != "":
|
|
output_dict["admin_password"] = hash_password(admin_password.value)
|
|
else:
|
|
output_dict["admin_password"] = data["admin_password"]
|
|
output_dict["port"] = port.value
|
|
output_dict["secret"] = secret
|
|
output_dict["touchscreen"] = touchscreen_switch.value
|
|
output_dict["times_on_touchscreen"] = timestamp_switch.value
|
|
output_dict["photos_on_touchscreen"] = photo_switch.value
|
|
output_dict["picture_height"] = picture_height_input.value
|
|
output_dict["button_height"] = button_height_input.value
|
|
output_dict["user_notes"] = notes_switch.value
|
|
output_dict["holidays"] = data["holidays"]
|
|
json_dict = json.dumps(output_dict, indent=4)
|
|
with open(os.path.join(scriptpath, usersettingsfilename), "w") as outputfile:
|
|
outputfile.write(json_dict)
|
|
if int(old_port) != int(port.value):
|
|
with ui.dialog() as dialog, ui.card():
|
|
ui.markdown("Damit die Porteinstellungen wirksam werden, muss der Server neu gestartet werden.")
|
|
ui.button("OK", on_click=lambda: dialog.close())
|
|
dialog.open()
|
|
ui.notify("Einstellungen gespeichert")
|
|
timetable.refresh()
|
|
|
|
ui.markdown("Benutzername des Adminstrators")
|
|
admin_user = ui.input().tooltip("Geben Sie hier den Benutzernamen für den Adminstationsnutzer ein")
|
|
admin_user.value = data["admin_user"]
|
|
ui.markdown("Passwort des Administrators")
|
|
admin_password = ui.input(password=True).tooltip("Geben Sie hier das Passwort für den Administationsnutzer ein. Merken Sie sich dieses Passwort gut. Es kann nicht über das Webinterface zurückgesetzt werden.")
|
|
|
|
secret = data["secret"]
|
|
|
|
with ui.card():
|
|
ui.markdown("**Systemeinstellungen:**")
|
|
with ui.grid(columns=2):
|
|
def check_is_number(number):
|
|
try:
|
|
number = int(number)
|
|
return True
|
|
except:
|
|
return False
|
|
|
|
ui.markdown("Port:")
|
|
port = ui.input(validation={"Nur ganzzahlige Portnummern erlaubt": lambda value: check_is_number(value),
|
|
"Portnummer zu klein": lambda value: len(value)>=2}).tooltip("Geben Sie hier die Portnummer ein, unter der die Zeiterfassung erreichbar ist.").props('size=5')
|
|
old_port = data["port"]
|
|
port.value = old_port
|
|
|
|
with ui.card():
|
|
ui.markdown("**Einstellungen für das Touchscreenterminal:**")
|
|
with ui.column():
|
|
touchscreen_switch = ui.switch("Touchscreenterminal aktiviert")
|
|
touchscreen_switch.value = data["touchscreen"]
|
|
timestamp_switch = ui.switch("Stempelzeiten anzeigen").bind_visibility_from(touchscreen_switch, 'value')
|
|
photo_switch = ui.switch("Fotos anzeigen").bind_visibility_from(touchscreen_switch, 'value')
|
|
timestamp_switch.value = bool(data["times_on_touchscreen"])
|
|
with ui.row().bind_visibility_from(touchscreen_switch, 'value'):
|
|
photo_switch.value = bool(data["photos_on_touchscreen"])
|
|
with ui.row().bind_visibility_from(photo_switch, 'value'):
|
|
ui.markdown("Maximale Bilderöhe")
|
|
picture_height_input = ui.input(validation={"Größe muss eine Ganzzahl sein.": lambda value: check_is_number(value),
|
|
"Größe muss größer 0 sein": lambda value: int(value)>0}).props('size=5')
|
|
picture_height_input.value = data["picture_height"]
|
|
ui.markdown('px')
|
|
with ui.row().bind_visibility_from(touchscreen_switch, 'value'):
|
|
ui.markdown("Minimale Buttonhöhe")
|
|
def compare_button_height(height):
|
|
if not photo_switch.value:
|
|
return True
|
|
elif int(height) < int(picture_height_input.value):
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
button_height_input = ui.input(validation={"Größe muss eine Ganzzahl sein.": lambda value: check_is_number(value),
|
|
"Größe muss größer 0 sein": lambda value: int(value)>0,
|
|
"Buttons dürfen nicht kleiner als die Fotos sein": lambda value: compare_button_height(value)}).props('size=5')
|
|
button_height_input.value = data["button_height"]
|
|
photo_switch.on_value_change(button_height_input.validate)
|
|
picture_height_input.on_value_change(button_height_input.validate)
|
|
ui.markdown('px')
|
|
|
|
with ui.card():
|
|
ui.markdown("**Einstellungen für Benutzerfrontend**")
|
|
notes_switch = ui.switch("Notizfunktion aktiviert", value=data["user_notes"])
|
|
|
|
def holiday_section():
|
|
with ui.card():
|
|
ui.markdown('**Feiertage:**')
|
|
|
|
reset_visibility = ValueBinder()
|
|
reset_visibility.value = False
|
|
|
|
def new_holiday_entry():
|
|
def add_holiday_to_dict():
|
|
year_from_picker = int(datepicker.value.split("-")[0])
|
|
month_from_picker = int(datepicker.value.split("-")[1])
|
|
day_from_picker = int(datepicker.value.split("-")[2])
|
|
for i in range(0, int(repetition.value)):
|
|
repetition_date_dt = datetime.datetime(year_from_picker + i, month_from_picker, day_from_picker)
|
|
date_entry = repetition_date_dt.strftime('%Y-%m-%d')
|
|
data["holidays"][date_entry] = description.value
|
|
holiday_buttons_grid.refresh()
|
|
reset_visibility.value = True
|
|
dialog.close()
|
|
|
|
with ui.dialog() as dialog, ui.card():
|
|
with ui.grid(columns='auto auto'):
|
|
ui.markdown('Geben Sie den neuen Feiertag ein:').classes('col-span-2')
|
|
datepicker = ui.date(value=datetime.datetime.now().strftime('%Y-%m-%d')).classes('col-span-2')
|
|
description = ui.input('Beschreibung').classes('col-span-2')
|
|
repetition = ui.number('Für Jahre wiederholen', value=1, min=1, precision=0).classes('col-span-2')
|
|
ui.button("OK", on_click=add_holiday_to_dict)
|
|
ui.button('Abbrechen', on_click=dialog.close)
|
|
dialog.open()
|
|
|
|
@ui.refreshable
|
|
def holiday_buttons_grid():
|
|
|
|
holidays = list(data["holidays"])
|
|
holidays.sort()
|
|
|
|
year_list = []
|
|
|
|
# Jahresliste erzeugen
|
|
for i in holidays:
|
|
i_split = i.split("-")
|
|
year = int(i_split[0])
|
|
year_list.append(year)
|
|
|
|
year_list = list(set(year_list))
|
|
year_dict = {}
|
|
|
|
# Jahresdictionary konstruieren
|
|
for i in year_list:
|
|
year_dict[i] = []
|
|
|
|
for i in holidays:
|
|
i_split = i.split("-")
|
|
year = int(i_split[0])
|
|
month = int(i_split[1])
|
|
day = int(i_split[2])
|
|
date_dt = datetime.datetime(year, month, day)
|
|
year_dict[year].append(date_dt)
|
|
|
|
def del_holiday_entry(entry):
|
|
del(data['holidays'][entry.strftime('%Y-%m-%d')])
|
|
reset_visibility.value = True
|
|
holiday_buttons_grid.refresh()
|
|
|
|
def defined_holidays():
|
|
with ui.dialog() as dialog, ui.card():
|
|
ui.markdown("Bitte wählen Sie aus, welche Feiertage eingetragen werden sollen. Vom Osterdatum abhängige Feiertage werden für die verschiedenen Jahre berechnet.:")
|
|
with ui.grid(columns='auto auto'):
|
|
with ui.column().classes('gap-0'): # Auswahlen für Feiertage
|
|
|
|
checkbox_classes = 'py-0'
|
|
|
|
new_year = ui.checkbox("Neujahr (1. Januar)").classes(checkbox_classes)
|
|
heilige_drei_koenige = ui.checkbox("Heilige Drei Könige (6. Januar)").classes(checkbox_classes)
|
|
womens_day = ui.checkbox("Internationaler Frauentag (8. März)").classes(checkbox_classes)
|
|
gruendonnerstag = ui.checkbox("Gründonnerstag (berechnet)").classes(checkbox_classes)
|
|
karfreitag = ui.checkbox("Karfreitag (berechnet").classes(checkbox_classes)
|
|
easter_sunday = ui.checkbox("Ostersonntag (berechnet)").classes(checkbox_classes)
|
|
easter_monday = ui.checkbox("Ostermontag (berechnet)").classes(checkbox_classes)
|
|
first_of_may = ui.checkbox("Tag der Arbeit (1. Mai)").classes(checkbox_classes)
|
|
liberation_day = ui.checkbox("Tag der Befreiung (8. Mai)").classes(checkbox_classes)
|
|
ascension_day = ui.checkbox("Christi Himmelfahrt (berechnet)").classes(checkbox_classes)
|
|
whitsun_sunday = ui.checkbox("Pfingssonntag (berechnet)").classes(checkbox_classes)
|
|
whitsun_monday = ui.checkbox("Pfingsmontag (berechnet)").classes(checkbox_classes)
|
|
fronleichnam = ui.checkbox("Fronleichnam (berechnet)").classes(checkbox_classes)
|
|
peace_party = ui.checkbox("Friedensfest (Augsburg - 8. August)").classes(checkbox_classes)
|
|
mary_ascension = ui.checkbox("Mariä Himmelfahrt (15. August)").classes(checkbox_classes)
|
|
childrens_day = ui.checkbox("Weltkindertag (20. September)").classes(checkbox_classes)
|
|
unity_day = ui.checkbox("Tag der deutschen Einheit (3. Oktober)").classes(checkbox_classes)
|
|
reformation_day = ui.checkbox("Reformationstag (30. Oktober)").classes(checkbox_classes)
|
|
all_hallows = ui.checkbox("Allerheiligen (1. November)").classes(checkbox_classes)
|
|
praying_day = ui.checkbox("Buß- und Bettag (berechnet)").classes(checkbox_classes)
|
|
christmas_day = ui.checkbox("1. Weihnachtsfeiertag (25. Dezember)").classes(checkbox_classes)
|
|
boxing_day = ui.checkbox("2. Weihnachtsfeiertag (26. Dezember)").classes(checkbox_classes)
|
|
|
|
def enter_holidays():
|
|
|
|
for year in range (int(starting_year.value), int(end_year.value) + 1):
|
|
ostersonntag = dateutil.easter.easter(year)
|
|
if new_year.value:
|
|
data["holidays"][f"{year}-01-01"] = f"Neujahr"
|
|
if heilige_drei_koenige.value:
|
|
data["holidays"][f"{year}-01-06"] = f"Hl. Drei Könige"
|
|
if womens_day.value:
|
|
data["holidays"][f"{year}-03-08"] = f"Intern. Frauentag"
|
|
if gruendonnerstag.value:
|
|
datum_dt = ostersonntag - datetime.timedelta(days=3)
|
|
datum = datum_dt.strftime("%Y-%m-%d")
|
|
data["holidays"][f"{datum}"] = f"Gründonnerstag"
|
|
if karfreitag.value:
|
|
datum_dt = ostersonntag - datetime.timedelta(days=2)
|
|
datum = datum_dt.strftime("%Y-%m-%d")
|
|
data["holidays"][f"{datum}"] = f"Karfreitag"
|
|
if easter_sunday.value:
|
|
datum_dt = ostersonntag
|
|
datum = datum_dt.strftime("%Y-%m-%d")
|
|
data["holidays"][f"{datum}"] = "Ostersonntag"
|
|
if easter_monday.value:
|
|
datum_dt = ostersonntag + datetime.timedelta(days=1)
|
|
datum = datum_dt.strftime("%Y-%m-%d")
|
|
data["holidays"][f"{datum}"] = "Ostermontag"
|
|
if first_of_may.value:
|
|
data["holidays"][f"{year}-05-01"] = f"Tag der Arbeit"
|
|
if liberation_day.value:
|
|
data["holidays"][f"{year}-05-08"] = f"Tag der Befreiung"
|
|
if ascension_day.value:
|
|
datum_dt = ostersonntag + datetime.timedelta(days=39)
|
|
datum = datum_dt.strftime("%Y-%m-%d")
|
|
data["holidays"][f"{datum}"] = f"Christi Himmelfahrt"
|
|
if whitsun_sunday.value:
|
|
datum_dt = ostersonntag + datetime.timedelta(days=49)
|
|
datum = datum_dt.strftime("%Y-%m-%d")
|
|
data["holidays"][f"{datum}"] = f"Pfingssonntag"
|
|
if whitsun_monday.value:
|
|
datum_dt = ostersonntag + datetime.timedelta(days=49)
|
|
datum = datum_dt.strftime("%Y-%m-%d")
|
|
data["holidays"][f"{datum}"] = f"Pfingstmontag"
|
|
if fronleichnam.value:
|
|
datum_dt = ostersonntag + datetime.timedelta(days=60)
|
|
datum = datum_dt.strftime("%Y-%m-%d")
|
|
data["holidays"][f"{datum}"] = f"Fronleichnam"
|
|
if peace_party.value:
|
|
data["holidays"][f"{year}-08-08"] = f"Friedensfest"
|
|
if mary_ascension.value:
|
|
data["holidays"][f"{year}-08-15"] = f"Mariä Himmelfahrt"
|
|
if childrens_day.value:
|
|
data["holidays"][f"{year}-09-20"] = f"Intern. Kindertag"
|
|
if unity_day.value:
|
|
data["holidays"][f"{year}-10-03"] = f"Tag der deutschen Einheit"
|
|
if reformation_day.value:
|
|
data["holidays"][f"{year}-10-30"] = f"Reformationstag"
|
|
if all_hallows.value:
|
|
data["holidays"][f"{year}-11-01"] = f"Allerheiligen"
|
|
if praying_day.value:
|
|
starting_day = datetime.datetime(year, 11 ,23)
|
|
for i in range(1, 8):
|
|
test_day = starting_day - datetime.timedelta(days=-i)
|
|
if test_day.weekday() == 2:
|
|
datum_dt = test_day
|
|
break
|
|
datum = datum_dt.strftime("%Y-%m-%d")
|
|
data["holidays"][f"{datum}"] = f"Bu0- und Bettag"
|
|
if christmas_day.value:
|
|
data["holidays"][f"{year}-12-25"] = f"1. Weihnachtsfeiertag"
|
|
if boxing_day.value:
|
|
data["holidays"][f"{year}-12-26"] = f"2. Weihnachtsfeiertag"
|
|
reset_visibility.value = True
|
|
dialog.close()
|
|
holiday_buttons_grid.refresh()
|
|
|
|
with ui.column():
|
|
starting_year = ui.number(value=datetime.datetime.now().year, label="Startjahr")
|
|
end_year = ui.number(value=starting_year.value, label="Endjahr")
|
|
with ui.row():
|
|
ui.button("Anwenden", on_click=enter_holidays)
|
|
ui.button("Abbrechen", on_click=dialog.close)
|
|
dialog.open()
|
|
|
|
def reset_holidays():
|
|
old_data = load_adminsettings()
|
|
data["holidays"] = old_data["holidays"]
|
|
reset_visibility.value = False
|
|
holiday_buttons_grid.refresh()
|
|
|
|
with ui.row():
|
|
ui.button("Gesetzliche Feiertage eintragen", on_click=defined_holidays).tooltip("Hier können Sie automatisiert gesetzliche Feiertage in Deutschland eintragen.")
|
|
ui.button("Eigener Eintrag", on_click=new_holiday_entry).tooltip("Hier können Sie einen eigenen Feiertag definieren.")
|
|
ui.button("Zurücksetzen", icon="undo", on_click=reset_holidays).bind_visibility_from(reset_visibility, 'value').classes('bg-red').tooltip("Hier können Sie ungespeicherte Änderungen zurücknehmen.")
|
|
|
|
ui.separator()
|
|
|
|
for year_entry in year_list:
|
|
with ui.expansion(year_entry):
|
|
with ui.column():
|
|
for entry in year_dict[year_entry]:
|
|
date_label = entry.strftime("%d.%m.%y")
|
|
with ui.button(on_click=lambda entry=entry: del_holiday_entry(entry)).classes('w-full').props('color=light-blue-8').tooltip(f"Klicken Sie hier, um den Feiertag \"{data['holidays'][entry.strftime('%Y-%m-%d')]}\" zu löschen."):
|
|
with ui.grid(columns="auto auto").classes('w-full'):
|
|
ui.label(f"{data['holidays'][entry.strftime('%Y-%m-%d')]}").props('align="left"')
|
|
ui.label(f"{date_label}").props('align="right"')
|
|
holiday_buttons_grid()
|
|
|
|
holiday_section()
|
|
|
|
ui.button("Speichern", on_click=save_admin_settings).tooltip("Hiermit werden sämtliche oben gemachten Einstellungen gespeichert.")
|
|
|
|
with ui.tab_panel(users):
|
|
ui.markdown("###Benutzerverwaltung")
|
|
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
|
|
|
|
api_key_input.value = current_user.api_key
|
|
|
|
api_link_column.clear()
|
|
for i in app.urls:
|
|
with ui.row() as link_row:
|
|
link_string = f'{i}/api/stamp/{api_key_input.value}'
|
|
link = ui.link(f'{i}/api/stamp/"API-Schlüssel"', link_string).tooltip(("ACHTUNG: Klick auf den Link löst Stempelaktion aus!"))
|
|
qr = segno.make_qr(link_string).svg_data_uri()
|
|
with ui.image(qr).classes('w-1/3'):
|
|
with ui.tooltip().classes('bg-white border'):
|
|
ui.image(qr).classes('w-64')
|
|
|
|
link_row.move(api_link_column)
|
|
|
|
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
|
|
|
|
user_photo.set_source(current_user.photofile)
|
|
user_photo.force_reload()
|
|
|
|
user_photo.set_visibility(os.path.exists(current_user.photofile))
|
|
delete_button.set_visibility(os.path.exists(current_user.photofile))
|
|
|
|
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 = hash_password(password_input.value)
|
|
current_user.api_key = api_key_input.value
|
|
current_user.write_settings()
|
|
password_input.value = ""
|
|
userlist = list_users()
|
|
userlist.sort()
|
|
user_selection.clear()
|
|
user_selection.set_options(userlist)
|
|
user_selection.value = current_user.username
|
|
user_selection_changed()
|
|
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():
|
|
if current_user.username == time_user.value:
|
|
if userlist.index(current_user.username) == 0:
|
|
time_user.value = userlist[1]
|
|
else:
|
|
time_user.value = userlist[0]
|
|
current_user.del_user()
|
|
#userlist = list_users()
|
|
#userlist.sort()
|
|
#user_selection.clear()
|
|
#user_selection.set_options(userlist)
|
|
#user_selection.value = userlist[0]
|
|
update_userlist()
|
|
time_user.set_options(userlist)
|
|
user_ui.refresh()
|
|
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:
|
|
construct_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()
|
|
|
|
def dialog_new_user():
|
|
def create_new_user():
|
|
if user_name_input.validate():
|
|
|
|
new_user(user_name_input.value)
|
|
update_userlist()
|
|
time_user.set_options(userlist)
|
|
user_ui.refresh()
|
|
dialog.close()
|
|
else:
|
|
ui.notify("Ungültiger Benutzername")
|
|
|
|
with ui.dialog() as dialog, ui.card():
|
|
ui.markdown("Geben Sie den Benutzernamen für das neue Konto an:")
|
|
user_name_input = ui.input(label="Benutzername", validation={'Leerer Benutzername nicht erlaubt': lambda value: len(value) != 0,
|
|
'Leerzeichen im Benutzername nicht erlaubt': lambda value: " " not in value,
|
|
'Benutzername schon vergeben': lambda value: value not in userlist}).on('keypress.enter', create_new_user)
|
|
with ui.row():
|
|
ui.button("OK", on_click=create_new_user)
|
|
ui.button("Abbrechen", on_click=dialog.close)
|
|
dialog.open()
|
|
|
|
@ui.refreshable
|
|
def user_ui():
|
|
userlist = list_users()
|
|
userlist.sort()
|
|
|
|
with ui.column():
|
|
global user_selection
|
|
user_selection = ui.select(options=userlist, with_input=True, on_change=user_selection_changed)
|
|
user_selection.value = userlist[0]
|
|
ui.button("Neu", on_click=dialog_new_user)
|
|
user_ui()
|
|
|
|
with ui.column():
|
|
@ui.refreshable
|
|
def usersettings_card():
|
|
global usersettingscard
|
|
with ui.card() as usersettingscard:
|
|
ui.markdown("**Benutzereinstellungen**")
|
|
with ui.grid(columns="auto 1fr") as usersettingsgrid:
|
|
|
|
ui.markdown("Benutzername:")
|
|
global username_input
|
|
username_input = ui.input()
|
|
ui.markdown("Voller Name:")
|
|
global fullname_input
|
|
fullname_input = ui.input()
|
|
ui.markdown("Passwort")
|
|
global password_input
|
|
password_input = ui.input(password=True)
|
|
password_input.value = ""
|
|
ui.markdown("API-Schlüssel:")
|
|
with ui.row():
|
|
global api_key_input
|
|
api_key_input = ui.input().props('size=37')
|
|
def new_api_key():
|
|
api_key_input.value = hashlib.shake_256(bytes(f'{username_input.value}_{datetime.datetime.now().timestamp()}', 'utf-8')).hexdigest(20)
|
|
|
|
ui.button("Neu", on_click=new_api_key).tooltip("Neuen API-Schlüssel erzeugen. Wird erst beim Klick auf Speichern übernommen und entsprechende Links und QR-Codes aktualisiert")
|
|
ui.markdown('Aufruf zum Stempeln:')
|
|
global api_link_column
|
|
with ui.column().classes('gap-0') as api_link_column:
|
|
global stamp_link
|
|
stamp_link = [ ]
|
|
for i in app.urls:
|
|
stamp_link.append(ui.link(f'{i}/api/stamp/"API-Schüssel"'))
|
|
|
|
|
|
with ui.grid(columns=2):
|
|
ui.button("Speichern", on_click=save_user_settings).tooltip("Klicken Sie hier um die Änderungen zu speichern.")
|
|
ui.button("Löschen", on_click=del_user)
|
|
|
|
usersettings_card()
|
|
|
|
with ui.card() as photocard:
|
|
ui.markdown('**Foto**')
|
|
current_user = user(user_selection.value)
|
|
user_photo = ui.image(current_user.photofile)
|
|
|
|
def handle_upload(e: events.UploadEventArguments):
|
|
picture = e.content.read()
|
|
current_user = user(user_selection.value)
|
|
with open(current_user.photofile, 'wb') as outoutfile:
|
|
outoutfile.write(picture)
|
|
uploader.reset()
|
|
user_selection_changed()
|
|
|
|
|
|
def del_photo():
|
|
current_user = user(user_selection.value)
|
|
current_user.delete_photo()
|
|
user_selection_changed()
|
|
|
|
uploader = ui.upload(label="Foto hochladen", on_upload=handle_upload).props('accept=.jpg|.jpeg').classes('max-w-full')
|
|
delete_button = ui.button("Löschen", on_click=del_photo)
|
|
|
|
|
|
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='auto auto auto'):
|
|
ui.markdown("gültig ab:")
|
|
workhours_select = ui.select(options=workhours, on_change=workhours_selection_changed).classes('col-span-2')
|
|
|
|
days = [ ]
|
|
weekdays = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"]
|
|
counter = 0
|
|
for day in weekdays:
|
|
ui.markdown(f"{day}:")
|
|
days.append(ui.number(on_change=calculate_weekhours).props('size=3'))
|
|
ui.markdown('Stunden')
|
|
counter = counter + 1
|
|
ui.separator().classes('col-span-full')
|
|
ui.markdown("**Summe:**")
|
|
workhours_sum = ui.markdown()
|
|
ui.markdown("Stunden")
|
|
|
|
with ui.card():
|
|
with ui.grid(columns='auto auto auto'):
|
|
ui.markdown("Urlaubstage")
|
|
vacation_input = ui.number().props('size=3')
|
|
ui.markdown("Tage")
|
|
|
|
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)
|
|
with ui.row():
|
|
ui.button("Speichern", on_click=save_workhours)
|
|
ui.button("Löschen", on_click=delete_workhour_entry)
|
|
user_selection_changed()
|
|
|
|
# Alternativ zur Loginseite navigieren
|
|
else:
|
|
login = login_mask()
|
|
#ui.navigate.to("/login") |