Merge branch 'rework_homepage' into web_ui
This commit is contained in:
commit
3d04f81c82
75
admin.py
75
admin.py
@ -1,9 +1,11 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import dateutil.easter
|
import dateutil.easter
|
||||||
|
from PIL.SpiderImagePlugin import isInt
|
||||||
from dateutil.easter import *
|
from dateutil.easter import *
|
||||||
|
|
||||||
from nicegui import ui, app, events
|
from nicegui import ui, app, events
|
||||||
|
from nicegui.html import button
|
||||||
|
|
||||||
from users import *
|
from users import *
|
||||||
from definitions import *
|
from definitions import *
|
||||||
@ -192,7 +194,7 @@ def page_admin():
|
|||||||
|
|
||||||
# Alle Timestamps durchgehen und sie den Dictionaryeinträgen zuordnen:
|
# Alle Timestamps durchgehen und sie den Dictionaryeinträgen zuordnen:
|
||||||
for stamp in timestamps:
|
for stamp in timestamps:
|
||||||
day_of_month_of_timestamp = int(datetime.datetime.fromtimestamp(int(stamp)).strftime("%-d"))
|
day_of_month_of_timestamp = int(datetime.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
|
||||||
@ -338,7 +340,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
|||||||
# Suche mir alle timestamps für diesen Tag
|
# Suche mir alle timestamps für diesen Tag
|
||||||
for i in timestamps:
|
for i in timestamps:
|
||||||
actual_timestamp = datetime.datetime.fromtimestamp(int(i))
|
actual_timestamp = datetime.datetime.fromtimestamp(int(i))
|
||||||
timestamp_day = actual_timestamp.strftime('%-d')
|
timestamp_day = actual_timestamp.day
|
||||||
|
|
||||||
if int(timestamp_day) == int(day):
|
if int(timestamp_day) == int(day):
|
||||||
timestamps_of_this_day.append(i)
|
timestamps_of_this_day.append(i)
|
||||||
@ -596,6 +598,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
|||||||
button_update.move(timetable_header)
|
button_update.move(timetable_header)
|
||||||
|
|
||||||
with ui.tab_panel(settings):
|
with ui.tab_panel(settings):
|
||||||
|
with ui.grid(columns='auto auto'):
|
||||||
with ui.card():
|
with ui.card():
|
||||||
ui.markdown("**Administrationsbenutzer:**")
|
ui.markdown("**Administrationsbenutzer:**")
|
||||||
with ui.grid(columns=2):
|
with ui.grid(columns=2):
|
||||||
@ -608,9 +611,14 @@ Dies kann nicht rückgängig gemacht werden!''')
|
|||||||
output_dict["admin_password"] = data["admin_password"]
|
output_dict["admin_password"] = data["admin_password"]
|
||||||
output_dict["port"] = port.value
|
output_dict["port"] = port.value
|
||||||
output_dict["secret"] = secret
|
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["holidays"] = data["holidays"]
|
output_dict["holidays"] = data["holidays"]
|
||||||
json_dict = json.dumps(output_dict, indent=4)
|
json_dict = json.dumps(output_dict, indent=4)
|
||||||
with open(f"{scriptpath}/{usersettingsfilename}", "w") as outputfile:
|
with open(os.path.join(scriptpath, usersettingsfilename), "w") as outputfile:
|
||||||
outputfile.write(json_dict)
|
outputfile.write(json_dict)
|
||||||
if int(old_port) != int(port.value):
|
if int(old_port) != int(port.value):
|
||||||
with ui.dialog() as dialog, ui.card():
|
with ui.dialog() as dialog, ui.card():
|
||||||
@ -620,24 +628,66 @@ Dies kann nicht rückgängig gemacht werden!''')
|
|||||||
ui.notify("Einstellungen gespeichert")
|
ui.notify("Einstellungen gespeichert")
|
||||||
timetable.refresh()
|
timetable.refresh()
|
||||||
|
|
||||||
|
|
||||||
ui.markdown("Benutzername des Adminstrators")
|
ui.markdown("Benutzername des Adminstrators")
|
||||||
admin_user = ui.input()
|
admin_user = ui.input().tooltip("Geben Sie hier den Benutzernamen für den Adminstationsnutzer ein")
|
||||||
admin_user.value = data["admin_user"]
|
admin_user.value = data["admin_user"]
|
||||||
ui.markdown("Passwort des Administrators")
|
ui.markdown("Passwort des Administrators")
|
||||||
admin_password = ui.input(password=True)
|
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"]
|
secret = data["secret"]
|
||||||
|
|
||||||
with ui.card():
|
with ui.card():
|
||||||
ui.markdown("**Systemeinstellungen:**")
|
ui.markdown("**Systemeinstellungen:**")
|
||||||
with ui.grid(columns=2):
|
with ui.grid(columns=2):
|
||||||
|
def check_is_number(number):
|
||||||
|
try:
|
||||||
|
number = int(number)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
ui.markdown("Port:")
|
ui.markdown("Port:")
|
||||||
port = ui.input()
|
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"]
|
old_port = data["port"]
|
||||||
port.value = old_port
|
port.value = old_port
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
with ui.card():
|
||||||
|
ui.markdown("**Einstellungen für das Touchscreenterminal:**")
|
||||||
|
with ui.column():
|
||||||
|
touchscreen_switch = ui.switch("Touchscreenterminal aktivieren")
|
||||||
|
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')
|
||||||
|
|
||||||
def holiday_section():
|
def holiday_section():
|
||||||
with ui.card():
|
with ui.card():
|
||||||
ui.markdown('**Feiertage:**')
|
ui.markdown('**Feiertage:**')
|
||||||
@ -825,9 +875,9 @@ Dies kann nicht rückgängig gemacht werden!''')
|
|||||||
with ui.grid(columns='auto auto'):
|
with ui.grid(columns='auto auto'):
|
||||||
ui.space()
|
ui.space()
|
||||||
with ui.row():
|
with ui.row():
|
||||||
ui.button("Gesetzliche Feiertage eintragen", on_click=defined_holidays)
|
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)
|
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')
|
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().classes('col-span-2')
|
ui.separator().classes('col-span-2')
|
||||||
for year_entry in year_list:
|
for year_entry in year_list:
|
||||||
@ -840,7 +890,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
|||||||
|
|
||||||
holiday_section()
|
holiday_section()
|
||||||
|
|
||||||
ui.button("Speichern", on_click=save_admin_settings)
|
ui.button("Speichern", on_click=save_admin_settings).tooltip("Hiermit werden sämtliche oben gemachten Einstellungen gespeichert.")
|
||||||
|
|
||||||
with ui.tab_panel(users):
|
with ui.tab_panel(users):
|
||||||
ui.markdown("###Benutzerverwaltung")
|
ui.markdown("###Benutzerverwaltung")
|
||||||
@ -1026,7 +1076,8 @@ Dies kann nicht rückgängig gemacht werden!''')
|
|||||||
with ui.dialog() as dialog, ui.card():
|
with ui.dialog() as dialog, ui.card():
|
||||||
ui.markdown("Geben Sie den Benutzernamen für das neue Konto an:")
|
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,
|
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})
|
'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():
|
with ui.row():
|
||||||
ui.button("OK", on_click=create_new_user)
|
ui.button("OK", on_click=create_new_user)
|
||||||
ui.button("Abbrechen", on_click=dialog.close)
|
ui.button("Abbrechen", on_click=dialog.close)
|
||||||
|
13
api.py
13
api.py
@ -387,17 +387,26 @@ def page_overview_absence(username: str, year: int):
|
|||||||
if str(column) in list(absences):
|
if str(column) in list(absences):
|
||||||
bg_color = absence_entries[absences[str(column)]]['color']
|
bg_color = absence_entries[absences[str(column)]]['color']
|
||||||
text_color = absence_entries[absences[str(column)]]['text-color']
|
text_color = absence_entries[absences[str(column)]]['text-color']
|
||||||
ui.markdown(absences[str(column)]).classes(f'border px-{pad_x} py-{pad_y} bg-{bg_color} text-{text_color} text-center')
|
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.tooltip(tooltip_text)
|
||||||
else:
|
else:
|
||||||
|
tooltip_text = ""
|
||||||
if column > monthrange(year, month)[1]:
|
if column > monthrange(year, month)[1]:
|
||||||
bg_color = 'gray-500'
|
bg_color = 'gray-500'
|
||||||
|
tooltip_text="Tag exisitiert nicht"
|
||||||
elif int(current_user.get_day_workhours(year, month, column)) == 0:
|
elif int(current_user.get_day_workhours(year, month, column)) == 0:
|
||||||
bg_color = 'gray-300'
|
bg_color = 'gray-300'
|
||||||
|
tooltip_text = "Kein Arbeitstag"
|
||||||
elif int(current_user.get_day_workhours(year, month, column)) == -1:
|
elif int(current_user.get_day_workhours(year, month, column)) == -1:
|
||||||
bg_color = 'gray-400'
|
bg_color = 'gray-400'
|
||||||
|
tooltip_text = "Kein Arbeitsverhältnis"
|
||||||
else:
|
else:
|
||||||
bg_color = 'inherit'
|
bg_color = 'inherit'
|
||||||
ui.space().classes(f'border px-{pad_x} py-{pad_y} bg-{bg_color}')
|
with ui.label("").classes(f'border px-{pad_x} py-{pad_y} bg-{bg_color}'):
|
||||||
|
if tooltip_text != "":
|
||||||
|
ui.tooltip(tooltip_text)
|
||||||
|
|
||||||
absence_calender()
|
absence_calender()
|
||||||
|
|
||||||
|
@ -26,6 +26,11 @@ standard_adminsettings = { "admin_user": "admin",
|
|||||||
"admin_password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",
|
"admin_password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",
|
||||||
"port": "8090",
|
"port": "8090",
|
||||||
"secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise",
|
"secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise",
|
||||||
|
"times_on_touchscreen": True,
|
||||||
|
"photos_on_touchscreen": True,
|
||||||
|
"touchscreen": True,
|
||||||
|
"picure_height": 200,
|
||||||
|
"button_height": 300,
|
||||||
"holidays": { }
|
"holidays": { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
52
homepage.py
52
homepage.py
@ -1,7 +1,9 @@
|
|||||||
# Zeiterfassung
|
# Zeiterfassung
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from nicegui import ui, app
|
from nicegui import ui, app, Client
|
||||||
|
from nicegui.page import page
|
||||||
|
|
||||||
|
|
||||||
from users import *
|
from users import *
|
||||||
from definitions import *
|
from definitions import *
|
||||||
@ -26,6 +28,12 @@ def homepage():
|
|||||||
pageheader(f"Willkommen, {current_user.fullname}")
|
pageheader(f"Willkommen, {current_user.fullname}")
|
||||||
|
|
||||||
today = datetime.datetime.now()
|
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
|
@ui.refreshable
|
||||||
def stamp_interface():
|
def stamp_interface():
|
||||||
@ -38,6 +46,16 @@ def homepage():
|
|||||||
|
|
||||||
with ui.grid(columns='20% auto 20%').classes('w-full justify-center'):
|
with ui.grid(columns='20% auto 20%').classes('w-full justify-center'):
|
||||||
ui.space()
|
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'):
|
with ui.grid(columns='1fr 1fr'):
|
||||||
if current_user.stamp_status() == status_in:
|
if current_user.stamp_status() == status_in:
|
||||||
bg_color = 'green'
|
bg_color = 'green'
|
||||||
@ -46,10 +64,8 @@ def homepage():
|
|||||||
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}')
|
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')
|
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')
|
out_button = ui.button("Ausstempeln", on_click=stamp_and_refresh).classes('bg-red')
|
||||||
|
time_toggle = ui.toggle({"day": "Tagesarbeitszeit", "total": "Gesamtzeit"}, value="day",
|
||||||
def update_timer():
|
on_change=update_timer).classes('w-full justify-center col-span-2')
|
||||||
time_in_total = time_so_far + int((datetime.datetime.now().timestamp() - current_user.get_worked_time(today.year, today.month, today.day)[1]))
|
|
||||||
working_hours.set_content(convert_seconds_to_hours(time_in_total))
|
|
||||||
|
|
||||||
working_timer = ui.timer(1.0, update_timer)
|
working_timer = ui.timer(1.0, update_timer)
|
||||||
working_timer.active = False
|
working_timer.active = False
|
||||||
@ -75,10 +91,10 @@ def homepage():
|
|||||||
|
|
||||||
binder_available_years = ValueBinder()
|
binder_available_years = ValueBinder()
|
||||||
|
|
||||||
binder_vacation = ValueBinder
|
binder_vacation = ValueBinder()
|
||||||
binder_vacation.value = False
|
binder_vacation.value = False
|
||||||
|
|
||||||
binder_absence = ValueBinder
|
binder_absence = ValueBinder()
|
||||||
binder_absence.value = False
|
binder_absence.value = False
|
||||||
|
|
||||||
def enable_month():
|
def enable_month():
|
||||||
@ -93,7 +109,6 @@ def homepage():
|
|||||||
month_month_select.enable()
|
month_month_select.enable()
|
||||||
|
|
||||||
with ui.grid(columns='1fr auto 1fr').classes('w-full justify-center'):
|
with ui.grid(columns='1fr auto 1fr').classes('w-full justify-center'):
|
||||||
|
|
||||||
ui.space()
|
ui.space()
|
||||||
with ui.expansion("Tagesnotiz", icon='o_description'):
|
with ui.expansion("Tagesnotiz", icon='o_description'):
|
||||||
with ui.grid(columns=2):
|
with ui.grid(columns=2):
|
||||||
@ -137,6 +152,9 @@ def homepage():
|
|||||||
def activate_vacation():
|
def activate_vacation():
|
||||||
binder_vacation.value = True
|
binder_vacation.value = True
|
||||||
|
|
||||||
|
def activate_absence():
|
||||||
|
binder_absence.value = True
|
||||||
|
|
||||||
with ui.grid(columns='1fr 1fr'):
|
with ui.grid(columns='1fr 1fr'):
|
||||||
|
|
||||||
ui.markdown("**Monatsübersicht:**").classes('col-span-2')
|
ui.markdown("**Monatsübersicht:**").classes('col-span-2')
|
||||||
@ -151,8 +169,8 @@ def homepage():
|
|||||||
vacation_select = ui.select(list(reversed(available_years)), on_change=activate_vacation)
|
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')
|
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.markdown("**Fehlzeitenübersicht**").classes('col-span-2')
|
||||||
absences_select = ui.select(list(reversed(available_years)))
|
absences_select = ui.select(list(reversed(available_years)), on_change=activate_absence)
|
||||||
absences_button = ui.button("Anzeigen").bind_enabled_from(binder_absence, 'value')
|
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')
|
ui.separator().classes('col-span-2')
|
||||||
|
|
||||||
def logout():
|
def logout():
|
||||||
@ -164,3 +182,17 @@ def homepage():
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
login_mask()
|
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)
|
54
main.py
54
main.py
@ -1,3 +1,4 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
# Zeiterfassung
|
# Zeiterfassung
|
||||||
|
|
||||||
from web_ui import *
|
from web_ui import *
|
||||||
@ -10,6 +11,18 @@ from api import *
|
|||||||
from homepage import *
|
from homepage import *
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
from web_ui import hash_password
|
||||||
|
|
||||||
|
|
||||||
|
class Commandline_Header:
|
||||||
|
message_string = f"{app_title} {app_version}"
|
||||||
|
underline = ""
|
||||||
|
for i in range(len(message_string)):
|
||||||
|
underline += "-"
|
||||||
|
print(message_string)
|
||||||
|
print(underline)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
||||||
@ -24,12 +37,7 @@ def main():
|
|||||||
|
|
||||||
def startup_message():
|
def startup_message():
|
||||||
|
|
||||||
message_string = f"{app_title} {app_version}"
|
Commandline_Header()
|
||||||
underline = ""
|
|
||||||
for i in range(len(message_string)):
|
|
||||||
underline += "-"
|
|
||||||
print(message_string)
|
|
||||||
print(underline)
|
|
||||||
|
|
||||||
url_string = ""
|
url_string = ""
|
||||||
for i in list(app.urls):
|
for i in list(app.urls):
|
||||||
@ -41,4 +49,38 @@ def main():
|
|||||||
ui.run(favicon="favicon.svg", port=port, storage_secret=secret, language='de-DE', show_welcome_message=False)
|
ui.run(favicon="favicon.svg", port=port, storage_secret=secret, language='de-DE', show_welcome_message=False)
|
||||||
|
|
||||||
if __name__ in ("__main__", "__mp_main__"):
|
if __name__ in ("__main__", "__mp_main__"):
|
||||||
|
parser = argparse.ArgumentParser(description=f'{app_title} {app_version}')
|
||||||
|
parser.add_argument('--admin-access', help='Zugangsdaten für Administrator einstellen', action="store_true")
|
||||||
|
args = parser.parse_args()
|
||||||
|
if args.admin_access:
|
||||||
|
Commandline_Header()
|
||||||
|
print("Lade Administrationseinstellungen")
|
||||||
|
admin_settings = load_adminsettings()
|
||||||
|
print("Geben Sie den neuen Benutzernamen für den Administrationsbenutzer an:")
|
||||||
|
admin_user = input()
|
||||||
|
if admin_user == "":
|
||||||
|
print("Ungültiger Benutzername. Breche ab.")
|
||||||
|
quit()
|
||||||
|
print("Geben Sie das neue Passwort für den Administrationsbenutzer ein:")
|
||||||
|
admin_password = input()
|
||||||
|
if admin_password == "":
|
||||||
|
print("Ungültiges Passwort. Breche ab.")
|
||||||
|
quit()
|
||||||
|
print("Sie haben folgende Informationen eingegeben.")
|
||||||
|
print(f"Benutzername: {admin_user}")
|
||||||
|
print(f"Passwort: {admin_password}")
|
||||||
|
print("Sollen diese Einstellungen übernommen werden? j=Ja")
|
||||||
|
question = input()
|
||||||
|
if question == "j":
|
||||||
|
admin_settings["admin_user"] = admin_user
|
||||||
|
admin_settings["admin_password"] = hash_password(admin_password)
|
||||||
|
json_dict = json.dumps(admin_settings, indent=4)
|
||||||
|
with open(os.path.join(scriptpath, usersettingsfilename), "w") as outputfile:
|
||||||
|
outputfile.write(json_dict)
|
||||||
|
print("Daten geschrieben")
|
||||||
|
quit()
|
||||||
|
else:
|
||||||
|
print("Breche ab.")
|
||||||
|
quit()
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
@ -3,6 +3,11 @@
|
|||||||
"admin_password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",
|
"admin_password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",
|
||||||
"port": "8090",
|
"port": "8090",
|
||||||
"secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise",
|
"secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise",
|
||||||
|
"touchscreen": true,
|
||||||
|
"times_on_touchscreen": true,
|
||||||
|
"photos_on_touchscreen": true,
|
||||||
|
"picture_height": "100",
|
||||||
|
"button_height": "120",
|
||||||
"holidays": {
|
"holidays": {
|
||||||
"2025-01-01": "Neujahr",
|
"2025-01-01": "Neujahr",
|
||||||
"2025-04-18": "Karfreitag",
|
"2025-04-18": "Karfreitag",
|
||||||
|
@ -14,6 +14,8 @@ import locale
|
|||||||
@ui.page('/touchscreen')
|
@ui.page('/touchscreen')
|
||||||
def page_touchscreen():
|
def page_touchscreen():
|
||||||
|
|
||||||
|
if load_adminsettings()["touchscreen"]:
|
||||||
|
|
||||||
def button_click(name):
|
def button_click(name):
|
||||||
#nonlocal buttons
|
#nonlocal buttons
|
||||||
current_user = user(name)
|
current_user = user(name)
|
||||||
@ -26,8 +28,10 @@ def page_touchscreen():
|
|||||||
# ui.notify(status_out)
|
# ui.notify(status_out)
|
||||||
user_buttons.refresh()
|
user_buttons.refresh()
|
||||||
|
|
||||||
pageheader("Bitte User auswählen:")
|
pageheader("Stempeluhr")
|
||||||
|
ui.page_title("Stempeluhr")
|
||||||
|
|
||||||
|
admin_settings = load_adminsettings()
|
||||||
userlist = list_users()
|
userlist = list_users()
|
||||||
number_of_users = len(userlist)
|
number_of_users = len(userlist)
|
||||||
buttons = { }
|
buttons = { }
|
||||||
@ -39,22 +43,43 @@ def page_touchscreen():
|
|||||||
else:
|
else:
|
||||||
number_of_columns = number_of_users
|
number_of_columns = number_of_users
|
||||||
|
|
||||||
with ui.grid(columns=number_of_columns):
|
with ui.grid(columns=number_of_columns).classes('w-full center'):
|
||||||
for name in userlist:
|
for name in userlist:
|
||||||
current_user = user(name)
|
current_user = user(name)
|
||||||
current_button = ui.button(on_click=lambda name=name: button_click(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:
|
with current_button:
|
||||||
|
if admin_settings["photos_on_touchscreen"]:
|
||||||
try:
|
try:
|
||||||
with open(current_user.photofile, 'r') as file:
|
with open(current_user.photofile, 'r') as file:
|
||||||
pass
|
pass
|
||||||
file.close()
|
file.close()
|
||||||
ui.image(current_user.photofile)
|
ui.image(current_user.photofile).classes(f'max-h-[{admin_settings["picture_height"]}px]').props('fit=scale-down')
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
ui.label(current_user.fullname)
|
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')
|
||||||
if current_user.stamp_status() == status_in:
|
if current_user.stamp_status() == status_in:
|
||||||
current_button.props('color=green')
|
current_button.props('color=green')
|
||||||
else:
|
else:
|
||||||
current_button.props('color=red')
|
current_button.props('color=red')
|
||||||
buttons[name] = current_button
|
buttons[name] = current_button
|
||||||
user_buttons()
|
user_buttons()
|
||||||
|
|
||||||
|
else:
|
||||||
|
pageheader("Interface deaktiviert")
|
63
users.py
63
users.py
@ -17,9 +17,9 @@ from definitions import userfolder, scriptpath, usersettingsfilename, photofilen
|
|||||||
|
|
||||||
class user:
|
class user:
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.userfolder = f"{scriptpath}/{userfolder}/{name}"
|
self.userfolder = os.path.join(scriptpath, userfolder, name)
|
||||||
self.settingsfile = f"{self.userfolder}/{usersettingsfilename}"
|
self.settingsfile = os.path.join(self.userfolder, usersettingsfilename)
|
||||||
self.photofile = f"{self.userfolder}/{photofilename}"
|
self.photofile = os.path.join(self.userfolder, photofilename)
|
||||||
|
|
||||||
# Stammdaten einlesen
|
# Stammdaten einlesen
|
||||||
try:
|
try:
|
||||||
@ -43,7 +43,7 @@ class user:
|
|||||||
else:
|
else:
|
||||||
year = str(datetime.datetime.fromtimestamp(time_stamp).year)
|
year = str(datetime.datetime.fromtimestamp(time_stamp).year)
|
||||||
month = str(datetime.datetime.fromtimestamp(time_stamp).month)
|
month = str(datetime.datetime.fromtimestamp(time_stamp).month)
|
||||||
completepath = f"{self.userfolder}/{year}-{month}"
|
completepath = os.path.join(self.userfolder, f"{year}-{month}")
|
||||||
return completepath
|
return completepath
|
||||||
|
|
||||||
def timestamp(self, stamptime=-1):
|
def timestamp(self, stamptime=-1):
|
||||||
@ -87,7 +87,7 @@ class user:
|
|||||||
# Zähle die Zeilen
|
# Zähle die Zeilen
|
||||||
lines = file.readlines()
|
lines = file.readlines()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print(f"Die Datei {self.get_stamp_file()} wurde nicht gefunden.")
|
print(f"Die Datei {self.get_stamp_file()}.txt wurde nicht gefunden.")
|
||||||
print("Lege die Datei an.")
|
print("Lege die Datei an.")
|
||||||
with open(f'{self.get_stamp_file()}.txt', 'w') as file:
|
with open(f'{self.get_stamp_file()}.txt', 'w') as file:
|
||||||
file.write("")
|
file.write("")
|
||||||
@ -95,7 +95,7 @@ class user:
|
|||||||
# Zähle die Zeilen
|
# Zähle die Zeilen
|
||||||
lines = file.readlines()
|
lines = file.readlines()
|
||||||
if len(lines)== 0:
|
if len(lines)== 0:
|
||||||
print(f"Keine Einträge")
|
pass
|
||||||
elif len(lines) % 2 == 0:
|
elif len(lines) % 2 == 0:
|
||||||
return status_out
|
return status_out
|
||||||
else:
|
else:
|
||||||
@ -132,10 +132,10 @@ class user:
|
|||||||
outputfile.write(json_dict)
|
outputfile.write(json_dict)
|
||||||
|
|
||||||
pathcheck = self.userfolder
|
pathcheck = self.userfolder
|
||||||
pathcheck = pathcheck.removeprefix(f"{scriptpath}/{userfolder}/")
|
pathcheck = pathcheck.removeprefix(os.path.join(scriptpath, userfolder))
|
||||||
|
|
||||||
if pathcheck != self.username:
|
if pathcheck != self.username:
|
||||||
os.rename(self.userfolder, f"{scriptpath}/{userfolder}/{self.username}")
|
os.rename(self.userfolder, os.path.join(scriptpath, userfolder, self.username))
|
||||||
|
|
||||||
def del_user(self):
|
def del_user(self):
|
||||||
shutil.rmtree(self.userfolder)
|
shutil.rmtree(self.userfolder)
|
||||||
@ -204,7 +204,7 @@ class user:
|
|||||||
|
|
||||||
def get_timestamps(self, year, month):
|
def get_timestamps(self, year, month):
|
||||||
try:
|
try:
|
||||||
with open(f"{self.userfolder}/{year}-{month}.txt", "r") as file:
|
with open(os.path.join(self.userfolder, f"{year}-{month}.txt"), "r") as file:
|
||||||
timestamps = file.readlines()
|
timestamps = file.readlines()
|
||||||
timestamps.sort()
|
timestamps.sort()
|
||||||
return timestamps
|
return timestamps
|
||||||
@ -218,14 +218,14 @@ class user:
|
|||||||
|
|
||||||
def get_archive_status(self, year, month):
|
def get_archive_status(self, year, month):
|
||||||
try:
|
try:
|
||||||
with open(f"{self.userfolder}/{year}-{month}.json", 'r') as json_file:
|
with open(os.path.join(self.userfolder, f"{year}-{month}.json"), 'r') as json_file:
|
||||||
data = json.load(json_file)
|
data = json.load(json_file)
|
||||||
return data["archived"]
|
return data["archived"]
|
||||||
except:
|
except:
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
def archiving_validity_check(self, year: int, month: int):
|
def archiving_validity_check(self, year: int, month: int):
|
||||||
timestampfilename = f"{self.userfolder}/{year}-{month}.txt"
|
timestampfilename = os.path.join(self.userfolder, f"{year}-{month}.txt")
|
||||||
try:
|
try:
|
||||||
with open(timestampfilename) as timestampfile:
|
with open(timestampfilename) as timestampfile:
|
||||||
timestamps = timestampfile.readlines()
|
timestamps = timestampfile.readlines()
|
||||||
@ -246,7 +246,7 @@ class user:
|
|||||||
|
|
||||||
def archive_hours(self, year, month, overtime: int):
|
def archive_hours(self, year, month, overtime: int):
|
||||||
|
|
||||||
filename = f"{self.userfolder}/{year}-{month}.json"
|
filename = os.path.join(self.userfolder, f"{year}-{month}.json")
|
||||||
with open(filename, 'r') as json_file:
|
with open(filename, 'r') as json_file:
|
||||||
data = json.load(json_file)
|
data = json.load(json_file)
|
||||||
data["archived"] = 1
|
data["archived"] = 1
|
||||||
@ -258,7 +258,7 @@ class user:
|
|||||||
outputfile.write(json_dict)
|
outputfile.write(json_dict)
|
||||||
# Dateien auf readonly setzen
|
# Dateien auf readonly setzen
|
||||||
os.chmod(filename, S_IREAD)
|
os.chmod(filename, S_IREAD)
|
||||||
filename_txt = f"{self.userfolder}/{year}-{month}.txt"
|
filename_txt = os.path.join(self.userfolder, f"{year}-{month}.txt")
|
||||||
os.chmod(filename_txt, S_IREAD)
|
os.chmod(filename_txt, S_IREAD)
|
||||||
|
|
||||||
def get_last_months_overtime(self, year, month):
|
def get_last_months_overtime(self, year, month):
|
||||||
@ -268,7 +268,7 @@ class user:
|
|||||||
month = str(12)
|
month = str(12)
|
||||||
else:
|
else:
|
||||||
month = str(int(month) - 1)
|
month = str(int(month) - 1)
|
||||||
with open(f"{self.userfolder}/{year}-{month}.json", "r") as json_file:
|
with open(os.path.join(self.userfolder, f"{year}-{month}.json"), "r") as json_file:
|
||||||
json_data = json.load(json_file)
|
json_data = json.load(json_file)
|
||||||
|
|
||||||
if json_data["archived"] == 1:
|
if json_data["archived"] == 1:
|
||||||
@ -281,7 +281,7 @@ class user:
|
|||||||
|
|
||||||
def get_absence(self, year, month):
|
def get_absence(self, year, month):
|
||||||
try:
|
try:
|
||||||
with open(f"{self.userfolder}/{year}-{month}.json", "r") as json_file:
|
with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "r") as json_file:
|
||||||
json_data = json.load(json_file)
|
json_data = json.load(json_file)
|
||||||
absence = json_data["absence"]
|
absence = json_data["absence"]
|
||||||
return absence
|
return absence
|
||||||
@ -290,7 +290,7 @@ class user:
|
|||||||
|
|
||||||
def get_day_notes(self, year, month, day):
|
def get_day_notes(self, year, month, day):
|
||||||
try:
|
try:
|
||||||
with open(f"{self.userfolder}/{int(year)}-{int(month)}.json", "r") as json_file:
|
with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "r") as json_file:
|
||||||
json_data = json.load(json_file)
|
json_data = json.load(json_file)
|
||||||
day_note = json_data["notes"][str(day)]
|
day_note = json_data["notes"][str(day)]
|
||||||
return day_note
|
return day_note
|
||||||
@ -298,7 +298,7 @@ class user:
|
|||||||
return { }
|
return { }
|
||||||
|
|
||||||
def write_notes(self, year, month, day, note_dict):
|
def write_notes(self, year, month, day, note_dict):
|
||||||
with open(f"{self.userfolder}/{int(year)}-{int(month)}.json", "r") as json_file:
|
with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "r") as json_file:
|
||||||
json_data = json.load(json_file)
|
json_data = json.load(json_file)
|
||||||
if len(note_dict) == 1:
|
if len(note_dict) == 1:
|
||||||
json_data["notes"][str(day)] = { }
|
json_data["notes"][str(day)] = { }
|
||||||
@ -309,16 +309,16 @@ class user:
|
|||||||
json_data["notes"][str(day)] = note_dict
|
json_data["notes"][str(day)] = note_dict
|
||||||
|
|
||||||
json_output = json.dumps(json_data, indent=4)
|
json_output = json.dumps(json_data, indent=4)
|
||||||
with open(f"{self.userfolder}/{int(year)}-{int(month)}.json", "w") as json_file:
|
with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "w") as json_file:
|
||||||
json_file.write(json_output)
|
json_file.write(json_output)
|
||||||
|
|
||||||
|
|
||||||
def update_absence(self, year, month, day, absence_type):
|
def update_absence(self, year, month, day, absence_type):
|
||||||
try:
|
try:
|
||||||
with open(f"{self.userfolder}/{int(year)}-{int(month)}.json", "r") as json_file:
|
with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "r") as json_file:
|
||||||
json_data = json.load(json_file)
|
json_data = json.load(json_file)
|
||||||
except:
|
except:
|
||||||
with open(f"{self.userfolder}/{year}-{month}.json", "w") as json_file:
|
with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "w") as json_file:
|
||||||
json_data = { }
|
json_data = { }
|
||||||
json_data["archived"] = 0
|
json_data["archived"] = 0
|
||||||
json_data["overtime"] = 0
|
json_data["overtime"] = 0
|
||||||
@ -330,17 +330,17 @@ class user:
|
|||||||
json_data.update({ "absence": { str(int(day)): absence_type}})
|
json_data.update({ "absence": { str(int(day)): absence_type}})
|
||||||
json_dict = json.dumps(json_data, indent=4)
|
json_dict = json.dumps(json_data, indent=4)
|
||||||
|
|
||||||
with open(f"{self.userfolder}/{int(year)}-{int(month)}.json", "w") as json_file:
|
with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "w") as json_file:
|
||||||
json_file.write(json_dict)
|
json_file.write(json_dict)
|
||||||
|
|
||||||
def del_absence(self, year, month, day):
|
def del_absence(self, year, month, day):
|
||||||
with open(f"{self.userfolder}/{int(year)}-{int(month)}.json", "r") as json_file:
|
with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "r") as json_file:
|
||||||
json_data = json.load(json_file)
|
json_data = json.load(json_file)
|
||||||
|
|
||||||
del json_data["absence"][str(day)]
|
del json_data["absence"][str(day)]
|
||||||
json_dict = json.dumps(json_data, indent=4)
|
json_dict = json.dumps(json_data, indent=4)
|
||||||
|
|
||||||
with open(f"{self.userfolder}/{int(year)}-{int(month)}.json", "w") as json_file:
|
with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "w") as json_file:
|
||||||
json_file.write(json_dict)
|
json_file.write(json_dict)
|
||||||
|
|
||||||
def get_day_workhours(self, year, month, day):
|
def get_day_workhours(self, year, month, day):
|
||||||
@ -416,10 +416,10 @@ class user:
|
|||||||
def delete_photo(self):
|
def delete_photo(self):
|
||||||
os.remove(self.photofile)
|
os.remove(self.photofile)
|
||||||
|
|
||||||
def get_worked_time(self, year, month, day):
|
def get_day_timestamps(self, year=datetime.datetime.now().year, month=datetime.datetime.now().month, day=datetime.datetime.now().day):
|
||||||
timestamps = self.get_timestamps(year, month)
|
timestamps = self.get_timestamps(year, month)
|
||||||
check_day_dt = datetime.datetime(year, month, day)
|
check_day_dt = datetime.datetime(year, month, day)
|
||||||
todays_timestamps = [ ]
|
todays_timestamps = []
|
||||||
|
|
||||||
for i in timestamps:
|
for i in timestamps:
|
||||||
i_dt = datetime.datetime.fromtimestamp(int(i))
|
i_dt = datetime.datetime.fromtimestamp(int(i))
|
||||||
@ -427,6 +427,13 @@ class user:
|
|||||||
todays_timestamps.append(int(i))
|
todays_timestamps.append(int(i))
|
||||||
|
|
||||||
todays_timestamps.sort()
|
todays_timestamps.sort()
|
||||||
|
|
||||||
|
return todays_timestamps
|
||||||
|
|
||||||
|
def get_worked_time(self, year=datetime.datetime.now().year, month=datetime.datetime.now().month, day=datetime.datetime.now().day):
|
||||||
|
|
||||||
|
todays_timestamps = self.get_day_timestamps(year, month, day)
|
||||||
|
|
||||||
if len(todays_timestamps) % 2 == 0:
|
if len(todays_timestamps) % 2 == 0:
|
||||||
workrange = len(todays_timestamps)
|
workrange = len(todays_timestamps)
|
||||||
in_time_stamp = -1
|
in_time_stamp = -1
|
||||||
@ -460,8 +467,8 @@ def list_users():
|
|||||||
def new_user(username: str):
|
def new_user(username: str):
|
||||||
if not os.path.exists(userfolder):
|
if not os.path.exists(userfolder):
|
||||||
os.makedirs(userfolder)
|
os.makedirs(userfolder)
|
||||||
if not os.path.exists(f"{userfolder}/{username}"):
|
if not os.path.exists(os.path.join(userfolder, username)):
|
||||||
os.makedirs(f"{userfolder}/{username}")
|
os.makedirs(os.path.join(userfolder, username))
|
||||||
start_date_dt = datetime.datetime.now()
|
start_date_dt = datetime.datetime.now()
|
||||||
start_date = start_date_dt.strftime("%Y-%m-%d")
|
start_date = start_date_dt.strftime("%Y-%m-%d")
|
||||||
settings_to_write = standard_usersettings
|
settings_to_write = standard_usersettings
|
||||||
@ -482,7 +489,7 @@ def new_user(username: str):
|
|||||||
# Admineinstellungen auslesen
|
# Admineinstellungen auslesen
|
||||||
def load_adminsettings():
|
def load_adminsettings():
|
||||||
# Settingsdatei einlesen
|
# Settingsdatei einlesen
|
||||||
settings_filename = f"{scriptpath}/{usersettingsfilename}"
|
settings_filename = os.path.join(scriptpath, usersettingsfilename)
|
||||||
if not os.path.exists(settings_filename):
|
if not os.path.exists(settings_filename):
|
||||||
print("Keine Einstellungsdatei gefunden. Lege Standarddatei an.")
|
print("Keine Einstellungsdatei gefunden. Lege Standarddatei an.")
|
||||||
with open(settings_filename, 'w') as json_file:
|
with open(settings_filename, 'w') as json_file:
|
||||||
|
0
users/filler2/2025-5.txt
Normal file
0
users/filler2/2025-5.txt
Normal file
18
users/filler2/settings.json
Normal file
18
users/filler2/settings.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"username": "filler2",
|
||||||
|
"fullname": "filler2",
|
||||||
|
"password": "37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f",
|
||||||
|
"api_key": "0f36286bf8c96de1922ab41e2682ba5a81793525",
|
||||||
|
"workhours": {
|
||||||
|
"2025-05-16": {
|
||||||
|
"1": 0,
|
||||||
|
"2": 0,
|
||||||
|
"3": 0,
|
||||||
|
"4": 0,
|
||||||
|
"5": 0,
|
||||||
|
"6": 0,
|
||||||
|
"7": 0,
|
||||||
|
"vacation": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
users/filler3/2025-5.json
Normal file
4
users/filler3/2025-5.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"archived": 0,
|
||||||
|
"total_hours": 0
|
||||||
|
}
|
2
users/filler3/2025-5.txt
Normal file
2
users/filler3/2025-5.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
1747391900
|
||||||
|
1747391907
|
18
users/filler3/settings.json
Normal file
18
users/filler3/settings.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"username": "filler3",
|
||||||
|
"fullname": "filler3",
|
||||||
|
"password": "37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f",
|
||||||
|
"api_key": "9e3f37809cd898a3db340c453df53bd0793a99fa",
|
||||||
|
"workhours": {
|
||||||
|
"2025-05-16": {
|
||||||
|
"1": 0,
|
||||||
|
"2": 0,
|
||||||
|
"3": 0,
|
||||||
|
"4": 0,
|
||||||
|
"5": 0,
|
||||||
|
"6": 0,
|
||||||
|
"7": 0,
|
||||||
|
"vacation": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
0
users/filler4/2025-5.txt
Normal file
0
users/filler4/2025-5.txt
Normal file
18
users/filler4/settings.json
Normal file
18
users/filler4/settings.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"username": "filler4",
|
||||||
|
"fullname": "filler4",
|
||||||
|
"password": "37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f",
|
||||||
|
"api_key": "614e31aab9fcf1373558f100cb2c7a9918349eec",
|
||||||
|
"workhours": {
|
||||||
|
"2025-05-16": {
|
||||||
|
"1": 0,
|
||||||
|
"2": 0,
|
||||||
|
"3": 0,
|
||||||
|
"4": 0,
|
||||||
|
"5": 0,
|
||||||
|
"6": 0,
|
||||||
|
"7": 0,
|
||||||
|
"vacation": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
0
users/filler5/2025-5.txt
Normal file
0
users/filler5/2025-5.txt
Normal file
18
users/filler5/settings.json
Normal file
18
users/filler5/settings.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"username": "filler5",
|
||||||
|
"fullname": "filler5",
|
||||||
|
"password": "37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f",
|
||||||
|
"api_key": "ad32682beb4e19f78efc1bdae259aee3ccbf9883",
|
||||||
|
"workhours": {
|
||||||
|
"2025-05-16": {
|
||||||
|
"1": 0,
|
||||||
|
"2": 0,
|
||||||
|
"3": 0,
|
||||||
|
"4": 0,
|
||||||
|
"5": 0,
|
||||||
|
"6": 0,
|
||||||
|
"7": 0,
|
||||||
|
"vacation": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
0
users/filler6/2025-5.txt
Normal file
0
users/filler6/2025-5.txt
Normal file
18
users/filler6/settings.json
Normal file
18
users/filler6/settings.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"username": "filler6",
|
||||||
|
"fullname": "filler6",
|
||||||
|
"password": "37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f",
|
||||||
|
"api_key": "68d974e4ed516795d48d5cb8b7dc8b8ca4144a9b",
|
||||||
|
"workhours": {
|
||||||
|
"2025-05-16": {
|
||||||
|
"1": 0,
|
||||||
|
"2": 0,
|
||||||
|
"3": 0,
|
||||||
|
"4": 0,
|
||||||
|
"5": 0,
|
||||||
|
"6": 0,
|
||||||
|
"7": 0,
|
||||||
|
"vacation": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,3 +12,21 @@
|
|||||||
1747214813
|
1747214813
|
||||||
1747216800
|
1747216800
|
||||||
1747220619
|
1747220619
|
||||||
|
1747301302
|
||||||
|
1747301459
|
||||||
|
1747302876
|
||||||
|
1747302887
|
||||||
|
1747302889
|
||||||
|
1747302897
|
||||||
|
1747386098
|
||||||
|
1747386110
|
||||||
|
1747387148
|
||||||
|
1747387150
|
||||||
|
1747387501
|
||||||
|
1747387508
|
||||||
|
1747387633
|
||||||
|
1747387635
|
||||||
|
1747387761
|
||||||
|
1747388239
|
||||||
|
1747388242
|
||||||
|
1747388615
|
||||||
|
4
users/testuser10/2025-5.json
Normal file
4
users/testuser10/2025-5.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"archived": 0,
|
||||||
|
"total_hours": 0
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
1747387168
|
||||||
|
1747387171
|
||||||
|
1747388261
|
||||||
|
1747388617
|
@ -1,2 +1,6 @@
|
|||||||
1746385111
|
1746385111
|
||||||
1746385118
|
1746385118
|
||||||
|
1747388255
|
||||||
|
1747388619
|
||||||
|
1747391536
|
||||||
|
1747391567
|
||||||
|
Loading…
x
Reference in New Issue
Block a user