Zusätzliche Anzeigefunktionen für Touchscreen
Einstellungen für Admininterface, für Touchscreen Elemente
This commit is contained in:
parent
67e68ffa2c
commit
859d55c99a
129
admin.py
129
admin.py
@ -1,9 +1,11 @@
|
||||
from datetime import datetime
|
||||
|
||||
import dateutil.easter
|
||||
from PIL.SpiderImagePlugin import isInt
|
||||
from dateutil.easter import *
|
||||
|
||||
from nicegui import ui, app, events
|
||||
from nicegui.html import button
|
||||
|
||||
from users import *
|
||||
from definitions import *
|
||||
@ -596,47 +598,89 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
button_update.move(timetable_header)
|
||||
|
||||
with ui.tab_panel(settings):
|
||||
with ui.card():
|
||||
ui.markdown("**Administrationsbenutzer:**")
|
||||
with ui.grid(columns=2):
|
||||
def save_admin_settings():
|
||||
output_dict = { }
|
||||
output_dict["admin_user"] = admin_user.value
|
||||
if admin_password.value != "":
|
||||
output_dict["admin_password"] = hash_password(admin_password.value)
|
||||
else:
|
||||
output_dict["admin_password"] = data["admin_password"]
|
||||
output_dict["port"] = port.value
|
||||
output_dict["secret"] = secret
|
||||
output_dict["holidays"] = data["holidays"]
|
||||
json_dict = json.dumps(output_dict, indent=4)
|
||||
with open(f"{scriptpath}/{usersettingsfilename}", "w") as outputfile:
|
||||
outputfile.write(json_dict)
|
||||
if int(old_port) != int(port.value):
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
ui.markdown("Damit die Porteinstellungen wirksam werden, muss der Server neu gestartet werden.")
|
||||
ui.button("OK", on_click=lambda: dialog.close())
|
||||
dialog.open()
|
||||
ui.notify("Einstellungen gespeichert")
|
||||
timetable.refresh()
|
||||
with ui.grid(columns='auto auto'):
|
||||
with ui.card():
|
||||
ui.markdown("**Administrationsbenutzer:**")
|
||||
with ui.grid(columns=2):
|
||||
def save_admin_settings():
|
||||
output_dict = { }
|
||||
output_dict["admin_user"] = admin_user.value
|
||||
if admin_password.value != "":
|
||||
output_dict["admin_password"] = hash_password(admin_password.value)
|
||||
else:
|
||||
output_dict["admin_password"] = data["admin_password"]
|
||||
output_dict["port"] = port.value
|
||||
output_dict["secret"] = secret
|
||||
output_dict["times_on_touchscreen"] = timestamp_switch.value
|
||||
output_dict["photos_on_touchscreen"] = photo_switch.value
|
||||
output_dict["holidays"] = data["holidays"]
|
||||
json_dict = json.dumps(output_dict, indent=4)
|
||||
with open(f"{scriptpath}/{usersettingsfilename}", "w") as outputfile:
|
||||
outputfile.write(json_dict)
|
||||
if int(old_port) != int(port.value):
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
ui.markdown("Damit die Porteinstellungen wirksam werden, muss der Server neu gestartet werden.")
|
||||
ui.button("OK", on_click=lambda: dialog.close())
|
||||
dialog.open()
|
||||
ui.notify("Einstellungen gespeichert")
|
||||
timetable.refresh()
|
||||
|
||||
ui.markdown("Benutzername des Adminstrators")
|
||||
admin_user = ui.input().tooltip("Geben Sie hier den Benutzernamen für den Adminstationsnutzer ein")
|
||||
admin_user.value = data["admin_user"]
|
||||
ui.markdown("Passwort des Administrators")
|
||||
admin_password = ui.input(password=True).tooltip("Geben Sie hier das Passwort für den Administationsnutzer ein. Merken Sie sich dieses Passwort gut. Es kann nicht über das Webinterface zurückgesetzt werden.")
|
||||
|
||||
secret = data["secret"]
|
||||
|
||||
with ui.card():
|
||||
ui.markdown("**Systemeinstellungen:**")
|
||||
with ui.grid(columns=2):
|
||||
def check_is_number(number):
|
||||
try:
|
||||
number = int(number)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
ui.markdown("Port:")
|
||||
port = ui.input(validation={"Nur ganzzahlige Portnummern erlaubt": lambda value: check_is_number(value),
|
||||
"Portnummer zu klein": lambda value: len(value)>=2}).tooltip("Geben Sie hier die Portnummer ein, unter der die Zeiterfassung erreichbar ist.").props('size=5')
|
||||
old_port = data["port"]
|
||||
port.value = old_port
|
||||
|
||||
|
||||
ui.markdown("Benutzername des Adminstrators")
|
||||
admin_user = ui.input()
|
||||
admin_user.value = data["admin_user"]
|
||||
ui.markdown("Passwort des Administrators")
|
||||
admin_password = ui.input(password=True)
|
||||
|
||||
secret = data["secret"]
|
||||
with ui.card():
|
||||
ui.markdown("**Einstellungen für das Stempelterminal:**")
|
||||
with ui.column():
|
||||
timestamp_switch = ui.switch("Stempelzeiten anzeigen")
|
||||
photo_switch = ui.switch("Fotos anzeigen")
|
||||
timestamp_switch.value = bool(data["times_on_touchscreen"])
|
||||
with ui.row():
|
||||
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():
|
||||
ui.markdown("Maximale 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
|
||||
|
||||
with ui.card():
|
||||
ui.markdown("**Systemeinstellungen:**")
|
||||
with ui.grid(columns=2):
|
||||
|
||||
ui.markdown("Port:")
|
||||
port = ui.input()
|
||||
old_port = data["port"]
|
||||
port.value = old_port
|
||||
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)
|
||||
ui.markdown('px')
|
||||
|
||||
def holiday_section():
|
||||
with ui.card():
|
||||
@ -825,9 +869,9 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
with ui.grid(columns='auto auto'):
|
||||
ui.space()
|
||||
with ui.row():
|
||||
ui.button("Gesetzliche Feiertage eintragen", on_click=defined_holidays)
|
||||
ui.button("Eigener Eintrag", on_click=new_holiday_entry)
|
||||
ui.button("Zurücksetzen", icon="undo", on_click=reset_holidays).bind_visibility_from(reset_visibility, 'value').classes('bg-red')
|
||||
ui.button("Gesetzliche Feiertage eintragen", on_click=defined_holidays).tooltip("Hier können Sie automatisiert gesetzliche Feiertage in Deutschland eintragen.")
|
||||
ui.button("Eigener Eintrag", on_click=new_holiday_entry).tooltip("Hier können Sie einen eigenen Feiertag definieren.")
|
||||
ui.button("Zurücksetzen", icon="undo", on_click=reset_holidays).bind_visibility_from(reset_visibility, 'value').classes('bg-red').tooltip("Hier können Sie ungespeicherte Änderungen zurücknehmen.")
|
||||
|
||||
ui.separator().classes('col-span-2')
|
||||
for year_entry in year_list:
|
||||
@ -840,7 +884,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
|
||||
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):
|
||||
ui.markdown("###Benutzerverwaltung")
|
||||
@ -1026,7 +1070,8 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
ui.markdown("Geben Sie den Benutzernamen für das neue Konto an:")
|
||||
user_name_input = ui.input(label="Benutzername", validation={'Leerer Benutzername nicht erlaubt': lambda value: len(value) != 0,
|
||||
'Leerzeichen im Benutzername nicht erlaubt': lambda value: " " not in value})
|
||||
'Leerzeichen im Benutzername nicht erlaubt': lambda value: " " not in value,
|
||||
'Benutzername schon vergeben': lambda value: value not in userlist}).on('keypress.enter', create_new_user)
|
||||
with ui.row():
|
||||
ui.button("OK", on_click=create_new_user)
|
||||
ui.button("Abbrechen", on_click=dialog.close)
|
||||
|
@ -26,6 +26,10 @@ standard_adminsettings = { "admin_user": "admin",
|
||||
"admin_password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",
|
||||
"port": "8090",
|
||||
"secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise",
|
||||
"times_on_touchscreen": True,
|
||||
"photos_on_touchscreen": True,
|
||||
"picure_height": 200,
|
||||
"button_height": 300,
|
||||
"holidays": { }
|
||||
}
|
||||
|
||||
|
20
homepage.py
20
homepage.py
@ -1,7 +1,9 @@
|
||||
# Zeiterfassung
|
||||
import datetime
|
||||
|
||||
from nicegui import ui, app
|
||||
from nicegui import ui, app, Client
|
||||
from nicegui.page import page
|
||||
|
||||
|
||||
from users import *
|
||||
from definitions import *
|
||||
@ -179,4 +181,18 @@ def homepage():
|
||||
ui.space()
|
||||
|
||||
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)
|
@ -3,6 +3,10 @@
|
||||
"admin_password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",
|
||||
"port": "8090",
|
||||
"secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise",
|
||||
"times_on_touchscreen": true,
|
||||
"photos_on_touchscreen": true,
|
||||
"button_height": 300,
|
||||
"picture_height": 200,
|
||||
"holidays": {
|
||||
"2025-01-01": "Neujahr",
|
||||
"2025-04-18": "Karfreitag",
|
||||
|
@ -26,8 +26,10 @@ def page_touchscreen():
|
||||
# ui.notify(status_out)
|
||||
user_buttons.refresh()
|
||||
|
||||
pageheader("Bitte User auswählen:")
|
||||
pageheader("Stempeluhr")
|
||||
ui.page_title("Stempeluhr")
|
||||
|
||||
admin_settings = load_adminsettings()
|
||||
userlist = list_users()
|
||||
number_of_users = len(userlist)
|
||||
buttons = { }
|
||||
@ -39,22 +41,40 @@ def page_touchscreen():
|
||||
else:
|
||||
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:
|
||||
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('w-md h-full min-h-[250px]')
|
||||
with current_button:
|
||||
try:
|
||||
with open(current_user.photofile, 'r') as file:
|
||||
if admin_settings["photos_on_touchscreen"]:
|
||||
try:
|
||||
with open(current_user.photofile, 'r') as file:
|
||||
pass
|
||||
file.close()
|
||||
ui.image(current_user.photofile).classes('max-h-[200px]').props('fit=scale-down')
|
||||
except:
|
||||
pass
|
||||
file.close()
|
||||
ui.image(current_user.photofile)
|
||||
except:
|
||||
pass
|
||||
ui.label(current_user.fullname)
|
||||
if current_user.stamp_status() == status_in:
|
||||
current_button.props('color=green')
|
||||
else:
|
||||
current_button.props('color=red')
|
||||
buttons[name] = current_button
|
||||
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:
|
||||
current_button.props('color=green')
|
||||
else:
|
||||
current_button.props('color=red')
|
||||
buttons[name] = current_button
|
||||
user_buttons()
|
15
users.py
15
users.py
@ -416,10 +416,10 @@ class user:
|
||||
def delete_photo(self):
|
||||
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)
|
||||
check_day_dt = datetime.datetime(year, month, day)
|
||||
todays_timestamps = [ ]
|
||||
todays_timestamps = []
|
||||
|
||||
for i in timestamps:
|
||||
i_dt = datetime.datetime.fromtimestamp(int(i))
|
||||
@ -427,6 +427,13 @@ class user:
|
||||
todays_timestamps.append(int(i))
|
||||
|
||||
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:
|
||||
workrange = len(todays_timestamps)
|
||||
in_time_stamp = -1
|
||||
@ -460,8 +467,8 @@ def list_users():
|
||||
def new_user(username: str):
|
||||
if not os.path.exists(userfolder):
|
||||
os.makedirs(userfolder)
|
||||
if not os.path.exists(f"{userfolder}/{username}"):
|
||||
os.makedirs(f"{userfolder}/{username}")
|
||||
if not os.path.exists(os.path.join(userfolder, username)):
|
||||
os.makedirs(os.path.join(userfolder, username))
|
||||
start_date_dt = datetime.datetime.now()
|
||||
start_date = start_date_dt.strftime("%Y-%m-%d")
|
||||
settings_to_write = standard_usersettings
|
||||
|
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
|
||||
}
|
||||
}
|
||||
}
|
@ -18,3 +18,15 @@
|
||||
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
|
||||
1746385118
|
||||
1747388255
|
||||
1747388619
|
||||
1747391536
|
||||
1747391567
|
||||
|
Loading…
x
Reference in New Issue
Block a user