zeiterfassung/lib/homepage.py
Alexander Malzkuhn 9e8fa9ad62 Ordnerstruktur sortiert
imports angespasst
Stylinganpassungen im Adminbereich (Responsive, Farben für die Feiertagsbuttons)
2025-05-23 10:43:03 +02:00

239 lines
11 KiB
Python

# Zeiterfassung
import datetime
from nicegui import ui, app, Client
from nicegui.page import page
from lib.users import *
from lib.definitions import *
from calendar import monthrange, month_name
import hashlib
import calendar
import locale
from lib.web_ui import *
@ui.page('/')
def homepage():
ui.page_title(f'{app_title} {app_version}')
if login_is_valid():
try:
current_user = user(app.storage.user["active_user"])
except:
del(app.storage.user["active_user"])
ui.navigate.reload()
pageheader(f"Willkommen, {current_user.fullname}")
today = datetime.datetime.now()
def yesterdays_overtime():
last_months_overtime = current_user.get_last_months_overtime(today.year, today.month)
overtime_this_month = 0
for i in range(1, today.day):
overtime_this_month += (int(current_user.get_worked_time(today.year, today.month, i)[0]) - int(current_user.get_day_workhours(today.year, today.month, i)))
return last_months_overtime + overtime_this_month
@ui.refreshable
def stamp_interface():
time_so_far = current_user.get_worked_time(today.year, today.month, today.day)[0]
def stamp_and_refresh():
current_user.timestamp()
stamp_interface.refresh()
with ui.grid(columns='20% auto 20%').classes('w-full justify-center'):
ui.space()
def update_timer():
additional_time = 0
if time_toggle.value == "total":
additional_time = yesterdays_overtime()
if current_user.get_worked_time(today.year, today.month, today.day)[1] > 0:
time_in_total = additional_time + time_so_far + int((datetime.datetime.now().timestamp() - current_user.get_worked_time(today.year, today.month, today.day)[1]))
else:
time_in_total = additional_time + time_so_far
working_hours.set_content(convert_seconds_to_hours(time_in_total))
with ui.grid(columns='1fr 1fr'):
if current_user.stamp_status() == status_in:
bg_color = 'green'
else:
bg_color = 'red'
working_hours = ui.markdown(convert_seconds_to_hours(time_so_far)).classes(f'col-span-2 rounded-3xl text-center text-white text-bold text-2xl border-4 border-gray-600 bg-{bg_color}')
in_button = ui.button("Einstempeln", on_click=stamp_and_refresh).classes('bg-green')
out_button = ui.button("Ausstempeln", on_click=stamp_and_refresh).classes('bg-red')
time_toggle = ui.toggle({"day": "Tagesarbeitszeit", "total": "Gesamtzeit"}, value="day",
on_change=update_timer).classes('w-full justify-center col-span-2').tooltip("Hier lässt sich die Anzeige oben zwischen heute geleisteter Arbeitszeit und summierter Arbeitszeit umschalten.")
working_timer = ui.timer(1.0, update_timer)
working_timer.active = False
if current_user.stamp_status() == status_in:
in_button.set_enabled(False)
out_button.set_enabled(True)
working_timer.active = True
else:
in_button.set_enabled(True)
out_button.set_enabled(False)
working_timer.active = False
stamp_interface()
available_years = current_user.get_years()
available_months = [ ]
binder_month_button = ValueBinder()
binder_month_button.value = False
binder_available_years = ValueBinder()
binder_vacation = ValueBinder()
binder_vacation.value = False
binder_absence = ValueBinder()
binder_absence.value = False
def enable_month():
binder_month_button.value = True
def update_month():
month_dict = { }
for i in current_user.get_months(month_year_select.value):
month_dict[i] = month_name[i]
month_month_select.set_options(month_dict)
month_month_select.enable()
if load_adminsettings()["user_notes"]:
with ui.grid(columns='1fr auto 1fr').classes('w-full justify-center'):
ui.space()
with ui.expansion("Tagesnotizen", icon='o_description'):
with ui.grid(columns=2):
last_selection = 0
@ui.refreshable
def day_note_ui():
day_notes = { }
options = { }
options[0] = "Heute"
for i in range(1, monthrange(today.year, today.month)[1] + 1):
notes_of_i = current_user.get_day_notes(today.year, today.month, i)
if len(notes_of_i) > 0:
try:
day_notes[i] = notes_of_i["user"]
options[i] = f"{i}.{today.month}.{today.year}"
except KeyError:
pass
select_value = last_selection
try:
day_notes[today.day]
del(options[0])
select_value = today.day
except KeyError:
select_value = 0
day_selector = ui.select(options=options, value=select_value).classes('col-span-2')
#except ValueError:
# day_selector = ui.select(options=options, value=0).classes('col-span-2')
daynote = ui.textarea().classes('col-span-2')
try:
if last_selection == 0:
daynote.value = current_user.get_day_notes(today.year, today.month, today.day)["user"]
else:
daynote.value = day_notes[day_selector.value]
except:
daynote.value = ""
def call_note():
if day_selector.value == 0:
daynote.value = current_user.get_day_notes(today.year, today.month, today.day)["user"]
else:
daynote.value = day_notes[day_selector.value]
day_selector.on_value_change(call_note)
def save_note():
note_dict = { }
note_dict["user"] = daynote.value
nonlocal last_selection
last_selection = day_selector.value
print(f"Last selection from save: {last_selection}")
if day_selector.value == 0:
day_to_write = today.day
else:
day_to_write = day_selector.value
current_user.write_notes(today.year, today.month, day_to_write, note_dict)
day_note_ui.refresh()
save_button = ui.button("Speichern", on_click=save_note)
def del_text():
daynote.value = ""
delete_button = ui.button("Löschen", on_click=del_text)
notes = current_user.get_day_notes(today.year, today.month, today.day)
try:
daynote.value = notes[current_user.username]
except:
pass
day_note_ui()
ui.separator()
with ui.grid(columns='1fr auto 1fr').classes('w-full justify-center'):
ui.space()
def activate_vacation():
binder_vacation.value = True
def activate_absence():
binder_absence.value = True
with ui.grid(columns='1fr 1fr'):
ui.markdown("**Monatsübersicht:**").classes('col-span-2')
month_year_select = ui.select(list(reversed(available_years)), label="Jahr", on_change=update_month).bind_value_to(binder_available_years, 'value')
month_month_select = ui.select(available_months, label="Monat", on_change=enable_month)
month_month_select.disable()
ui.space()
month_button = ui.button("Anzeigen", on_click=lambda: ui.navigate.to(f"/api/month/{current_user.username}/{month_year_select.value}-{month_month_select.value}", new_tab=True)).bind_enabled_from(binder_month_button, 'value')
ui.markdown("**Urlaubsanspruch**").classes('col-span-2')
vacation_select = ui.select(list(reversed(available_years)), on_change=activate_vacation)
vacation_button = ui.button("Anzeigen", on_click=lambda: ui.navigate.to(f"/api/vacation/{current_user.username}/{vacation_select.value}", new_tab=True)).bind_enabled_from(binder_vacation, 'value')
ui.markdown("**Fehlzeitenübersicht**").classes('col-span-2')
absences_select = ui.select(list(reversed(available_years)), on_change=activate_absence)
absences_button = ui.button("Anzeigen", on_click=lambda: ui.navigate.to(f"api/absence/{current_user.username}/{absences_select.value}", new_tab=True)).bind_enabled_from(binder_absence, 'value')
ui.separator().classes('col-span-2')
def logout():
app.storage.user.pop("active_user", None)
ui.navigate.to("/")
ui.button("Logout", on_click=logout).classes('col-span-2')
ui.space()
else:
login_mask()
# 404 Fehlerseite
@app.exception_handler(404)
async def exception_handler_404(request, exception: Exception):
with Client(page(''), request=request) as client:
pageheader("Fehler 404")
ui.label("Diese Seite existiert nicht.")
ui.label("Was möchten Sie tun?")
with ui.list().props('dense'):
with ui.item():
ui.link("zur Startseite", "/")
with ui.item():
ui.link("zum Administratrionsbereich", "/admin")
return client.build_response(request, 404)