Merge branch 'master' into docker
This commit is contained in:
commit
449a3a578b
193
lib/admin.py
193
lib/admin.py
@ -78,7 +78,7 @@ def page_admin():
|
||||
|
||||
with ui.tab_panels(overview_tabs, value = user_month_overview):
|
||||
with ui.tab_panel(user_month_overview).classes('w-full'):
|
||||
ui.markdown("##Übersichten")
|
||||
ui.label("Übersichten").classes(h3)
|
||||
|
||||
# Tabelle konstruieren
|
||||
with ui.card().classes('w-full'):
|
||||
@ -119,7 +119,7 @@ def page_admin():
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
ui.markdown("Benutzer:")
|
||||
ui.label("Benutzer:")
|
||||
|
||||
time_user = ui.select(options=userlist, on_change=update_user)
|
||||
time_user.value = userlist[0]
|
||||
@ -159,7 +159,7 @@ def page_admin():
|
||||
#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:
|
||||
with ui.grid(columns='auto auto auto 1fr 1fr 1fr 1fr').classes('w-full md:min-w-[600px] lg:min-w-[800px] items-baseline') as table_grid:
|
||||
if int(select_month.value) > 1:
|
||||
archive_status = current_user.get_archive_status(int(select_year.value),
|
||||
int(select_month.value))
|
||||
@ -199,12 +199,12 @@ def page_admin():
|
||||
else:
|
||||
calendar_card.classes('bg-white')
|
||||
# Überschriften
|
||||
ui.markdown("**Datum**")
|
||||
ui.markdown("**Buchungen**")
|
||||
ui.label("Datum").classes('font-bold')
|
||||
ui.label("Buchungen").classes('font-bold')
|
||||
ui.space()
|
||||
ui.markdown("**Ist**")
|
||||
ui.markdown("**Soll**")
|
||||
ui.markdown("**Saldo**")
|
||||
ui.label("Ist").classes('font-bold')
|
||||
ui.label("Soll").classes('font-bold')
|
||||
ui.label("Saldo").classes('font-bold')
|
||||
ui.space()
|
||||
|
||||
timestamps = current_user.get_timestamps(year=select_year.value, month=select_month.value)
|
||||
@ -229,7 +229,7 @@ def page_admin():
|
||||
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)
|
||||
ui.label(f"{day_in_list.strftime('%a')}., {day}. {calendar.month_name[int(select_month.value)]}").classes(class_content)
|
||||
|
||||
# Buchungen
|
||||
|
||||
@ -242,9 +242,8 @@ def page_admin():
|
||||
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!''')
|
||||
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?')
|
||||
ui.label('Dies kann nicht rückgängig gemacht werden!')
|
||||
with ui.grid(columns=3):
|
||||
ui.button("Ja", on_click=execute_deletion)
|
||||
ui.space()
|
||||
@ -269,7 +268,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
except:
|
||||
pass
|
||||
|
||||
day_type = ui.markdown("Kein Arbeitstag")
|
||||
day_type = ui.label("Kein Arbeitstag")
|
||||
day_type.set_visibility(False)
|
||||
|
||||
# Hier werden nur die Tage mit Timestamps behandelt
|
||||
@ -279,7 +278,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
def edit_entry(t_stamp, day):
|
||||
|
||||
with ui.dialog() as edit_dialog, ui.card():
|
||||
ui.markdown("**Eintrag bearbeiten**")
|
||||
ui.label("Eintrag bearbeiten").classes(h4)
|
||||
timestamp = datetime.datetime.fromtimestamp(int(t_stamp))
|
||||
input_time = ui.time().props('format24h now-btn').classes('w-full justify-center')
|
||||
|
||||
@ -355,14 +354,14 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
if days_notes != { }:
|
||||
with ui.icon('o_description').classes('text-2xl'):
|
||||
with ui.tooltip():
|
||||
with ui.grid(columns='auto auto'):
|
||||
with ui.grid(columns='auto auto').classes('items-center'):
|
||||
for username, text in days_notes.items():
|
||||
admins_name = load_adminsettings()["admin_user"]
|
||||
if username == admins_name:
|
||||
ui.markdown('Administrator:')
|
||||
ui.label('Administrator:')
|
||||
else:
|
||||
ui.markdown(current_user.fullname)
|
||||
ui.markdown(text)
|
||||
ui.label(current_user.fullname)
|
||||
ui.label(text)
|
||||
else:
|
||||
ui.space()
|
||||
|
||||
@ -396,25 +395,27 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
timestamps_of_this_day[i])
|
||||
time_sum = time_sum + time_delta
|
||||
|
||||
ui.markdown(convert_seconds_to_hours(time_sum)).classes('text-right')
|
||||
ui.label(convert_seconds_to_hours(time_sum)).classes('text-right')
|
||||
else:
|
||||
ui.markdown("Kein")
|
||||
ui.label("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.text="Kein Arbeitsverhältnis"
|
||||
day_type.set_visibility(True)
|
||||
else:
|
||||
ui.markdown(f"{convert_seconds_to_hours(int(hours_to_work) * 3600)}").classes('text-right')
|
||||
ui.label(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.text = "Kein Arbeitstag"
|
||||
day_type.classes('text-bold')
|
||||
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")]}**'
|
||||
day_type.text = f'{data["holidays"][day_in_list.strftime("%Y-%m-%d")]}'
|
||||
day_type.classes('text-bold')
|
||||
|
||||
# Saldo für den Tag berechnen
|
||||
|
||||
@ -434,13 +435,13 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
pass
|
||||
|
||||
general_saldo = general_saldo + saldo
|
||||
ui.markdown(convert_seconds_to_hours(saldo)).classes('text-right')
|
||||
ui.label(convert_seconds_to_hours(saldo)).classes('text-right')
|
||||
else:
|
||||
ui.markdown("-").classes('text-center')
|
||||
ui.label("-").classes('text-center')
|
||||
|
||||
def add_entry(day):
|
||||
with ui.dialog() as add_dialog, ui.card():
|
||||
ui.markdown("###Eintrag hinzufügen")
|
||||
ui.label("Eintrag hinzufügen").classes(h4)
|
||||
input_time = ui.time().classes('w-full justify-center')
|
||||
|
||||
def add_entry_save():
|
||||
@ -563,19 +564,19 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
note_labels = { }
|
||||
del_buttons = { }
|
||||
|
||||
ui.markdown(f'**Notizen für {day}.{current_month}.{current_year}**')
|
||||
with ui.grid(columns='auto auto auto'):
|
||||
ui.label(f'Notizen für {day}.{current_month}.{current_year}').classes('font-bold')
|
||||
with ui.grid(columns='auto auto auto').classes('items-baseline'):
|
||||
admin_settings = load_adminsettings()
|
||||
# Beschreibungsfeld für Admin
|
||||
username_labels["admin"] = ui.markdown("Administrator:")
|
||||
username_labels["admin"] = ui.label("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)
|
||||
username_labels["user"] = ui.label(current_user.fullname)
|
||||
note_labels["user"] = ui.label(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
|
||||
@ -612,12 +613,12 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
|
||||
#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')
|
||||
ui.label(convert_seconds_to_hours(general_saldo)).classes('text-right')
|
||||
ui.label("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')
|
||||
ui.label(convert_seconds_to_hours(last_months_overtime)).classes('text-right')
|
||||
ui.label("Gesamtsaldo").classes('col-span-5 text-right')
|
||||
ui.label(convert_seconds_to_hours(general_saldo + last_months_overtime)).classes('text-right text-bold text-underline')
|
||||
|
||||
table_grid.move(calendar_card)
|
||||
|
||||
@ -744,20 +745,20 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
with ui.tab_panel(settings):
|
||||
with ui.grid(columns='auto auto'):
|
||||
with ui.card():
|
||||
ui.markdown("**Administrationsbenutzer:**")
|
||||
with ui.grid(columns=2):
|
||||
ui.label("Administrationsbenutzer:").classes('text-bold')
|
||||
with ui.grid(columns=2).classes('items-baseline'):
|
||||
|
||||
ui.markdown("Benutzername des Adminstrators")
|
||||
ui.label("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")
|
||||
ui.label("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):
|
||||
ui.label("Systemeinstellungen:").classes('text-bold')
|
||||
with ui.grid(columns=2).classes('items-baseline'):
|
||||
def check_is_number(number):
|
||||
try:
|
||||
number = int(number)
|
||||
@ -765,7 +766,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
except:
|
||||
return False
|
||||
|
||||
ui.markdown("Port:")
|
||||
ui.label("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').bind_enabled_from(enabled_because_not_docker, 'value')
|
||||
if is_docker():
|
||||
@ -774,8 +775,8 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
port.value = old_port
|
||||
|
||||
with ui.card():
|
||||
ui.markdown("**Einstellungen für das Touchscreenterminal:**")
|
||||
with ui.column():
|
||||
ui.label("Einstellungen für das Touchscreenterminal:").classes('text-bold')
|
||||
with ui.column().classes('items-baseline'):
|
||||
touchscreen_switch = ui.switch("Touchscreenterminal aktiviert")
|
||||
touchscreen_switch.value = data["touchscreen"]
|
||||
timestamp_switch = ui.switch("Stempelzeiten anzeigen").bind_visibility_from(touchscreen_switch, 'value')
|
||||
@ -784,13 +785,13 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
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")
|
||||
ui.label("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')
|
||||
ui.label('px')
|
||||
with ui.row().bind_visibility_from(touchscreen_switch, 'value'):
|
||||
ui.markdown("Minimale Buttonhöhe")
|
||||
ui.label("Minimale Buttonhöhe:")
|
||||
def compare_button_height(height):
|
||||
if not photo_switch.value:
|
||||
return True
|
||||
@ -805,16 +806,16 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
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')
|
||||
ui.label('px')
|
||||
|
||||
with ui.card():
|
||||
ui.markdown("**Einstellungen für Benutzerfrontend**")
|
||||
ui.label("Einstellungen für Benutzerfrontend").classes('text-bold')
|
||||
notes_switch = ui.switch("Notizfunktion aktiviert", value=data["user_notes"])
|
||||
va_switch = ui.switch("Urlaubsanträge", value=data["vacation_application"])
|
||||
reset_visibility = ValueBinder()
|
||||
def holiday_section():
|
||||
with ui.card():
|
||||
ui.markdown('**Feiertage:**')
|
||||
ui.label('Feiertage:').classes('text-bold')
|
||||
|
||||
|
||||
reset_visibility.value = False
|
||||
@ -834,7 +835,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
|
||||
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')
|
||||
ui.label('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')
|
||||
@ -878,7 +879,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
|
||||
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.:")
|
||||
ui.label("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
|
||||
|
||||
@ -1035,7 +1036,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
|
||||
if int(old_port) != int(port.value):
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
ui.markdown(
|
||||
ui.label(
|
||||
"Damit die Porteinstellungen wirksam werden, muss der Server neu gestartet werden.")
|
||||
ui.button("OK", on_click=lambda: dialog.close())
|
||||
dialog.open()
|
||||
@ -1046,7 +1047,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
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")
|
||||
ui.label("Benutzerverwaltung").classes(h3)
|
||||
workhours = [ ]
|
||||
|
||||
with ui.row():
|
||||
@ -1126,9 +1127,9 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
|
||||
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?")
|
||||
ui.label("Benutzername wurde geändert.").classes('text-bold')
|
||||
ui.label(f"Benutzerdaten werden in den neuen Ordner {username_input.value} verschoben.")
|
||||
ui.label("Sollen die Einstellungen gespeichert werden?")
|
||||
with ui.row():
|
||||
ui.button("Speichern", on_click=save_settings)
|
||||
ui.button("Abbrechen", on_click=dialog.close)
|
||||
@ -1156,8 +1157,8 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
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?**")
|
||||
ui.label(f"Soll der Benutzer *{current_user.username}* gelöscht werden?")
|
||||
ui.label("Dies kann nicht rückgängig gemacht werden?").classes('text-bold')
|
||||
with ui.row():
|
||||
ui.button("Löschen", on_click=del_definitely)
|
||||
ui.button("Abbrechen", on_click=dialog.close)
|
||||
@ -1181,7 +1182,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
ui.notify("Einstellungen gespeichert")
|
||||
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
ui.markdown("Sollen die Änderungen an den Arbeitsstunden und/oder Urlaubstagen gespeichert werden?")
|
||||
ui.label("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)
|
||||
@ -1204,8 +1205,8 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
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.**")
|
||||
ui.label(f"Soll der Eintrag *{workhours_select.value}* wirklich gelöscht werden?")
|
||||
ui.label("Dies kann nicht rückgängig gemacht werden.").classes('text-bold')
|
||||
with ui.row():
|
||||
ui.button("Löschen", on_click=delete_entry)
|
||||
ui.button("Abbrechen", on_click=dialog.close)
|
||||
@ -1217,7 +1218,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
dialog.close()
|
||||
keyboard = ui.keyboard(on_key=handle_key)
|
||||
else:
|
||||
ui.markdown("Es gibt nur einen Eintrag. Dieser kann nicht gelöscht werden.")
|
||||
ui.label("Es gibt nur einen Eintrag. Dieser kann nicht gelöscht werden.")
|
||||
ui.button("OK", on_click=dialog.close)
|
||||
dialog.open()
|
||||
|
||||
@ -1234,7 +1235,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
ui.notify("Ungültiger Benutzername")
|
||||
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
ui.markdown("Geben Sie den Benutzernamen für das neue Konto an:")
|
||||
ui.label("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)
|
||||
@ -1260,20 +1261,20 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
def usersettings_card():
|
||||
global usersettingscard
|
||||
with ui.card() as usersettingscard:
|
||||
ui.markdown("**Benutzereinstellungen**")
|
||||
with ui.grid(columns="auto 1fr") as usersettingsgrid:
|
||||
ui.label("Benutzereinstellungen").classes('text-bold')
|
||||
with ui.grid(columns="auto 1fr").classes('items-baseline') as usersettingsgrid:
|
||||
|
||||
ui.markdown("Benutzername:")
|
||||
ui.label("Benutzername:")
|
||||
global username_input
|
||||
username_input = ui.input()
|
||||
ui.markdown("Voller Name:")
|
||||
ui.label("Voller Name:")
|
||||
global fullname_input
|
||||
fullname_input = ui.input()
|
||||
ui.markdown("Passwort")
|
||||
ui.label("Passwort:")
|
||||
global password_input
|
||||
password_input = ui.input(password=True)
|
||||
password_input.value = ""
|
||||
ui.markdown("API-Schlüssel:")
|
||||
ui.label("API-Schlüssel:")
|
||||
with ui.row():
|
||||
global api_key_input
|
||||
api_key_input = ui.input().props('size=37')
|
||||
@ -1281,7 +1282,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
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:')
|
||||
ui.label('Aufruf zum Stempeln:')
|
||||
global api_link_column
|
||||
with ui.column().classes('gap-0') as api_link_column:
|
||||
global stamp_link
|
||||
@ -1297,7 +1298,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
usersettings_card()
|
||||
|
||||
with ui.card() as photocard:
|
||||
ui.markdown('**Foto**')
|
||||
ui.label('Foto').classes('text-bold')
|
||||
current_user = user(user_selection.value)
|
||||
user_photo = ui.image(current_user.photofile)
|
||||
|
||||
@ -1321,7 +1322,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
|
||||
with ui.card() as workinghourscard:
|
||||
workhours = []
|
||||
ui.markdown("**Arbeitszeiten**")
|
||||
ui.label("Arbeitszeiten").classes('text-bold')
|
||||
|
||||
with ui.card():
|
||||
|
||||
@ -1332,30 +1333,30 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
sum = float(days[i].value) + sum
|
||||
except:
|
||||
pass
|
||||
workhours_sum.set_content(str(sum))
|
||||
workhours_sum.value = str(sum)
|
||||
|
||||
with ui.grid(columns='auto auto auto'):
|
||||
ui.markdown("gültig ab:")
|
||||
with ui.grid(columns='auto auto auto').classes('items-baseline'):
|
||||
ui.label("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}:")
|
||||
ui.label(f"{day}:")
|
||||
days.append(ui.number(on_change=calculate_weekhours).props('size=3'))
|
||||
ui.markdown('Stunden')
|
||||
ui.label('Stunden')
|
||||
counter = counter + 1
|
||||
ui.separator().classes('col-span-full')
|
||||
ui.markdown("**Summe:**")
|
||||
workhours_sum = ui.markdown()
|
||||
ui.markdown("Stunden")
|
||||
ui.label("Summe:").classes('text-bold')
|
||||
workhours_sum = ui.label()
|
||||
ui.label("Stunden")
|
||||
|
||||
with ui.card():
|
||||
with ui.grid(columns='auto auto auto'):
|
||||
ui.markdown("Urlaubstage")
|
||||
with ui.grid(columns='auto auto auto').classes('items-baseline'):
|
||||
ui.label("Urlaubstage")
|
||||
vacation_input = ui.number().props('size=3')
|
||||
ui.markdown("Tage")
|
||||
ui.label("Tage")
|
||||
|
||||
def new_workhours_entry():
|
||||
current_user = user(user_selection.value)
|
||||
@ -1378,7 +1379,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
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.")
|
||||
ui.label("Geben Sie das Gültigkeitsdatum an, ab wann die Einträge gültig sein sollen.")
|
||||
date_picker = ui.date()
|
||||
|
||||
with ui.row():
|
||||
@ -1403,8 +1404,8 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
api_key = ""
|
||||
|
||||
ui.label("Backupeinstellungen").classes('font-bold')
|
||||
with ui.grid(columns='auto auto auto'):
|
||||
ui.markdown("Backupordner:")
|
||||
with ui.grid(columns='auto auto auto').classes('items-baseline'):
|
||||
ui.label("Backupordner:")
|
||||
backupfolder_input = ui.input(value=backupfolder).props(f"size={len(backupfolder)}").bind_enabled_from(enabled_because_not_docker, 'value')
|
||||
def save_new_folder_name():
|
||||
if os.path.exists(backupfolder_input.value):
|
||||
@ -1421,7 +1422,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
if is_docker():
|
||||
save_backup_folder_button.tooltip("Diese Einstellung ist beim Einsatz von Docker deaktiviert.")
|
||||
|
||||
ui.markdown("API-Schlüssel:")
|
||||
ui.label("API-Schlüssel:")
|
||||
backup_api_key_input = ui.input(value=api_key).tooltip("Hier den API-Schlüssel eintragen, der für Backuperzeugung mittels API-Aufruf verwendet werden soll.")
|
||||
def new_backup_api_key():
|
||||
backup_api_key_input.value = hashlib.shake_256(bytes(f"{backupfolder}-{datetime.datetime.now().timestamp()}", 'utf-8')).hexdigest(20)
|
||||
@ -1435,7 +1436,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
|
||||
ui.separator()
|
||||
|
||||
ui.markdown('**Backups**')
|
||||
ui.label('Backups').classes('text-bold')
|
||||
date_format = '%Y-%m-%d_%H-%M'
|
||||
searchpath = backupfolder
|
||||
|
||||
@ -1447,7 +1448,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
|
||||
backup_files = []
|
||||
file_info = []
|
||||
with ui.grid(columns='auto auto auto auto auto auto'):
|
||||
with ui.grid(columns='auto auto auto auto auto auto').classes('items-baseline'):
|
||||
|
||||
ui.label("Backupzeitpunkt/Dateiname")
|
||||
ui.label("Backupgröße")
|
||||
@ -1478,12 +1479,12 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
button_string = date_string_dt.strftime('%d.%m.%Y - %H:%M')
|
||||
except ValueError:
|
||||
button_string = date_string
|
||||
ui.markdown(button_string)
|
||||
ui.label(button_string)
|
||||
if size > 1_000_000:
|
||||
ui.markdown(f'{round(size/1_000_000,2)} MB')
|
||||
ui.label(f'{round(size/1_000_000,2)} MB')
|
||||
else:
|
||||
ui.markdown(f'{round(size / 1_000, 2)} kB')
|
||||
ui.markdown(version)
|
||||
ui.label(f'{round(size / 1_000, 2)} kB')
|
||||
ui.label(version)
|
||||
ui.button(icon='download', on_click=lambda file=date_string: ui.download.file(
|
||||
os.path.join(scriptpath, backupfolder, f'{file}.zip'))).tooltip(
|
||||
"Backup herunterladen")
|
||||
|
130
lib/api.py
130
lib/api.py
@ -38,10 +38,10 @@ def page_overview_month(username: str, year: int, month: int):
|
||||
else:
|
||||
with ui.column().classes('w-full items-end gap-0'):
|
||||
ui.label(f"Bericht erstellt am {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}")
|
||||
ui.markdown(f'#Bericht für {current_user.fullname} für {calendar.month_name[month]} {year}')
|
||||
ui.label(f'Bericht für {current_user.fullname} für {calendar.month_name[month]} {year}').classes(h1)
|
||||
|
||||
pad_x = 4
|
||||
pad_y = 0
|
||||
pad_y = 2
|
||||
|
||||
color_weekend = "gray-100"
|
||||
color_holiday = "gray-100"
|
||||
@ -73,12 +73,12 @@ def page_overview_month(username: str, year: int, month: int):
|
||||
if current_user.get_archive_status(year, month):
|
||||
bg_color = ' bg-yellow-100'
|
||||
|
||||
with ui.grid(columns='auto auto 1fr 1fr 1fr').classes(f'gap-0 border px-0 py-0{bg_color}'):
|
||||
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}')
|
||||
with ui.grid(columns='auto auto 1fr 1fr 1fr').classes(f'gap-0 border px-0 py-0 {bg_color}'):
|
||||
ui.label("Datum").classes(f'border px-{pad_x} py-{pad_y} text-bold')
|
||||
ui.label("Buchungen").classes(f'border px-{pad_x} py-{pad_y} text-bold')
|
||||
ui.label("Ist").classes(f'border px-{pad_x} py-{pad_y} text-bold')
|
||||
ui.label("Soll").classes(f'border px-{pad_x} py-{pad_y} text-bold')
|
||||
ui.label("Saldo").classes(f'border px-{pad_x} py-{pad_y} text-bold')
|
||||
|
||||
# Gehe jeden einzelnen Tag des Dictionaries für die Timestamps durch
|
||||
for day in list(timestamps_dict):
|
||||
@ -89,18 +89,21 @@ def page_overview_month(username: str, year: int, month: int):
|
||||
|
||||
current_day_date = f"{datetime(year, month, day).strftime('%a')}, {day}.{month}.{year}"
|
||||
with ui.link_target(day).classes(f'border px-{pad_x} py-{pad_y} bg-{color_day}'):
|
||||
ui.markdown(current_day_date)
|
||||
ui.label(current_day_date)
|
||||
|
||||
# Abwesenheitseinträge
|
||||
booking_color = "inherit"
|
||||
booking_text_color = "inherit"
|
||||
bold = ''
|
||||
|
||||
try:
|
||||
# Abwesenheitszeiten behandeln
|
||||
for i in list(user_absent):
|
||||
if int(i) == day:
|
||||
booking_text += absence_entries[user_absent[i]]["name"] + "<br>"
|
||||
booking_text += absence_entries[user_absent[i]]["name"] + "\n"
|
||||
booking_color = absence_entries[user_absent[i]]["color"]
|
||||
booking_text_color = absence_entries[user_absent[i]]["text-color"]
|
||||
bold = 'text-bold'
|
||||
except:
|
||||
pass
|
||||
|
||||
@ -108,29 +111,30 @@ def page_overview_month(username: str, year: int, month: int):
|
||||
for i in range(0, len(timestamps_dict[day]), 2):
|
||||
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')) + "<br>"
|
||||
booking_text = booking_text + str(datetime.fromtimestamp(temp_pair[0]).strftime('%H:%M')) + " - " + str(datetime.fromtimestamp(temp_pair[1]).strftime('%H:%M')) + "\n"
|
||||
|
||||
except:
|
||||
if len(timestamps_dict[day]) % 2 != 0:
|
||||
booking_text += datetime.fromtimestamp(int(timestamps_dict[day][i])).strftime('%H:%M') + " - ***Buchung fehlt!***"
|
||||
booking_text += datetime.fromtimestamp(int(timestamps_dict[day][i])).strftime('%H:%M') + " - Buchung fehlt!"
|
||||
|
||||
day_notes = current_user.get_day_notes(year, month, day)
|
||||
just_once = True
|
||||
|
||||
with ui.column().classes(f'border px-{pad_x} py-{pad_y} bg-{booking_color} text-{booking_text_color}'):
|
||||
booking_text_element = ui.markdown(booking_text)
|
||||
booking_text_element = ui.label(booking_text).style('white-space: pre-wrap').classes(bold)
|
||||
if len(day_notes) > 0:
|
||||
if len(timestamps_dict[day]) > 0 or day in list(map(int, list(user_absent))):
|
||||
ui.separator()
|
||||
for user_key, notes in day_notes.items():
|
||||
if user_key == "admin":
|
||||
ui.markdown(f"Administrator:<br>{notes}")
|
||||
ui.label(f"Administrator:\n{notes}").style('white-space: pre-wrap')
|
||||
else:
|
||||
with ui.element():
|
||||
ui.markdown(f"{current_user.fullname}:<br>{notes}")
|
||||
ui.label(f"{current_user.fullname}:\n{notes}").style('white-space: pre-wrap')
|
||||
if len(day_notes) > 1 and just_once:
|
||||
ui.separator()
|
||||
just_once = False
|
||||
|
||||
# Ist-Zeiten berechnen
|
||||
timestamps_of_this_day = []
|
||||
|
||||
@ -163,11 +167,10 @@ def page_overview_month(username: str, year: int, month: int):
|
||||
else:
|
||||
is_time = "Kein"
|
||||
|
||||
ui.markdown(is_time).classes(f'border px-{pad_x} py-{pad_y} text-center')
|
||||
ui.label(is_time).classes(f'border px-{pad_x} py-{pad_y} text-center')
|
||||
# Sollzeit bestimmen
|
||||
|
||||
hours_to_work = int(current_user.get_day_workhours(year, month, day))
|
||||
|
||||
if hours_to_work < 0:
|
||||
target_time = ""
|
||||
else:
|
||||
@ -176,10 +179,11 @@ def page_overview_month(username: str, year: int, month: int):
|
||||
booking_text = "Kein Arbeitstag"
|
||||
date_dt = datetime(year, month, day)
|
||||
if date_dt.strftime("%Y-%m-%d") in data["holidays"]:
|
||||
booking_text = f'**{data["holidays"][date_dt.strftime("%Y-%m-%d")]}**'
|
||||
booking_text_element.set_content(booking_text)
|
||||
booking_text = f'{data["holidays"][date_dt.strftime("%Y-%m-%d")]}'
|
||||
booking_text_element.classes('text-bold')
|
||||
booking_text_element.text = booking_text
|
||||
|
||||
ui.markdown(target_time).classes(f'border px-{pad_x} py-{pad_y} text-center')
|
||||
ui.label(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)
|
||||
@ -190,7 +194,7 @@ def page_overview_month(username: str, year: int, month: int):
|
||||
saldo = 0
|
||||
total = ""
|
||||
booking_text = "Kein Arbeitsverhältnis"
|
||||
booking_text_element.set_content(booking_text)
|
||||
booking_text_element.value = booking_text
|
||||
else:
|
||||
saldo = int(time_sum) - int(time_duty)
|
||||
# Nach Abwesenheitseinträgen suchen
|
||||
@ -210,18 +214,18 @@ def page_overview_month(username: str, year: int, month: int):
|
||||
total_class = 'text-center'
|
||||
else:
|
||||
total_class = 'text-right'
|
||||
ui.markdown(total).classes(total_class).classes(f'border px-{pad_x} py-{pad_y}')
|
||||
ui.label(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}')
|
||||
ui.label("Ü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}')
|
||||
ui.label(f"{convert_seconds_to_hours(last_months_overtime)} h").classes(f'text-right border px-{pad_x} py-{pad_y}')
|
||||
ui.label("Überstunden diesen Monat:").classes(f'col-span-4 text-right border px-{pad_x} py-{pad_y}')
|
||||
ui.label(f"{convert_seconds_to_hours(general_saldo)} h").classes(f'text-right border px-{pad_x} py-{pad_y}')
|
||||
ui.label("Überstunden Gesamt:").classes(f'col-span-4 text-right border px-{pad_x} py-{pad_y} text-bold')
|
||||
global overtime_overall
|
||||
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}')
|
||||
ui.label(f"{convert_seconds_to_hours(overtime_overall)} h").classes(f'text-right border px-{pad_x} py-{pad_y} text-bold')
|
||||
|
||||
overview_table()
|
||||
|
||||
@ -241,14 +245,14 @@ def page_overview_month(username: str, year: int, month: int):
|
||||
total_absence_days += absence_dict[key]
|
||||
|
||||
if total_absence_days > 0:
|
||||
ui.markdown("###Abwesenheitstage diesen Monat:")
|
||||
ui.label("Abwesenheitstage diesen Monat:").classes(h3)
|
||||
|
||||
with ui.grid(columns='auto 25%').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')
|
||||
ui.label(absence_entries[key]['name']).classes(f"border px-{pad_x} py-{pad_y}")
|
||||
ui.label(str(value)).classes(f'border px-{pad_x} py-{pad_y} text-center')
|
||||
|
||||
absence_table()
|
||||
|
||||
@ -274,7 +278,7 @@ def page_overview_month(username: str, year: int, month: int):
|
||||
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
with ui.grid(columns='1fr 1fr'):
|
||||
ui.markdown("Hiermit bestätigen Sie, dass die Zeitbuchungen im Montagsjournal korrekt sind.<br>Sollte dies nicht der Fall sein, wenden Sie sich für eine Korrektur an den Administrator.").classes('col-span-2')
|
||||
ui.label("Hiermit bestätigen Sie, dass die Zeitbuchungen im Montagsjournal korrekt sind.\nSollte dies nicht der Fall sein, wenden Sie sich für eine Korrektur an den Administrator.").classes('col-span-2').style('white-space: pre-wrap')
|
||||
ui.button("Archivieren", on_click=do_archiving)
|
||||
ui.button("Abbrechen", on_click=dialog.close)
|
||||
|
||||
@ -296,12 +300,12 @@ def page_overview_month(username: str, year: int, month: int):
|
||||
except Exception as e:
|
||||
print(str(type(e).__name__) + " " + str(e))
|
||||
if type(e) == UnboundLocalError:
|
||||
ui.markdown('#Fehler')
|
||||
ui.markdown('Benutzer existiert nicht')
|
||||
ui.label('Fehler').classes(h1)
|
||||
ui.label('Benutzer existiert nicht')
|
||||
else:
|
||||
ui.markdown('#Fehler')
|
||||
ui.markdown(str(type(e)))
|
||||
ui.markdown(str(e))
|
||||
ui.label('Fehler').classes(h1)
|
||||
ui.label(str(type(e)))
|
||||
ui.label(str(e))
|
||||
else:
|
||||
login_mask(target=f'/api/month/{username}/{year}-{month}')
|
||||
@ui.page('/api/vacation/{username}/{year}')
|
||||
@ -321,22 +325,21 @@ def page_overview_vacation(username: str, year: int):
|
||||
day = datetime.now().day
|
||||
|
||||
ui.page_title(f"Urlaubsanspruch für {current_user.fullname} für {year}")
|
||||
ui.label(datetime.now().strftime('%d.%m.%Y')).classes('absolute top-5 right-5')
|
||||
ui.space()
|
||||
ui.markdown(f'#Urlaubsanspruch für {current_user.fullname} für {year}')
|
||||
ui.label(datetime.now().strftime('%d.%m.%Y')).classes('w-full text-right')
|
||||
ui.label(f'Urlaubsanspruch für {current_user.fullname} für {year}').classes(h1)
|
||||
|
||||
pad_x = 4
|
||||
pad_y = 0
|
||||
pad_y = 2
|
||||
|
||||
vacationclaim = int(current_user.get_vacation_claim(year, month, day))
|
||||
if vacationclaim == -1:
|
||||
ui.markdown(f"###Kein Urlaubsanspruch für {year}")
|
||||
ui.label(f"Kein Urlaubsanspruch für {year}").classes(h3)
|
||||
else:
|
||||
|
||||
with ui.grid(columns='auto auto').classes(f'gap-0 border px-0 py-0'):
|
||||
ui.markdown(f"Urlaubsanspruch für {year}:").classes(f'border px-{pad_x} py-{pad_y}')
|
||||
ui.markdown(f"{vacationclaim} Tage").classes(f'text-right border px-{pad_x} py-{pad_y}')
|
||||
ui.markdown("Registrierte Urlaubstage").classes(f'border px-{pad_x} py-{pad_y} col-span-2')
|
||||
ui.label(f"Urlaubsanspruch für {year}:").classes(f'border px-{pad_x} py-{pad_y}')
|
||||
ui.label(f"{vacationclaim} Tage").classes(f'text-right border px-{pad_x} py-{pad_y}')
|
||||
ui.label("Registrierte Urlaubstage").classes(f'border px-{pad_x} py-{pad_y} col-span-2')
|
||||
vacation_counter = 0
|
||||
try:
|
||||
for i in range(1, 13):
|
||||
@ -345,24 +348,24 @@ def page_overview_vacation(username: str, year: int):
|
||||
# print(day + "." + str(i) + " " + absence_type)
|
||||
if absence_type == "U":
|
||||
day_in_list = datetime(int(year), int(i), int(day)).strftime("%d.%m.%Y")
|
||||
ui.markdown(day_in_list).classes(f'border px-{pad_x} py-{pad_y}')
|
||||
ui.markdown("-1 Tag").classes(f'border px-{pad_x} py-{pad_y} text-center')
|
||||
ui.label(day_in_list).classes(f'border px-{pad_x} py-{pad_y}')
|
||||
ui.label("-1 Tag").classes(f'border px-{pad_x} py-{pad_y} text-center')
|
||||
vacation_counter += 1
|
||||
except Exception as e:
|
||||
print(str(type(e).__name__) + " " + str(e))
|
||||
ui.markdown("**Resturlaub:**").classes(f'border px-{pad_x} py-{pad_y}')
|
||||
ui.markdown(f'**{str(vacationclaim - vacation_counter)} Tage**').classes(f'border px-{pad_x} py-{pad_y} text-center')
|
||||
ui.label("Resturlaub:").classes(f'border px-{pad_x} py-{pad_y} text-bold')
|
||||
ui.label(f'{str(vacationclaim - vacation_counter)} Tage').classes(f'border px-{pad_x} py-{pad_y} text-center text-bold')
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(str(type(e).__name__) + " " + str(e))
|
||||
if type(e) == UnboundLocalError:
|
||||
ui.markdown('#Fehler')
|
||||
ui.markdown('Benutzer existiert nicht')
|
||||
ui.label('Fehler').classes(h1)
|
||||
ui.label('Benutzer existiert nicht')
|
||||
else:
|
||||
ui.markdown('#Fehler')
|
||||
ui.markdown(str(type(e)))
|
||||
ui.markdown(str(e))
|
||||
ui.label('Fehler').classes(h1)
|
||||
ui.label(str(type(e)))
|
||||
ui.label(str(e))
|
||||
else:
|
||||
login = login_mask(target=f'/api/vacation/{username}/{year}')
|
||||
|
||||
@ -376,12 +379,11 @@ def page_overview_absence(username: str, year: int):
|
||||
if login_is_valid(username) or admin_auth:
|
||||
current_user = user(username)
|
||||
ui.page_title(f"Abwesenheitsübersicht für {current_user.fullname} für {year}")
|
||||
ui.label(datetime.now().strftime('%d.%m.%Y')).classes('absolute top-5 right-5')
|
||||
ui.space()
|
||||
ui.label(datetime.now().strftime('%d.%m.%Y')).classes('w-full text-right')
|
||||
pageheader(f"Abwesenheitsübersicht für {current_user.fullname} für {year}")
|
||||
|
||||
pad_x = 2
|
||||
pad_y = 0
|
||||
pad_y = 1
|
||||
|
||||
def absence_calender():
|
||||
|
||||
@ -394,12 +396,12 @@ def page_overview_absence(username: str, year: int):
|
||||
# Erste Zeile
|
||||
ui.space()
|
||||
for i in range(1, 32):
|
||||
ui.markdown(str(i)).classes(f'border px-{pad_x} py-{pad_y} text-center')
|
||||
ui.label(str(i)).classes(f'border px-{pad_x} py-{pad_y} text-center')
|
||||
# Monate durchgehen
|
||||
for month in range(1, 13):
|
||||
for column in range(0, 32):
|
||||
if column == 0:
|
||||
ui.markdown(month_name[month]).classes(f'border px-{pad_x} py-{pad_y} text.center')
|
||||
ui.label(month_name[month]).classes(f'border px-{pad_x} py-{pad_y} text.center')
|
||||
else:
|
||||
absences = current_user.get_absence(year, month)
|
||||
if str(column) in list(absences):
|
||||
@ -407,7 +409,7 @@ def page_overview_absence(username: str, year: int):
|
||||
text_color = absence_entries[absences[str(column)]]['text-color']
|
||||
tooltip_text = absence_entries[absences[str(column)]]['name']
|
||||
with ui.element():
|
||||
ui.markdown(absences[str(column)]).classes(f'border px-{pad_x} py-{pad_y} bg-{bg_color} text-{text_color} align-middle text-center')
|
||||
ui.label(absences[str(column)]).classes(f'border px-{pad_x} py-{pad_y} bg-{bg_color} text-{text_color} align-middle text-center')
|
||||
ui.tooltip(tooltip_text)
|
||||
else:
|
||||
tooltip_text = ""
|
||||
@ -430,17 +432,17 @@ def page_overview_absence(username: str, year: int):
|
||||
|
||||
def absence_table():
|
||||
|
||||
with ui.grid(columns='auto auto').classes(f'gap-0 px-0 py-0'):
|
||||
ui.markdown('**Summen**').classes('col-span-2 px-2')
|
||||
with ui.grid(columns='auto auto').classes(f'gap-0 px-0 py-0 items-baseline'):
|
||||
ui.label('Summen').classes('col-span-2 px-2 text-bold')
|
||||
for type in list(absence_entries):
|
||||
number_of_days = 0
|
||||
ui.markdown(absence_entries[type]["name"]).classes(f'border px-{pad_x} py-{pad_y}')
|
||||
ui.label(absence_entries[type]["name"]).classes(f'border px-{pad_x} py-{pad_y}')
|
||||
for month in range(1, 13):
|
||||
absences_of_month = current_user.get_absence(year, month)
|
||||
for i in list(absences_of_month):
|
||||
if absences_of_month[i] == type:
|
||||
number_of_days += 1
|
||||
ui.markdown(str(number_of_days)).classes(f'border px-{pad_x} py-{pad_y} text-center')
|
||||
ui.label(str(number_of_days)).classes(f'border px-{pad_x} py-{pad_y} text-center')
|
||||
absence_table()
|
||||
|
||||
else:
|
||||
|
@ -85,3 +85,45 @@ absence_entries = {"U": { "name": "Urlaub",
|
||||
"color": "pink",
|
||||
"text-color": "white"}
|
||||
}
|
||||
|
||||
# Styling
|
||||
h1 = "text-5xl font-bold"
|
||||
h2 = "text-4xl font-medium"
|
||||
h3 = "text-3xl font-light"
|
||||
h4 = "text-2xl"
|
||||
|
||||
# SVGs:
|
||||
|
||||
no_photo_svg = f'''<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 496.158 496.158" xml:space="preserve">
|
||||
<path style="fill:#D61E1E;" d="M248.082,0.003C111.07,0.003,0,111.063,0,248.085c0,137.001,111.07,248.07,248.082,248.07
|
||||
c137.006,0,248.076-111.069,248.076-248.07C496.158,111.062,385.088,0.003,248.082,0.003z"/>
|
||||
<path style="fill:#F4EDED;" d="M248.082,39.002C132.609,39.002,39,132.602,39,248.084c0,115.463,93.609,209.072,209.082,209.072
|
||||
c115.467,0,209.076-93.609,209.076-209.072C457.158,132.602,363.549,39.002,248.082,39.002z"/>
|
||||
<g>
|
||||
<path style="fill:#5B5147;" d="M145.23,144.237h-24.44c-3.21,0-5.819,4.741-5.819,10.605s2.609,10.611,5.819,10.611h24.44
|
||||
c3.217,0,5.826-4.747,5.826-10.611C151.057,148.978,148.447,144.237,145.23,144.237z"/>
|
||||
<path style="fill:#5B5147;" d="M380.289,172.06H226.545c-2.025-9.851-9.416-17.176-18.244-17.176h-92.199
|
||||
c-10.403,0-18.818,10.125-18.818,22.592V328.9c0,10.254,8.314,18.581,18.58,18.581h264.425c10.262,0,18.586-8.327,18.586-18.581
|
||||
V190.655C398.875,180.38,390.551,172.06,380.289,172.06z"/>
|
||||
</g>
|
||||
<path style="fill:#F4EDED;" d="M248.076,166.711c-51.133,0-92.604,41.462-92.604,92.602c0,51.146,41.471,92.608,92.604,92.608
|
||||
c51.139,0,92.6-41.462,92.6-92.608C340.676,208.174,299.215,166.711,248.076,166.711z"/>
|
||||
<path style="fill:#5B5147;" d="M248.086,171.416c-48.547,0-87.909,39.355-87.909,87.909c0,48.537,39.362,87.898,87.909,87.898
|
||||
c48.543,0,87.896-39.361,87.896-87.898C335.981,210.771,296.629,171.416,248.086,171.416z"/>
|
||||
<path style="fill:#F4EDED;" d="M248.611,205.005c-29.992,0-54.312,24.31-54.312,54.308c0,29.991,24.319,54.321,54.312,54.321
|
||||
s54.318-24.33,54.318-54.321C302.93,229.315,278.603,205.005,248.611,205.005z"/>
|
||||
<path style="fill:#5B5147;" d="M248.611,209.528c-27.494,0-49.789,22.286-49.789,49.786c0,27.494,22.295,49.798,49.789,49.798
|
||||
c27.496,0,49.795-22.304,49.795-49.798C298.406,231.814,276.107,209.528,248.611,209.528z"/>
|
||||
<g>
|
||||
<path style="fill:#F4EDED;" d="M230.224,215.002c-14.401,0-26.065,11.674-26.065,26.067c0,14.399,11.664,26.073,26.065,26.073
|
||||
c14.391,0,26.065-11.674,26.065-26.073C256.289,226.676,244.614,215.002,230.224,215.002z"/>
|
||||
<path style="fill:#F4EDED;" d="M159.698,165.453h-45.712c-3.756,0-6.805,3.045-6.805,6.792v25.594c0,3.04,2.004,5.575,4.756,6.448
|
||||
c0.65,0.209,1.328,0.35,2.049,0.35h45.712c3.76,0,6.793-3.04,6.793-6.798v-25.594C166.491,168.498,163.458,165.453,159.698,165.453
|
||||
z"/>
|
||||
</g>
|
||||
<path style="fill:#D61E1E;" d="M85.85,60.394c-9.086,7.86-17.596,16.37-25.456,25.456l349.914,349.914
|
||||
c9.086-7.861,17.596-16.37,25.456-25.456L85.85,60.394z"/>
|
||||
</svg>'''
|
@ -199,6 +199,7 @@ def homepage():
|
||||
overviews = ui.tab('Übersichten')
|
||||
absence = ui.tab('Urlaubsantrag')
|
||||
absence.set_visibility(load_adminsettings()["vacation_application"])
|
||||
pw_change = ui.tab("Passwort")
|
||||
|
||||
with ui.grid(columns='1fr auto 1fr').classes('w-full items-center'):
|
||||
ui.space()
|
||||
@ -211,9 +212,9 @@ def homepage():
|
||||
def activate_absence():
|
||||
binder_absence.value = True
|
||||
|
||||
with ui.grid(columns='1fr 1fr'):
|
||||
with ui.grid(columns='1fr 1fr').classes('items-end'):
|
||||
|
||||
ui.markdown("**Monatsübersicht:**").classes('col-span-2')
|
||||
ui.label("Monatsübersicht:").classes('col-span-2 font-bold')
|
||||
|
||||
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)
|
||||
@ -221,10 +222,10 @@ def homepage():
|
||||
|
||||
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')
|
||||
ui.label("Urlaubsanspruch").classes('col-span-2 font-bold')
|
||||
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')
|
||||
ui.label("Fehlzeitenübersicht").classes('col-span-2 font-bold')
|
||||
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')
|
||||
@ -281,6 +282,31 @@ def homepage():
|
||||
ui.button("Zurückziehen", on_click=retract_va).tooltip("Hiermit wird der oben gewählte Urlaubsantrag zurückgezogen.").classes('w-full')
|
||||
|
||||
open_vacation_applications()
|
||||
with ui.tab_panel(pw_change):
|
||||
ui.label("Passwort ändern").classes('font-bold')
|
||||
with ui.grid(columns='auto auto').classes('items-end'):
|
||||
ui.label("Altes Passwort:")
|
||||
old_pw_input = ui.input(password=True)
|
||||
ui.label("Neues Passwort:")
|
||||
new_pw_input = ui.input(password=True)
|
||||
ui.label("Neues Passwort bestätigen:")
|
||||
new_pw_confirm_input = ui.input(password=True)
|
||||
def revert_pw_inputs():
|
||||
old_pw_input.value = ""
|
||||
new_pw_input.value = ""
|
||||
new_pw_confirm_input.value = ""
|
||||
def save_new_password():
|
||||
if hash_password(old_pw_input.value) == current_user.password:
|
||||
if new_pw_input.value == new_pw_confirm_input.value:
|
||||
current_user.password = hash_password(new_pw_input.value)
|
||||
current_user.write_settings()
|
||||
ui.notify("Neues Passwort gespeichert")
|
||||
else:
|
||||
ui.notify("Passwortbestätigung stimmt nicht überein")
|
||||
else:
|
||||
ui.notify("Altes Passwort nicht korrekt")
|
||||
ui.button("Speichern", on_click=save_new_password)
|
||||
ui.button("Zurücksetzen", on_click=revert_pw_inputs)
|
||||
ui.space()
|
||||
|
||||
else:
|
||||
|
@ -36,44 +36,106 @@ def page_touchscreen():
|
||||
number_of_users = len(userlist)
|
||||
buttons = { }
|
||||
|
||||
number_of_columns = 5
|
||||
|
||||
def set_columns(width):
|
||||
nonlocal number_of_columns
|
||||
if width > 1400:
|
||||
number_of_columns = 6
|
||||
elif width > 1200:
|
||||
number_of_columns = 5
|
||||
elif width > 900:
|
||||
number_of_columns = 4
|
||||
elif width > 750:
|
||||
number_of_columns = 3
|
||||
else:
|
||||
number_of_columns = 2
|
||||
user_buttons.refresh()
|
||||
|
||||
ui.on('resize', lambda e: set_columns(e.args['width']))
|
||||
|
||||
@ui.refreshable
|
||||
def user_buttons():
|
||||
if number_of_users > 5:
|
||||
number_of_columns = 5
|
||||
else:
|
||||
number_of_columns = number_of_users
|
||||
|
||||
# Fenstergröße bestimmen und dann Spalten anpassen
|
||||
ui.add_head_html('''
|
||||
<script>
|
||||
function emitSize() {
|
||||
emitEvent('resize', {
|
||||
width: document.body.offsetWidth,
|
||||
height: document.body.offsetHeight,
|
||||
});
|
||||
}
|
||||
window.onload = emitSize;
|
||||
window.onresize = emitSize;
|
||||
</script>
|
||||
''')
|
||||
|
||||
with ui.grid(columns=number_of_columns).classes('w-full center'):
|
||||
|
||||
for name in userlist:
|
||||
current_user = user(name)
|
||||
current_button = ui.button(on_click=lambda name=name: button_click(name)).classes(f'w-md h-full min-h-[{admin_settings["button_height"]}px]')
|
||||
|
||||
with current_button:
|
||||
if admin_settings["photos_on_touchscreen"]:
|
||||
try:
|
||||
with open(current_user.photofile, 'r') as file:
|
||||
pass
|
||||
file.close()
|
||||
ui.image(current_user.photofile).classes(f'max-h-[{admin_settings["picture_height"]}px]').props('fit=scale-down')
|
||||
except:
|
||||
pass
|
||||
column_classes = "w-full items-center"
|
||||
if admin_settings["times_on_touchscreen"] or admin_settings["photos_on_touchscreen"]:
|
||||
column_classes += " self-end"
|
||||
with ui.column().classes(column_classes):
|
||||
if admin_settings["times_on_touchscreen"]:
|
||||
todays_timestamps = current_user.get_day_timestamps()
|
||||
# Wenn wir Einträge haben
|
||||
if len(todays_timestamps) > 0 and admin_settings["times_on_touchscreen"]:
|
||||
table_string = ""
|
||||
for i in range(0, len(todays_timestamps), 2):
|
||||
try:
|
||||
table_string += f"{datetime.datetime.fromtimestamp(todays_timestamps[i]).strftime('%H:%M')} - {datetime.datetime.fromtimestamp(todays_timestamps[i+1]).strftime('%H:%M')}"
|
||||
except IndexError:
|
||||
table_string += f"{datetime.datetime.fromtimestamp(todays_timestamps[i]).strftime('%H:%M')} -"
|
||||
if i < len(todays_timestamps) - 2:
|
||||
table_string += ", "
|
||||
ui.markdown(table_string)
|
||||
ui.label(current_user.fullname).classes('text-center')
|
||||
with ui.grid(columns='1fr 1fr').classes('w-full h-full py-5 items-start'):
|
||||
|
||||
if admin_settings["photos_on_touchscreen"]:
|
||||
image_size = int(admin_settings["picture_height"])
|
||||
try:
|
||||
with open(current_user.photofile, 'r') as file:
|
||||
pass
|
||||
ui.image(current_user.photofile).classes(f'max-h-[{image_size}px]').props('fit=scale-down')
|
||||
except:
|
||||
no_photo_svg = f'''<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg height="{image_size/2}px" width="{image_size/2}px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 496.158 496.158" xml:space="preserve">
|
||||
<path style="fill:#D61E1E;" d="M248.082,0.003C111.07,0.003,0,111.063,0,248.085c0,137.001,111.07,248.07,248.082,248.07
|
||||
c137.006,0,248.076-111.069,248.076-248.07C496.158,111.062,385.088,0.003,248.082,0.003z"/>
|
||||
<path style="fill:#F4EDED;" d="M248.082,39.002C132.609,39.002,39,132.602,39,248.084c0,115.463,93.609,209.072,209.082,209.072
|
||||
c115.467,0,209.076-93.609,209.076-209.072C457.158,132.602,363.549,39.002,248.082,39.002z"/>
|
||||
<g>
|
||||
<path style="fill:#5B5147;" d="M145.23,144.237h-24.44c-3.21,0-5.819,4.741-5.819,10.605s2.609,10.611,5.819,10.611h24.44
|
||||
c3.217,0,5.826-4.747,5.826-10.611C151.057,148.978,148.447,144.237,145.23,144.237z"/>
|
||||
<path style="fill:#5B5147;" d="M380.289,172.06H226.545c-2.025-9.851-9.416-17.176-18.244-17.176h-92.199
|
||||
c-10.403,0-18.818,10.125-18.818,22.592V328.9c0,10.254,8.314,18.581,18.58,18.581h264.425c10.262,0,18.586-8.327,18.586-18.581
|
||||
V190.655C398.875,180.38,390.551,172.06,380.289,172.06z"/>
|
||||
</g>
|
||||
<path style="fill:#F4EDED;" d="M248.076,166.711c-51.133,0-92.604,41.462-92.604,92.602c0,51.146,41.471,92.608,92.604,92.608
|
||||
c51.139,0,92.6-41.462,92.6-92.608C340.676,208.174,299.215,166.711,248.076,166.711z"/>
|
||||
<path style="fill:#5B5147;" d="M248.086,171.416c-48.547,0-87.909,39.355-87.909,87.909c0,48.537,39.362,87.898,87.909,87.898
|
||||
c48.543,0,87.896-39.361,87.896-87.898C335.981,210.771,296.629,171.416,248.086,171.416z"/>
|
||||
<path style="fill:#F4EDED;" d="M248.611,205.005c-29.992,0-54.312,24.31-54.312,54.308c0,29.991,24.319,54.321,54.312,54.321
|
||||
s54.318-24.33,54.318-54.321C302.93,229.315,278.603,205.005,248.611,205.005z"/>
|
||||
<path style="fill:#5B5147;" d="M248.611,209.528c-27.494,0-49.789,22.286-49.789,49.786c0,27.494,22.295,49.798,49.789,49.798
|
||||
c27.496,0,49.795-22.304,49.795-49.798C298.406,231.814,276.107,209.528,248.611,209.528z"/>
|
||||
<g>
|
||||
<path style="fill:#F4EDED;" d="M230.224,215.002c-14.401,0-26.065,11.674-26.065,26.067c0,14.399,11.664,26.073,26.065,26.073
|
||||
c14.391,0,26.065-11.674,26.065-26.073C256.289,226.676,244.614,215.002,230.224,215.002z"/>
|
||||
<path style="fill:#F4EDED;" d="M159.698,165.453h-45.712c-3.756,0-6.805,3.045-6.805,6.792v25.594c0,3.04,2.004,5.575,4.756,6.448
|
||||
c0.65,0.209,1.328,0.35,2.049,0.35h45.712c3.76,0,6.793-3.04,6.793-6.798v-25.594C166.491,168.498,163.458,165.453,159.698,165.453
|
||||
z"/>
|
||||
</g>
|
||||
<path style="fill:#D61E1E;" d="M85.85,60.394c-9.086,7.86-17.596,16.37-25.456,25.456l349.914,349.914
|
||||
c9.086-7.861,17.596-16.37,25.456-25.456L85.85,60.394z"/>
|
||||
</svg>'''
|
||||
ui.html(no_photo_svg)
|
||||
with ui.column().classes('' if admin_settings["photos_on_touchscreen"] else 'col-span-2'):
|
||||
ui.label(current_user.fullname).classes('text-left text-xl text.bold')
|
||||
if admin_settings["times_on_touchscreen"]:
|
||||
todays_timestamps = current_user.get_day_timestamps()
|
||||
# Wenn wir Einträge haben
|
||||
if len(todays_timestamps) > 0 and admin_settings["times_on_touchscreen"]:
|
||||
table_string = ""
|
||||
for i in range(0, len(todays_timestamps), 2):
|
||||
try:
|
||||
table_string += f"{datetime.datetime.fromtimestamp(todays_timestamps[i]).strftime('%H:%M')} - {datetime.datetime.fromtimestamp(todays_timestamps[i+1]).strftime('%H:%M')}"
|
||||
except IndexError:
|
||||
table_string += f"{datetime.datetime.fromtimestamp(todays_timestamps[i]).strftime('%H:%M')} -"
|
||||
if i < len(todays_timestamps) - 2:
|
||||
table_string += "\n"
|
||||
ui.label(table_string).style('white-space: pre-wrap').classes('text-left')
|
||||
if current_user.stamp_status() == status_in:
|
||||
current_button.props('color=green')
|
||||
else:
|
||||
|
@ -225,6 +225,8 @@ class user:
|
||||
with open(os.path.join(self.userfolder, f"{year}-{month}.json"), 'r') as json_file:
|
||||
data = json.load(json_file)
|
||||
return data["archived"]
|
||||
except FileNotFoundError:
|
||||
return False
|
||||
except:
|
||||
return -1
|
||||
|
||||
|
@ -20,8 +20,8 @@ class pageheader:
|
||||
def __init__(self, heading):
|
||||
self.heading = heading
|
||||
|
||||
ui.markdown(f"##{app_title} {app_version}")
|
||||
ui.markdown(f"###{self.heading}")
|
||||
ui.label(f"{app_title} {app_version}").classes(h2)
|
||||
ui.label(self.heading).classes(h3)
|
||||
|
||||
class ValueBinder:
|
||||
def __init__(self):
|
||||
@ -63,7 +63,7 @@ class login_mask:
|
||||
|
||||
pageheader("Bitte einloggen:")
|
||||
|
||||
with ui.grid(columns='20% auto 20%').classes('w-full justify-center'):
|
||||
with ui.grid(columns='1fr auto 1fr').classes('w-full justify-center'):
|
||||
|
||||
ui.space()
|
||||
with ui.grid(columns=2):
|
||||
|
125
main.py
125
main.py
@ -41,7 +41,7 @@ def main():
|
||||
for i in list(app.urls):
|
||||
url_string += f"{i}, "
|
||||
url_string = url_string[0:-2]
|
||||
print("Weboberfläche erreichbar unter: " + url_string)
|
||||
print("Oberfläche erreichbar unter: " + url_string)
|
||||
|
||||
app.on_startup(startup_message)
|
||||
|
||||
@ -51,8 +51,129 @@ def main():
|
||||
ui.tab.default_classes('normal-case')
|
||||
ui.toggle.default_classes('normal-case')
|
||||
ui.toggle.default_props('rounded')
|
||||
ui.row.default_classes('items-baseline')
|
||||
|
||||
ui.run(favicon="favicon.svg", port=port, storage_secret=secret, language='de-DE', show_welcome_message=False)
|
||||
favicon = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:ns1="http://sozi.baierouge.fr"
|
||||
xmlns:cc="http://web.resource.org/cc/"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
id="svg1"
|
||||
sodipodi:docname="timeedit.svg"
|
||||
viewBox="0 0 60 60"
|
||||
sodipodi:version="0.32"
|
||||
_SVGFile__filename="oldscale/actions/todo.svg"
|
||||
version="1.0"
|
||||
y="0"
|
||||
x="0"
|
||||
inkscape:version="0.40"
|
||||
sodipodi:docbase="/home/danny/work/flat/SVG/mono/EXTRA/kexi"
|
||||
>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
bordercolor="#666666"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-y="0"
|
||||
pagecolor="#ffffff"
|
||||
inkscape:window-height="698"
|
||||
inkscape:zoom="5.5342875"
|
||||
inkscape:window-x="0"
|
||||
borderopacity="1.0"
|
||||
inkscape:current-layer="svg1"
|
||||
inkscape:cx="36.490405"
|
||||
inkscape:cy="19.560172"
|
||||
inkscape:window-width="1024"
|
||||
inkscape:pageopacity="0.0"
|
||||
/>
|
||||
<g
|
||||
id="g1113"
|
||||
transform="matrix(1.8141 0 0 1.8141 -24.352 -32.241)"
|
||||
>
|
||||
<path
|
||||
id="path1894"
|
||||
style="stroke-linejoin:round;stroke:#ffffff;stroke-linecap:round;stroke-width:5.5124;fill:none"
|
||||
d="m43.399 34.31c0 7.418-6.02 13.438-13.438 13.438s-13.438-6.02-13.438-13.438 6.02-13.438 13.438-13.438 13.438 6.02 13.438 13.438z"
|
||||
/>
|
||||
<path
|
||||
id="path741"
|
||||
style="stroke-linejoin:round;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-width:2.7562;fill:#ffffff"
|
||||
d="m43.399 34.31c0 7.418-6.02 13.438-13.438 13.438s-13.438-6.02-13.438-13.438 6.02-13.438 13.438-13.438 13.438 6.02 13.438 13.438z"
|
||||
/>
|
||||
<path
|
||||
id="path743"
|
||||
sodipodi:nodetypes="cc"
|
||||
style="stroke-linejoin:round;stroke:#000000;stroke-linecap:round;stroke-width:2.7562;fill:none"
|
||||
d="m29.961 34.169v-8.723"
|
||||
/>
|
||||
<path
|
||||
id="path744"
|
||||
style="stroke-linejoin:round;stroke:#000000;stroke-linecap:round;stroke-width:2.7562;fill:none"
|
||||
d="m30.185 34.066l5.869 3.388"
|
||||
/>
|
||||
<path
|
||||
id="path742"
|
||||
style="stroke-linejoin:round;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-width:1.7226;fill:#000000"
|
||||
d="m31.178 34.31c0 0.672-0.545 1.217-1.217 1.217s-1.217-0.545-1.217-1.217 0.545-1.218 1.217-1.218 1.217 0.546 1.217 1.218z"
|
||||
/>
|
||||
</g
|
||||
>
|
||||
<metadata
|
||||
>
|
||||
<rdf:RDF
|
||||
>
|
||||
<cc:Work
|
||||
>
|
||||
<dc:format
|
||||
>image/svg+xml</dc:format
|
||||
>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage"
|
||||
/>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/publicdomain/"
|
||||
/>
|
||||
<dc:publisher
|
||||
>
|
||||
<cc:Agent
|
||||
rdf:about="http://openclipart.org/"
|
||||
>
|
||||
<dc:title
|
||||
>Openclipart</dc:title
|
||||
>
|
||||
</cc:Agent
|
||||
>
|
||||
</dc:publisher
|
||||
>
|
||||
</cc:Work
|
||||
>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/publicdomain/"
|
||||
>
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction"
|
||||
/>
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution"
|
||||
/>
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks"
|
||||
/>
|
||||
</cc:License
|
||||
>
|
||||
</rdf:RDF
|
||||
>
|
||||
</metadata
|
||||
>
|
||||
</svg
|
||||
>
|
||||
'''
|
||||
|
||||
ui.run(favicon=favicon, port=port, storage_secret=secret, language='de-DE', show_welcome_message=False)
|
||||
|
||||
if __name__ in ("__main__", "__mp_main__"):
|
||||
parser = argparse.ArgumentParser(description=f'{app_title} {app_version}')
|
||||
|
33
no-photo-svgrepo-com.svg
Normal file
33
no-photo-svgrepo-com.svg
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 496.158 496.158" xml:space="preserve">
|
||||
<path style="fill:#D61E1E;" d="M248.082,0.003C111.07,0.003,0,111.063,0,248.085c0,137.001,111.07,248.07,248.082,248.07
|
||||
c137.006,0,248.076-111.069,248.076-248.07C496.158,111.062,385.088,0.003,248.082,0.003z"/>
|
||||
<path style="fill:#F4EDED;" d="M248.082,39.002C132.609,39.002,39,132.602,39,248.084c0,115.463,93.609,209.072,209.082,209.072
|
||||
c115.467,0,209.076-93.609,209.076-209.072C457.158,132.602,363.549,39.002,248.082,39.002z"/>
|
||||
<g>
|
||||
<path style="fill:#5B5147;" d="M145.23,144.237h-24.44c-3.21,0-5.819,4.741-5.819,10.605s2.609,10.611,5.819,10.611h24.44
|
||||
c3.217,0,5.826-4.747,5.826-10.611C151.057,148.978,148.447,144.237,145.23,144.237z"/>
|
||||
<path style="fill:#5B5147;" d="M380.289,172.06H226.545c-2.025-9.851-9.416-17.176-18.244-17.176h-92.199
|
||||
c-10.403,0-18.818,10.125-18.818,22.592V328.9c0,10.254,8.314,18.581,18.58,18.581h264.425c10.262,0,18.586-8.327,18.586-18.581
|
||||
V190.655C398.875,180.38,390.551,172.06,380.289,172.06z"/>
|
||||
</g>
|
||||
<path style="fill:#F4EDED;" d="M248.076,166.711c-51.133,0-92.604,41.462-92.604,92.602c0,51.146,41.471,92.608,92.604,92.608
|
||||
c51.139,0,92.6-41.462,92.6-92.608C340.676,208.174,299.215,166.711,248.076,166.711z"/>
|
||||
<path style="fill:#5B5147;" d="M248.086,171.416c-48.547,0-87.909,39.355-87.909,87.909c0,48.537,39.362,87.898,87.909,87.898
|
||||
c48.543,0,87.896-39.361,87.896-87.898C335.981,210.771,296.629,171.416,248.086,171.416z"/>
|
||||
<path style="fill:#F4EDED;" d="M248.611,205.005c-29.992,0-54.312,24.31-54.312,54.308c0,29.991,24.319,54.321,54.312,54.321
|
||||
s54.318-24.33,54.318-54.321C302.93,229.315,278.603,205.005,248.611,205.005z"/>
|
||||
<path style="fill:#5B5147;" d="M248.611,209.528c-27.494,0-49.789,22.286-49.789,49.786c0,27.494,22.295,49.798,49.789,49.798
|
||||
c27.496,0,49.795-22.304,49.795-49.798C298.406,231.814,276.107,209.528,248.611,209.528z"/>
|
||||
<g>
|
||||
<path style="fill:#F4EDED;" d="M230.224,215.002c-14.401,0-26.065,11.674-26.065,26.067c0,14.399,11.664,26.073,26.065,26.073
|
||||
c14.391,0,26.065-11.674,26.065-26.073C256.289,226.676,244.614,215.002,230.224,215.002z"/>
|
||||
<path style="fill:#F4EDED;" d="M159.698,165.453h-45.712c-3.756,0-6.805,3.045-6.805,6.792v25.594c0,3.04,2.004,5.575,4.756,6.448
|
||||
c0.65,0.209,1.328,0.35,2.049,0.35h45.712c3.76,0,6.793-3.04,6.793-6.798v-25.594C166.491,168.498,163.458,165.453,159.698,165.453
|
||||
z"/>
|
||||
</g>
|
||||
<path style="fill:#D61E1E;" d="M85.85,60.394c-9.086,7.86-17.596,16.37-25.456,25.456l349.914,349.914
|
||||
c9.086-7.861,17.596-16.37,25.456-25.456L85.85,60.394z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
Loading…
x
Reference in New Issue
Block a user