diff --git a/admin.py b/admin.py index 82a21f8..87c9b17 100644 --- a/admin.py +++ b/admin.py @@ -272,7 +272,7 @@ Dies kann nicht rückgägig gemacht werden!''') timestamps_of_this_day[i]) time_sum = time_sum + time_delta - ui.markdown(convert_seconds_to_hours(time_sum)) + ui.markdown(convert_seconds_to_hours(time_sum)).classes('text-right') else: ui.markdown("Kein") @@ -282,7 +282,7 @@ Dies kann nicht rückgägig gemacht werden!''') if hours_to_work < 0: ui.space() else: - ui.markdown(f"{convert_seconds_to_hours(int(hours_to_work) * 3600)}") + 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) @@ -307,9 +307,9 @@ Dies kann nicht rückgägig gemacht werden!''') pass general_saldo = general_saldo + saldo - ui.markdown(convert_seconds_to_hours(saldo)) + ui.markdown(convert_seconds_to_hours(saldo)).classes('text-right') else: - ui.markdown("-") + ui.markdown("-").classes('text-center') def add_entry(day): with ui.dialog() as add_dialog, ui.card(): @@ -425,18 +425,13 @@ Dies kann nicht rückgägig gemacht werden!''') #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") + ui.space().classes('col-span-4') + ui.markdown(f"{convert_seconds_to_hours(general_saldo)}").classes('text-right') + ui.markdown("Stunden aus Vormonat").classes('col-span-4 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)}") - for i in range(4): - ui.space() - ui.markdown("Gesamtsaldo") - ui.markdown(f"**{convert_seconds_to_hours(general_saldo + last_months_overtime)}**") + ui.markdown(f"{convert_seconds_to_hours(last_months_overtime)}").classes('text-right') + ui.markdown("Gesamtsaldo").classes('col-span-4 text-right') + ui.markdown(f"**{convert_seconds_to_hours(general_saldo + last_months_overtime)}**").classes('text-right') table_grid.move(calendar_card) update_month_and_year() diff --git a/api.py b/api.py index 88debe9..706ceb1 100644 --- a/api.py +++ b/api.py @@ -2,7 +2,9 @@ import sys from logging import exception from nicegui import * +from samba.graph import pad_char +import ui from definitions import * from web_ui import * from users import * @@ -22,179 +24,196 @@ def page(username: str, year: int, month: int): ui.space() ui.markdown(f'#Bericht für {current_user.fullname} für {calendar.month_name[month]} {year}') - columns = [ - {'name': 'date', 'label': 'Datum', 'field': 'date', 'required': True, 'align': 'left'}, - {'name:': 'bookings', 'label': 'Buchungen', 'field': 'bookings', 'align': 'left'}, - {'name:': 'is_time', 'label': 'Ist', 'field': 'is_time', 'align': 'left'}, - {'name:': 'target_time', 'label': 'Soll', 'field': 'target_time', 'align': 'left'}, - {'name:': 'total', 'label': 'Saldo', 'field': 'total', 'align': 'left'} - ] + pad_x = 4 + pad_y = 0 - rows = [ ] + color_weekend = "gray-100" + color_holiday = "gray-100" - # Timestamp in ein Array schreiben - timestamps = current_user.get_timestamps(year, month) - timestamps.sort() + def overview_table(): + # Timestamp in ein Array schreiben + timestamps = current_user.get_timestamps(year, month) + timestamps.sort() - # Abwesenheitsdaten in ein Dict schreiben - user_absent = current_user.get_absence(year, month) + # Abwesenheitsdaten in ein Dict schreiben + user_absent = current_user.get_absence(year, month) - # Dictionary für sortierte Timestamps - timestamps_dict = { } + # Dictionary für sortierte Timestamps + timestamps_dict = { } - # Dictionary mit zunächst leeren Tageinträgen befüllen - for day in range(1, monthrange(year, month)[1] + 1): - # Jeder Tag bekommt eine leere Liste - timestamps_dict[day] = [ ] + # Dictionary mit zunächst leeren Tageinträgen befüllen + for day in range(1, monthrange(year, month)[1] + 1): + # Jeder Tag bekommt eine leere Liste + timestamps_dict[day] = [ ] - # Timestamps den Monatstagen zuordnen - for stamp in timestamps: - day_of_month_of_timestamp = datetime.fromtimestamp(int(stamp)).day - timestamps_dict[day_of_month_of_timestamp].append(int(stamp)) - timestamps_dict[day_of_month_of_timestamp].append(int(stamp)) + # Timestamps den Monatstagen zuordnen + for stamp in timestamps: + day_of_month_of_timestamp = datetime.fromtimestamp(int(stamp)).day + timestamps_dict[day_of_month_of_timestamp].append(int(stamp)) + timestamps_dict[day_of_month_of_timestamp].append(int(stamp)) - general_saldo = 0 + general_saldo = 0 - # Gehe jeden einzelnen Tag des Dictionaries für die Timestamps durch - for day in list(timestamps_dict): - booking_text = "" - current_day_date = f"{datetime(year, month, day).strftime('%a')}, {day}.{month}.{year}" + with ui.grid(columns='auto auto 1fr 1fr 1fr').classes(f'gap-0 border px-0 py-0'): + ui.markdown("**Datum**").classes(f'border px-{pad_x} py-{pad_y}') + ui.markdown("**Buchungen**").classes(f'border px-{pad_x} py-{pad_y}') + ui.markdown("**Ist**").classes(f'border px-{pad_x} py-{pad_y}') + ui.markdown("**Soll**").classes(f'border px-{pad_x} py-{pad_y}') + ui.markdown("**Saldo**").classes(f'border px-{pad_x} py-{pad_y}') - # Abwesenheitseinträge - try: - # Abwesenheitszeiten behandeln - for i in list(user_absent): - if int(i) == day: - booking_text += absence_entries[user_absent[i]]["name"] + " " - except: - pass + # Gehe jeden einzelnen Tag des Dictionaries für die Timestamps durch + for day in list(timestamps_dict): + booking_text = "" + color_day = 'inherit' + if datetime(year, month, day).strftime('%w') in ["0", "6"]: + color_day = color_weekend - # Buchungen behandeln - for i in range(len(timestamps_dict[day])): - try: - temp_pair = [timestamps_dict[day][i], timestamps_dict[day][i + 1]] - booking_text = booking_text + str(datetime.fromtimestamp(temp_pair[0]).strftime('%H:%M')) + "-" + str(datetime.fromtimestamp(temp_pair[1]).strftime('%H:%M')) + "\n" + current_day_date = f"{datetime(year, month, day).strftime('%a')}, {day}.{month}.{year}" + day_text_element = ui.markdown(current_day_date).classes(f'border px-{pad_x} py-{pad_y} bg-{color_day}') - except: - if len(timestamps_dict[day]) % 2 != 0: - booking_text += datetime.fromtimestamp(int(timestamps_dict[day][i])).strftime('%H:%M') - # Ist-Zeiten berechnen - timestamps_of_this_day = [] - - # Suche mir alle timestamps für diesen Tag - for i in timestamps: - actual_timestamp = 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 - - is_time = convert_seconds_to_hours(time_sum) + " h" - else: - is_time = "Kein" - - # Sollzeit bestimmen - - hours_to_work = int(current_user.get_day_workhours(year, month, day)) - - if hours_to_work < 0: - target_time = "" - else: - target_time = f"{convert_seconds_to_hours(int(hours_to_work) * 3600)} h" - if int(hours_to_work) == 0: - booking_text = "Kein Arbeitstag" - - # Saldo für den Tag berechnen - day_in_list = datetime(year, month, day) - if time.time() > day_in_list.timestamp(): - - time_duty = int(current_user.get_day_workhours(year, month, day)) * 3600 - if time_duty < 0: - saldo = 0 - total = "" - booking_text = "Kein Arbeitsverhältnis" - else: - saldo = int(time_sum) - int(time_duty) - # Nach Abwesenheitseinträgen suchen + # Abwesenheitseinträge + booking_color = "inherit" + booking_text_color = "inherit" try: + # Abwesenheitszeiten behandeln for i in list(user_absent): - if int(i) == day and user_absent[i] != "UU": - saldo = 0 + if int(i) == day: + booking_text += absence_entries[user_absent[i]]["name"] + "
" + booking_color = absence_entries[user_absent[i]]["color"] + booking_text_color = absence_entries[user_absent[i]]["text-color"] except: pass - general_saldo = general_saldo + saldo - total = f"{convert_seconds_to_hours(saldo)} h" - else: - total = "-" + # Buchungen behandeln + for i in range(len(timestamps_dict[day])): + try: + temp_pair = [timestamps_dict[day][i], timestamps_dict[day][i + 1]] + booking_text = booking_text + str(datetime.fromtimestamp(temp_pair[0]).strftime('%H:%M')) + "-" + str(datetime.fromtimestamp(temp_pair[1]).strftime('%H:%M')) + "
" - rows.append({'date': current_day_date, 'bookings': booking_text, 'is_time': is_time, 'target_time': target_time, 'total': total}) + except: + if len(timestamps_dict[day]) % 2 != 0: + booking_text += datetime.fromtimestamp(int(timestamps_dict[day][i])).strftime('%H:%M') + print(booking_text) - overview_table = ui.table(columns=columns, rows=rows, row_key='date').classes('w-full') + booking_text_element = ui.markdown(booking_text).classes(f'border px-{pad_x} py-{pad_y} bg-{booking_color} text-{booking_text_color}') - # Zeilenumbruch umsetzen - overview_table.add_slot('body-cell', r''' - {{ props.value }} - ''') - # Überstundenzusammenfassung + # Ist-Zeiten berechnen + timestamps_of_this_day = [] - with ui.grid(columns=2).classes('w-full gap-0'): - ui.markdown("Überstunden aus Vormonat:") - last_months_overtime = current_user.get_last_months_overtime(year, month) - ui.markdown(f"{convert_seconds_to_hours(last_months_overtime)} h") - ui.markdown("Überstunden diesen Monat:") - ui.markdown(f"{convert_seconds_to_hours(general_saldo)} h") - ui.markdown("**Überstunden Gesamt:**") - overtime_overall = last_months_overtime + general_saldo - ui.markdown(f"**{convert_seconds_to_hours(overtime_overall)} h**") + # Suche mir alle timestamps für diesen Tag + for i in timestamps: + actual_timestamp = datetime.fromtimestamp(int(i)) + timestamp_day = actual_timestamp.strftime('%-d') - absences_this_month = current_user.get_absence(year, month) - absence_dict = { } + if int(timestamp_day) == int(day): + timestamps_of_this_day.append(i) - for abbr in list(absence_entries): - absence_dict[abbr] = 0 + timestamps_of_this_day.sort() + time_sum = 0 + if len(timestamps_of_this_day) > 1: - for key, value in absences_this_month.items(): - if value in list(absence_dict): - absence_dict[value] += 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 - total_absence_days = 0 - for key, value in absence_dict.items(): - total_absence_days += absence_dict[key] + is_time = convert_seconds_to_hours(time_sum) + " h" + else: + is_time = "Kein" - if total_absence_days > 0: - ui.markdown("###Abwesenheitstage diesen Monat:") - a_columns = [ - {'name': 'type', 'label': 'Typ', 'field': 'type', 'required': True, 'align': 'left'}, - {'name': 'sum', 'label': 'Tage', 'field': 'sum', 'required': True, 'align': 'left'}, - ] + ui.markdown(is_time).classes(f'border px-{pad_x} py-{pad_y} text-center') + # Sollzeit bestimmen - a_row = [ ] + hours_to_work = int(current_user.get_day_workhours(year, month, day)) - for key,value in absence_dict.items(): - if value > 0: - a_row.append({'type': absence_entries[key]['name'], 'sum': value}) + if hours_to_work < 0: + target_time = "" + else: + target_time = f"{convert_seconds_to_hours(int(hours_to_work) * 3600)} h" + if int(hours_to_work) == 0: + booking_text = "Kein Arbeitstag" + booking_text_element.set_content(booking_text) - absence_table = ui.table(columns=a_columns, rows=a_row) + ui.markdown(target_time).classes(f'border px-{pad_x} py-{pad_y} text-center') + # Saldo für den Tag berechnen + day_in_list = datetime(year, month, day) + if time.time() > day_in_list.timestamp(): + + time_duty = int(current_user.get_day_workhours(year, month, day)) * 3600 + if time_duty < 0: + saldo = 0 + total = "" + booking_text = "Kein Arbeitsverhältnis" + booking_text_element.set_content(booking_text) + 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 + total = f"{convert_seconds_to_hours(saldo)} h" + + else: + total = "-" + if total == "-": + total_class = 'text-center' + else: + total_class = 'text-right' + ui.markdown(total).classes(total_class).classes(f'border px-{pad_x} py-{pad_y}') + + # Überstundenzusammenfassung + ui.markdown("Überstunden aus Vormonat:").classes(f'col-span-4 text-right border px-{pad_x} py-{pad_y}') + last_months_overtime = current_user.get_last_months_overtime(year, month) + ui.markdown(f"{convert_seconds_to_hours(last_months_overtime)} h").classes(f'text-right border px-{pad_x} py-{pad_y}') + ui.markdown("Überstunden diesen Monat:").classes(f'col-span-4 text-right border px-{pad_x} py-{pad_y}') + ui.markdown(f"{convert_seconds_to_hours(general_saldo)} h").classes(f'text-right border px-{pad_x} py-{pad_y}') + ui.markdown("**Überstunden Gesamt:**").classes(f'col-span-4 text-right border px-{pad_x} py-{pad_y}') + overtime_overall = last_months_overtime + general_saldo + ui.markdown(f"**{convert_seconds_to_hours(overtime_overall)} h**").classes(f'text-right border px-{pad_x} py-{pad_y}') + + overview_table() + + def absence_table(): + absences_this_month = current_user.get_absence(year, month) + absence_dict = { } + + for abbr in list(absence_entries): + absence_dict[abbr] = 0 + + for key, value in absences_this_month.items(): + if value in list(absence_dict): + absence_dict[value] += 1 + + total_absence_days = 0 + for key, value in absence_dict.items(): + total_absence_days += absence_dict[key] + + if total_absence_days > 0: + ui.markdown("###Abwesenheitstage diesen Monat:") + + with ui.grid(columns='auto 20%').classes(f'gap-0 border px-0 py-0'): + + for key,value in absence_dict.items(): + if value > 0: + ui.markdown(absence_entries[key]['name']).classes(f"border px-{pad_x} py-{pad_y}") + ui.markdown(str(value)).classes(f'border px-{pad_x} py-{pad_y} text-center') + + absence_table() except Exception as e: print(str(type(e).__name__) + " " + str(e)) diff --git a/definitions.py b/definitions.py index 7cee06d..679c89b 100644 --- a/definitions.py +++ b/definitions.py @@ -23,14 +23,21 @@ status_out = "ausgestempelt" # Abesenheiten absence_entries = {"U": { "name": "Urlaub", - "color": "green"}, + "color": "green", + "text-color": "black"}, "K": { "name": "Krankheit", - "color": "red"}, + "color": "red", + "text-color": "white"}, "KK": { "name": "Krankheit Kind", - "color": "orange"}, + "color": "orange", + "text-color": "black"}, "UU": { "name": "Urlaub aus Überstunden", - "color": "green"}, + "color": "green", + "text-color": "black"}, "F": { "name": "Fortbildung", - "color": "black"}, + "color": "black", + "text-color": "white"}, "EZ": { "name": "Elternzeit", - "color": "purple"}} + "color": "purple", + "text-color": "white"} + } diff --git a/playgound.py b/playgound.py index 092d37e..6428d72 100644 --- a/playgound.py +++ b/playgound.py @@ -1,16 +1,8 @@ from nicegui import ui -columns=[ - {'name': 'name', 'label': 'Name', 'field': 'name', 'align': 'left', 'style': 'text-wrap: wrap'}, - {'name': 'age', 'label': 'Age', 'field': 'age'}, - ] -rows=[ - {'name': 'Alice', 'age': 18}, - {'name': 'Bob', 'age': 21}, - {'name': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec purus nec nunc ultricies'}, - ] - -ui.table(columns=columns, rows=rows).classes('w-80') +test = ('Eintrag 1') +ui.markdown(test) +ui.markdown('Nächstes Element') ui.run(language="de-DE", port=9000) \ No newline at end of file diff --git a/users.py b/users.py index 8dae9f2..0d5ef84 100644 --- a/users.py +++ b/users.py @@ -288,6 +288,24 @@ class user: hours_to_work = -1 return hours_to_work + def get_vacation_claim(self, year, month, day): + workhour_entries = list(self.workhours) + workhour_entries.sort() + day_to_check = datetime.datetime(int(year), int(month), int(day)) + + claim = -1 + + 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: + claim = self.workhours[entry]["vacation"] + break + + return claim + def delete_photo(self): os.remove(self.photofile) diff --git a/users/testuser1/2025-4.json b/users/testuser1/2025-4.json index 0eb538b..1346aed 100644 --- a/users/testuser1/2025-4.json +++ b/users/testuser1/2025-4.json @@ -3,8 +3,10 @@ "overtime": 0, "absence": { "7": "U", - "8": "U", - "9": "U", - "10": "U" + "8": "K", + "9": "KK", + "10": "UU", + "11": "F", + "14": "EZ" } } \ No newline at end of file