From e7c999dccbac5fbc4bfc62acd3f14ddc59883f02 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Fri, 6 Jun 2025 11:37:54 +0200 Subject: [PATCH 01/11] Docker Script angepasst --- create_docker.py | 4 ++-- docker-compose.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/create_docker.py b/create_docker.py index c094235..e7e6170 100644 --- a/create_docker.py +++ b/create_docker.py @@ -6,8 +6,8 @@ server = 'gitea.am-td.de' server_user = 'alexander' if os.getuid() == 0: - subprocess.run(["docker", "build", "-t", f"{server}/{server_user}/{app_title.lower()}:{app_version}", "."]) - if input("docker-compose erstellen j=JA") == "j": + subprocess.run(["docker", "build", "--force-rm", "-t", f"{server}/{server_user}/{app_title.lower()}:{app_version}", "."]) + if input("docker-compose erstellen j=JA ") == "j": userfolder = input("Pfad für Benutzerdaten /users:") backupfolder = input("Pfad für Backupdaten /backup:") settingsfolder = input("Pfad für Einstellungen /settings:") diff --git a/docker-compose.yml b/docker-compose.yml index cf14b60..589fadc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: zeiterfassung: - image: gitea.am-td.de/alexander/zeiterfassung:0.0.0 + image: gitea.am-td.de/alexander/zeiterfassung:beta-2025.0.1 restart: always ports: - 8090:8090 From a74a3959e4bd0b940660b3bd6004ded165ca5eed Mon Sep 17 00:00:00 2001 From: alexander Date: Fri, 6 Jun 2025 12:54:52 +0200 Subject: [PATCH 02/11] docker-compose.yml aktualisiert --- docker-compose.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 589fadc..a69f2bb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ - services: zeiterfassung: image: gitea.am-td.de/alexander/zeiterfassung:beta-2025.0.1 @@ -8,6 +7,6 @@ services: environment: - PYTHONUNBUFFERED=1 volumes: - - ./docker-work/users:/users - - ./docker-work/backup:/backup - - ./docker-work/settings:/settings \ No newline at end of file + - ./users:/users + - ./backup:/backup + - ./settings:/settings \ No newline at end of file From 38d2d95b038f7184dc5adc68f651d364e7708927 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Wed, 9 Jul 2025 11:12:28 +0200 Subject: [PATCH 03/11] =?UTF-8?q?Docker=20Zeitzonensynchronisation=20hinzu?= =?UTF-8?q?gef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 ++- docker-compose.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 187bb2d..53e57f5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ users/ backup/ Archiv/ Docker/ -docker-work/ \ No newline at end of file +docker-work/ +Wiki/ \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 589fadc..96c612c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,4 +10,5 @@ services: volumes: - ./docker-work/users:/users - ./docker-work/backup:/backup - - ./docker-work/settings:/settings \ No newline at end of file + - ./docker-work/settings:/settings + - /etc/localtime:/etc/localtime:ro \ No newline at end of file From 3035bb384c7a9e3c8322859d8227b323b261dbae Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Fri, 24 Oct 2025 12:41:20 +0200 Subject: [PATCH 04/11] =?UTF-8?q?Erste=20TODOs=20definiert=20f=C3=BCr=20di?= =?UTF-8?q?e=20Anpassungen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/definitions.py | 3 +- lib/login.py | 5 +- lib/users.py | 5 ++ requirements.txt | 131 +++++++++++++++++++++++++++++++++++++++++++++ settings.json | 18 +++++++ 5 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 requirements.txt create mode 100644 settings.json diff --git a/lib/definitions.py b/lib/definitions.py index f8a4747..f695ba5 100644 --- a/lib/definitions.py +++ b/lib/definitions.py @@ -36,7 +36,8 @@ status_out = "ausgestempelt" # Standardadmin Settings: -standard_adminsettings = { "admin_user": "admin", +standard_adminsettings = { "admin_user": { + 0: "admin"}, "admin_password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918", "port": "8090", "secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise", diff --git a/lib/login.py b/lib/login.py index 25520ae..eb001d4 100644 --- a/lib/login.py +++ b/lib/login.py @@ -20,10 +20,11 @@ def page_login(): def login(): nonlocal data - if username.value == data["admin_user"]: - print(f"Input Hash: {hash_password(password.value)} gespeichert: {data['admin_password']}") + if username.value in get_admin_list(): + #TODO Loginprozess anpassen_ if hash_password(password.value) == data["admin_password"]: app.storage.user['authenticated'] = True + #TODO Weiterleitungsauswahl ob zum Stempeln oder Administration ui.navigate.to("/admin") else: ui.notify("Login fehlgeschlagen") diff --git a/lib/users.py b/lib/users.py index eb5c00a..d9d8aa1 100644 --- a/lib/users.py +++ b/lib/users.py @@ -578,3 +578,8 @@ def write_adminsetting(key: str, value): except KeyError: print(f"Kein Einstellungsschlüssel {key} vorhanden.") +def get_admin_list(): + adnin_settings = load_adminsettings() + admin_list = admin_settings["admin_user"] + return admin_list.values() + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0f30e3d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,131 @@ +aiofiles==24.1.0 +aiohappyeyeballs==2.6.1 +aiohttp==3.11.16 +aiosignal==1.3.2 +altgraph==0.17.4 +annotated-types==0.7.0 +anyio==4.9.0 +attrs==25.3.0 +Babel==2.10.3 +beautifulsoup4==4.11.2 +bidict==0.23.1 +blinker==1.5 +Brlapi==0.8.4 +certifi==2025.1.31 +chardet==5.1.0 +charset-normalizer==3.0.1 +click==8.1.8 +crit==3.17.1 +cryptography==38.0.4 +cupshelpers==1.0 +dbus-python==1.3.2 +distro==1.8.0 +distro-info==1.5+deb12u1 +docker==5.0.3 +docker-compose==1.29.2 +dockerpty==0.4.1 +docopt==0.6.2 +docutils==0.21.2 +easygui==0.98.1 +fastapi==0.115.12 +feedgenerator==2.0.0 +frozenlist==1.5.0 +gitdb==4.0.9 +GitPython==3.1.30 +gpg==1.18.0 +h11==0.14.0 +html5lib==1.1 +httpcore==1.0.8 +httplib2==0.20.4 +httptools==0.6.4 +httpx==0.28.1 +idna==3.3 +ifaddr==0.2.0 +imaplib2==3.5 +invoke==2.0.0 +itsdangerous==2.2.0 +Jinja2==3.1.6 +jsonpointer==2.3 +jsonschema==4.10.3 +lazr.restfulclient==0.14.5 +lazr.uri==1.0.6 +louis==3.24.0 +lxml==4.9.2 +Mako==1.2.4.dev0 +Markdown==3.4.1 +markdown-it-py==2.1.0 +markdown2==2.5.3 +MarkupSafe==2.1.2 +mdurl==0.1.2 +menulibre==2.2.2 +multidict==6.4.3 +nicegui==2.15.0 +ntpsec==1.2.2 +numpy==2.2.5 +oauthlib==3.2.2 +olefile==0.46 +opencv-python==4.11.0.86 +orjson==3.10.16 +packaging==25.0 +pelican==4.8.0 +photocollage==1.4.5 +Pillow==9.4.0 +playsound3==3.2.3 +propcache==0.3.1 +protobuf==4.21.12 +pscript==0.7.7 +psutil==5.9.4 +pycairo==1.20.1 +pycups==2.0.1 +pydantic==2.11.3 +pydantic_core==2.33.1 +pyenchant==3.2.2 +Pygments==2.19.1 +PyGObject==3.42.2 +pygtkspellcheck==4.0.5 +pyinstaller==6.13.0 +pyinstaller-hooks-contrib==2025.4 +PyJWT==2.6.0 +pyparsing==3.0.9 +pyrsistent==0.18.1 +pysmbc==1.0.23 +python-apt==2.6.0 +python-dateutil==2.8.2 +python-dotenv==1.1.0 +python-engineio==4.12.0 +python-multipart==0.0.20 +python-socketio==5.13.0 +pytz==2022.7.1 +pyxdg==0.28 +PyYAML==6.0.2 +requests==2.32.3 +rfc3987==1.3.8 +rich==13.3.1 +roman==3.3 +segno==1.6.6 +simple-websocket==1.1.0 +six==1.16.0 +smmap==5.0.0 +sniffio==1.3.1 +soupsieve==2.3.2 +starlette==0.46.2 +texttable==1.6.7 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +Unidecode==1.3.6 +uritemplate==4.1.1 +urllib3==2.4.0 +uvicorn==0.34.1 +uvloop==0.21.0 +vboxapi==1.0 +vbuild==0.8.2 +wadllib==1.3.6 +watchfiles==1.0.5 +webcolors==1.11.1 +webencodings==0.5.1 +websocket-client==1.2.3 +websockets==15.0.1 +wsproto==1.2.0 +wxPython==4.2.0 +xdg==5 +yarl==1.19.0 diff --git a/settings.json b/settings.json new file mode 100644 index 0000000..d18eb29 --- /dev/null +++ b/settings.json @@ -0,0 +1,18 @@ +{ + "admin_user": { + 0: "admin" + }, + "admin_password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918", + "port": "8090", + "secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise", + "times_on_touchscreen": true, + "photos_on_touchscreen": true, + "touchscreen": true, + "picture_height": 200, + "button_height": 300, + "user_notes": true, + "vacation_application": true, + "backup_folder": "/home/alexander/Dokumente/Python/Zeiterfassung/backup", + "backup_api_key": "6fed93dc4a35308b2c073a8a6f3284afe1fb9946", + "holidays": {} +} \ No newline at end of file From 0b478b5e2e76c5c9ad396899683f9212d0a6ab11 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Sun, 26 Oct 2025 21:18:23 +0100 Subject: [PATCH 05/11] Update auf Nicegui 3 Umstellung auf admin als User und Auswahl von Adminbenutzer --- lib/admin.py | 69 +++++++++++++++++++++++++++------------------- lib/definitions.py | 7 ++--- lib/login.py | 4 +-- lib/users.py | 2 +- lib/web_ui.py | 21 +++++++++++--- main.py | 4 +-- settings.json | 3 +- 7 files changed, 66 insertions(+), 44 deletions(-) diff --git a/lib/admin.py b/lib/admin.py index bd27e74..d6406fc 100644 --- a/lib/admin.py +++ b/lib/admin.py @@ -762,12 +762,14 @@ def page_admin(): ui.label("Administrationsbenutzer:").classes('text-bold') with ui.grid(columns=2).classes('items-baseline'): - 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.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.") - + ui.label("Benutzer mit Administrationsrechten:") + user_switch_list = [] + with ui.element(): + for i in list_users(): + user_switch_list.append(ui.switch(i)) + for i in user_switch_list: + if i.text in get_admin_list(): + i.value = True secret = data["secret"] with ui.card(): @@ -1045,31 +1047,40 @@ def page_admin(): holiday_section() def save_admin_settings(): - write_adminsetting("admin_user", admin_user.value) - if admin_password.value != "": - write_adminsetting("admin_password", hash_password(admin_password.value)) - else: - write_adminsetting("admin_password", data["admin_password"]) - write_adminsetting("port", port.value) - write_adminsetting("secret", secret) - write_adminsetting("touchscreen", touchscreen_switch.value) - write_adminsetting("times_on_touchscreen", timestamp_switch.value) - write_adminsetting("photos_on_touchscreen", photo_switch.value) - write_adminsetting("picture_height", picture_height_input.value) - write_adminsetting("button_height", button_height_input.value) - write_adminsetting("user_notes", notes_switch.value) - write_adminsetting("holidays", data["holidays"]) - write_adminsetting("vacation_application", va_switch.value) - - if int(old_port) != int(port.value): + admin_users = { } + admin_counter = -1 + for i in user_switch_list: + if i.value == True: + admin_counter += 1 + admin_users[str(admin_counter)] = i.text + if len(admin_users) == 0: with ui.dialog() as dialog, ui.card(): - ui.label( - "Damit die Porteinstellungen wirksam werden, muss der Server neu gestartet werden.") - ui.button("OK", on_click=lambda: dialog.close()) + ui.label("Es wurde kein Administrationsbenutzer ausgewählt. Mindestens ein Benutzer muss Administrationsrechte haben.") + ui.button("OK", on_click=dialog.close) dialog.open() - ui.notify("Einstellungen gespeichert") - reset_visibility.value = False - timetable.refresh() + else: + + write_adminsetting("admin_user", admin_users) + write_adminsetting("port", port.value) + write_adminsetting("secret", secret) + write_adminsetting("touchscreen", touchscreen_switch.value) + write_adminsetting("times_on_touchscreen", timestamp_switch.value) + write_adminsetting("photos_on_touchscreen", photo_switch.value) + write_adminsetting("picture_height", picture_height_input.value) + write_adminsetting("button_height", button_height_input.value) + write_adminsetting("user_notes", notes_switch.value) + write_adminsetting("holidays", data["holidays"]) + write_adminsetting("vacation_application", va_switch.value) + + if int(old_port) != int(port.value): + with ui.dialog() as dialog, ui.card(): + ui.label( + "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") + reset_visibility.value = False + timetable.refresh() with ui.button("Speichern", on_click=save_admin_settings): with ui.tooltip(): diff --git a/lib/definitions.py b/lib/definitions.py index f695ba5..4b86e79 100644 --- a/lib/definitions.py +++ b/lib/definitions.py @@ -38,7 +38,6 @@ status_out = "ausgestempelt" standard_adminsettings = { "admin_user": { 0: "admin"}, - "admin_password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918", "port": "8090", "secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise", "times_on_touchscreen": True, @@ -56,9 +55,9 @@ standard_adminsettings = { "admin_user": { # Standard User Settings: standard_usersettings = { - "username": "default", - "fullname": "Standardbenutzer", - "password": "37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f", + "username": "admin", + "fullname": "Administrator", + "password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918", "api_key": "1234567890", "workhours": { } } diff --git a/lib/login.py b/lib/login.py index eb001d4..2aca6f0 100644 --- a/lib/login.py +++ b/lib/login.py @@ -19,11 +19,11 @@ def page_login(): def login(): nonlocal data - + print(username.value) if username.value in get_admin_list(): #TODO Loginprozess anpassen_ if hash_password(password.value) == data["admin_password"]: - app.storage.user['authenticated'] = True + app.storage.user['admin_authenticated'] = True #TODO Weiterleitungsauswahl ob zum Stempeln oder Administration ui.navigate.to("/admin") else: diff --git a/lib/users.py b/lib/users.py index d9d8aa1..ae8b92a 100644 --- a/lib/users.py +++ b/lib/users.py @@ -580,6 +580,6 @@ def write_adminsetting(key: str, value): def get_admin_list(): adnin_settings = load_adminsettings() - admin_list = admin_settings["admin_user"] + admin_list = load_adminsettings()["admin_user"] return admin_list.values() diff --git a/lib/web_ui.py b/lib/web_ui.py index 919eb4f..816ac23 100644 --- a/lib/web_ui.py +++ b/lib/web_ui.py @@ -38,10 +38,23 @@ class login_mask: def login(): nonlocal data - if username.value == data["admin_user"]: - if hash_password(password.value) == data["admin_password"]: - app.storage.user['admin_authenticated'] = True - ui.navigate.to("/admin") + if username.value in get_admin_list(): + current_user = user(username.value) + if hash_password(password.value) == current_user.password: + with ui.dialog() as forward_dialog, ui.card(): + ui.label("Wollen Sie den Administrationsbereich oder den Datenbereich aufrufen?") + def admin_area(): + app.storage.user['admin_authenticated'] = True + ui.navigate.to('/admin') + def time_area(): + app.storage.user['active_user'] = current_user.username + ui.navigate.to(self.target) + with ui.grid(columns=2): + ui.button("Administrationsbereich", on_click=admin_area) + ui.button("Datenbereich", on_click=time_area) + + forward_dialog.open() + #ui.navigate.to("/admin") else: ui.notify("Login fehlgeschlagen") else: diff --git a/main.py b/main.py index 6566a6c..d9b50cb 100644 --- a/main.py +++ b/main.py @@ -33,7 +33,7 @@ def main(): list_users() - homepage() + #homepage() def startup_message(): @@ -54,7 +54,7 @@ def main(): ui.toggle.default_props('rounded') ui.row.default_classes('items-baseline') - ui.run(favicon='⏲', port=port, storage_secret=secret, language='de-DE', show_welcome_message=False) + ui.run(root=homepage, 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}') diff --git a/settings.json b/settings.json index d18eb29..77397c6 100644 --- a/settings.json +++ b/settings.json @@ -1,8 +1,7 @@ { "admin_user": { - 0: "admin" + "0": "admin" }, - "admin_password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918", "port": "8090", "secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise", "times_on_touchscreen": true, From 47a71ebf445f492ff720b426eba5757091d838d1 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Mon, 27 Oct 2025 09:37:59 +0100 Subject: [PATCH 06/11] =?UTF-8?q?Login=20Seite=20entfernt.=20Ist=20in=20Ha?= =?UTF-8?q?uptseite=20integriert=20L=C3=B6schsperre=20f=C3=BCr=20Adminuser?= =?UTF-8?q?=20eingef=C3=BCgt=20Kommandozeilenfunktion=20f=C3=BCr=20Adminan?= =?UTF-8?q?lage=20angepasst.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/admin.py | 19 +++++++++++++++---- lib/login.py | 42 ------------------------------------------ main.py | 34 ++++++++++++++++++++++++++++------ settings.json | 3 ++- 4 files changed, 45 insertions(+), 53 deletions(-) delete mode 100644 lib/login.py diff --git a/lib/admin.py b/lib/admin.py index d6406fc..17c3df9 100644 --- a/lib/admin.py +++ b/lib/admin.py @@ -44,6 +44,10 @@ def page_admin(): updates_available = ValueBinder() updates_available.value = False + delete_binder = ValueBinder() + delete_binder.value = True + delete_info = ValueBinder() + delete_info.value = False enabled_because_not_docker = ValueBinder if is_docker(): @@ -1091,7 +1095,6 @@ def page_admin(): workhours = [ ] with ui.row(): - def user_selection_changed(): try: if user_selection.value != None: @@ -1100,7 +1103,14 @@ def page_admin(): fullname_input.value = current_user.fullname #password_input.value = current_user.password usersettingscard.visible = True - + if current_user.username in get_admin_list(): + delete_info.value = True + delete_binder.value = False + else: + delete_info.value = False + delete_binder.value = True + print(delete_info.value) + print(delete_binder.value) api_key_input.value = current_user.api_key api_link_column.clear() @@ -1338,10 +1348,11 @@ def page_admin(): for i in app.urls: stamp_link.append(ui.link(f'{i}/api/stamp/"API-Schüssel"')) - + ui.label("Administratoren können nicht gelöscht werden. Um das Konto zu löschen, müssen Sie ihm zuerst die Administrationsrechte entziehen.").bind_visibility_from(delete_info, 'value').classes('font-bold text-red') with ui.grid(columns=2): ui.button("Speichern", on_click=save_user_settings).tooltip("Klicken Sie hier um die Änderungen zu speichern.") - ui.button("Löschen", on_click=del_user) + ui.button("Löschen", on_click=del_user).bind_enabled_from(delete_binder, 'value') + usersettings_card() diff --git a/lib/login.py b/lib/login.py deleted file mode 100644 index 2aca6f0..0000000 --- a/lib/login.py +++ /dev/null @@ -1,42 +0,0 @@ -from datetime import datetime - -from nicegui import ui, app -from lib.web_ui import * - -from lib.users import * -from lib.definitions import * -from calendar import monthrange - -import hashlib -import calendar -import locale - -@ui.page('/login') -def page_login(): - - # Settingsdatei einlesen - data = load_adminsettings() - - def login(): - nonlocal data - print(username.value) - if username.value in get_admin_list(): - #TODO Loginprozess anpassen_ - if hash_password(password.value) == data["admin_password"]: - app.storage.user['admin_authenticated'] = True - #TODO Weiterleitungsauswahl ob zum Stempeln oder Administration - ui.navigate.to("/admin") - else: - ui.notify("Login fehlgeschlagen") - - #ui.markdown(f"## {app_title} {app_version}") - #ui.markdown("Bitte einloggen") - - pageheader("Bitte einloggen:") - - with ui.grid(columns=2): - ui.markdown("Benutzer:") - username = ui.input('Benutzername') - ui.markdown("Passwort:") - password = ui.input('Passwort', password=True) - ui.button(text="Login", on_click=lambda: login()) \ No newline at end of file diff --git a/main.py b/main.py index d9b50cb..2e524e8 100644 --- a/main.py +++ b/main.py @@ -4,7 +4,6 @@ import os.path from lib.web_ui import * from lib.admin import * -from lib.login import * from lib.users import * from lib.touchscreen import * from lib.definitions import * @@ -85,11 +84,34 @@ if __name__ in ("__main__", "__mp_main__"): 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) + if not os.path.exists(userfolder): + os.makedirs(userfolder) + print("Kein Ordner mit Benutzerdaten gefunden. Lege ihn an.") + if not os.path.exists(os.path.join(userfolder, admin_user)): + print("Benutzer existiert noch nicht. Lege ihn an.") + os.makedirs(os.path.join(userfolder, admin_user)) + start_date_dt = datetime.datetime.now() + start_date = start_date_dt.strftime("%Y-%m-%d") + settings_to_write = standard_usersettings + settings_to_write["workhours"][start_date] = {} + settings_to_write["fullname"] = "Administrator" + settings_to_write["username"] = admin_user + # API-Key erzeugen + string_to_hash = f'{admin_user}_{datetime.datetime.now().timestamp()}' + hash_string = hashlib.shake_256(bytes(string_to_hash, 'utf-8')).hexdigest(20) + settings_to_write["api_key"] = hash_string + for i in range(1, 8): + settings_to_write["workhours"][start_date][str(i)] = 0 + settings_to_write["workhours"][start_date]["vacation"] = 0 + with open(f"{userfolder}/{admin_user}/{usersettingsfilename}", 'w') as json_file: + json_dict = json.dumps(standard_usersettings, indent=4) + json_file.write(json_dict) + current_user = user(admin_user) + current_user.password = hash_password(admin_password) + current_user.write_settings() + admin_users_list = load_adminsettings()["admin_user"] + admin_users_list[str(len(admin_users_list))] = admin_user + write_adminsetting("admin_user", admin_users_list) print("Daten geschrieben") quit() else: diff --git a/settings.json b/settings.json index 77397c6..68532ee 100644 --- a/settings.json +++ b/settings.json @@ -1,6 +1,7 @@ { "admin_user": { - "0": "admin" + "0": "admin", + "1": "admin2" }, "port": "8090", "secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise", From 0b517f2b308df311bd2337b28042aacc7eb6094a Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Mon, 27 Oct 2025 13:47:57 +0100 Subject: [PATCH 07/11] =?UTF-8?q?"No=20Time=20Users"=20hinzugef=C3=BCgt.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- create_docker.py | 22 +++---- lib/admin.py | 59 +++++++++++++---- lib/definitions.py | 1 + lib/touchscreen.py | 141 +++++++++++++++++++++-------------------- lib/users.py | 5 ++ lib/web_ui.py | 29 +++++---- requirements.txt | 131 -------------------------------------- settings.json | 5 +- settings/settings.json | 17 +++++ 9 files changed, 170 insertions(+), 240 deletions(-) delete mode 100644 requirements.txt create mode 100644 settings/settings.json diff --git a/create_docker.py b/create_docker.py index e7e6170..d3266a9 100644 --- a/create_docker.py +++ b/create_docker.py @@ -5,13 +5,13 @@ import os server = 'gitea.am-td.de' server_user = 'alexander' -if os.getuid() == 0: - subprocess.run(["docker", "build", "--force-rm", "-t", f"{server}/{server_user}/{app_title.lower()}:{app_version}", "."]) - if input("docker-compose erstellen j=JA ") == "j": - userfolder = input("Pfad für Benutzerdaten /users:") - backupfolder = input("Pfad für Backupdaten /backup:") - settingsfolder = input("Pfad für Einstellungen /settings:") - docker_compose_content = f''' +#if os.getuid() == 0: +subprocess.run(["docker", "build", "--force-rm", "-t", f"{server}/{server_user}/{app_title.lower()}:{app_version}", "."]) +if input("docker-compose erstellen j=JA ") == "j": + userfolder = input("Pfad für Benutzerdaten /users:") + backupfolder = input("Pfad für Backupdaten /backup:") + settingsfolder = input("Pfad für Einstellungen /settings:") + docker_compose_content = f''' services: zeiterfassung: image: {server}/{server_user}/{app_title.lower()}:{app_version.lower()} @@ -25,7 +25,7 @@ services: - {backupfolder}:/backup - {settingsfolder}:/settings''' - with open('docker-compose.yml', 'w') as docker_compose: - docker_compose.write(docker_compose_content) -else: - print("Es werden Root-Rechte benötigt.") + with open('docker-compose.yml', 'w') as docker_compose: + docker_compose.write(docker_compose_content) +#else: +# print("Es werden Root-Rechte benötigt.") diff --git a/lib/admin.py b/lib/admin.py index 17c3df9..4804882 100644 --- a/lib/admin.py +++ b/lib/admin.py @@ -4,6 +4,7 @@ import dateutil.easter from dateutil.easter import * from nicegui import ui, app, events +from nicegui.functions.navigate import navigate from nicegui.html import button from nicegui.events import KeyEventArguments @@ -69,6 +70,13 @@ def page_admin(): def update_userlist(): nonlocal userlist userlist = list_users() + # Benutzerliste um No Time Users bereinigen + users_to_remove = [ ] + for entry in userlist: + if entry in get_no_time_users_list(): + users_to_remove.append(entry) + for i in users_to_remove: + userlist.remove(i) update_userlist() @@ -763,19 +771,27 @@ def page_admin(): with ui.tab_panel(settings): with ui.grid(columns='auto auto'): with ui.card(): - ui.label("Administrationsbenutzer:").classes('text-bold') - with ui.grid(columns=2).classes('items-baseline'): + ui.label("Benutzereinstellungen:").classes('text-bold') + with ui.grid(columns=2).classes('items-center'): ui.label("Benutzer mit Administrationsrechten:") user_switch_list = [] - with ui.element(): + with ui.grid(columns=2).classes('gap-y-0'): for i in list_users(): user_switch_list.append(ui.switch(i)) for i in user_switch_list: if i.text in get_admin_list(): i.value = True secret = data["secret"] - + ui.separator().classes('col-span-2') + ui.label("Benutzer ohne Zeiterfassung:") + no_time_user_switch_list = [ ] + with ui.grid(columns=2).classes('gap-y-0'): + for i in list_users(): + no_time_user_switch_list.append(ui.switch(i)) + for item in no_time_user_switch_list: + if item.text in get_no_time_users_list(): + item.value = True with ui.card(): ui.label("Systemeinstellungen:").classes('text-bold') with ui.grid(columns=2).classes('items-baseline'): @@ -1064,7 +1080,17 @@ def page_admin(): dialog.open() else: + no_time_users = { } + no_time_users_counter = -1 + for i in no_time_user_switch_list: + if i.value == True: + no_time_users_counter += 1 + no_time_users[str(no_time_users_counter)] = i.text + + old_no_time_users_list = get_no_time_users_list() + write_adminsetting("admin_user", admin_users) + write_adminsetting("no_time_users", no_time_users) write_adminsetting("port", port.value) write_adminsetting("secret", secret) write_adminsetting("touchscreen", touchscreen_switch.value) @@ -1086,6 +1112,12 @@ def page_admin(): reset_visibility.value = False timetable.refresh() + if not set(old_no_time_users_list) == set(get_no_time_users_list()): + with ui.dialog() as infobox, ui.card(): + ui.label("Benutzer ohne Zeiterfassung wurden geändert. Die Seite wird neu geladen.") + ui.button("OK", on_click=ui.navigate.reload) + infobox.open() + with ui.button("Speichern", on_click=save_admin_settings): with ui.tooltip(): ui.label("Hiermit werden sämtliche oben gemachten Einstellungen gespeichert.\nGgf. müssen Sie die Seite neu laden um die Auswirkungen sichtbar zu machen.").style('white-space: pre-wrap') @@ -1109,8 +1141,6 @@ def page_admin(): else: delete_info.value = False delete_binder.value = True - print(delete_info.value) - print(delete_binder.value) api_key_input.value = current_user.api_key api_link_column.clear() @@ -1183,7 +1213,7 @@ def page_admin(): 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(): + with ui.row().classes('w-full justify-center'): ui.button("Speichern", on_click=save_settings) ui.button("Abbrechen", on_click=dialog.close) dialog.open() @@ -1236,9 +1266,9 @@ def page_admin(): with ui.dialog() as dialog, ui.card(): ui.label("Sollen die Änderungen an den Arbeitsstunden und/oder Urlaubstagen gespeichert werden?") - with ui.row(): + with ui.row().classes('justify-center w-full'): ui.button("Speichern", on_click=save_settings) - ui.button("Abrrechen", on_click=dialog.close) + ui.button("Abbrechen", on_click=dialog.close) dialog.open() def delete_workhour_entry(): @@ -1342,11 +1372,12 @@ def page_admin(): 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.label('Aufruf zum Stempeln:') global api_link_column - with ui.column().classes('gap-0') as api_link_column: - global stamp_link - stamp_link = [ ] - for i in app.urls: - stamp_link.append(ui.link(f'{i}/api/stamp/"API-Schüssel"')) + with ui.expansion("").classes('w-full'): + with ui.column().classes('gap-0') as api_link_column: + global stamp_link + stamp_link = [ ] + for i in app.urls: + stamp_link.append(ui.link(f'{i}/api/stamp/"API-Schüssel"')) ui.label("Administratoren können nicht gelöscht werden. Um das Konto zu löschen, müssen Sie ihm zuerst die Administrationsrechte entziehen.").bind_visibility_from(delete_info, 'value').classes('font-bold text-red') with ui.grid(columns=2): diff --git a/lib/definitions.py b/lib/definitions.py index 4b86e79..f2438fb 100644 --- a/lib/definitions.py +++ b/lib/definitions.py @@ -38,6 +38,7 @@ status_out = "ausgestempelt" standard_adminsettings = { "admin_user": { 0: "admin"}, + "no_time_users": { }, "port": "8090", "secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise", "times_on_touchscreen": True, diff --git a/lib/touchscreen.py b/lib/touchscreen.py index 9557552..a06beca 100644 --- a/lib/touchscreen.py +++ b/lib/touchscreen.py @@ -41,15 +41,15 @@ def page_touchscreen(): 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: + elif width > 1200: number_of_columns = 4 - elif width > 750: + elif width > 900: number_of_columns = 3 - else: + elif width > 750: number_of_columns = 2 + else: + number_of_columns = 1 user_buttons.refresh() ui.on('resize', lambda e: set_columns(e.args['width'])) @@ -71,76 +71,77 @@ def page_touchscreen(): ''') - with ui.grid(columns=number_of_columns).classes('w-full center'): + with ui.grid(columns=number_of_columns).classes('w-full'): 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]') + if not current_user.username in get_no_time_users_list(): + current_button = ui.button(on_click=lambda name=name: button_click(name)).classes(f'h-full min-h-[{admin_settings["button_height"]}px]') - with current_button: - with ui.grid(columns='1fr 1fr').classes('w-full h-full py-5 items-start'): + with current_button: + 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''' - - - - - - - - - - - - - - - - - - ''' - 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: - current_button.props('color=red') - buttons[name] = current_button + 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''' + + + + + + + + + + + + + + + + + + ''' + ui.html(no_photo_svg, sanitize=False) + 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: + current_button.props('color=red') + buttons[name] = current_button user_buttons() else: diff --git a/lib/users.py b/lib/users.py index ae8b92a..345b440 100644 --- a/lib/users.py +++ b/lib/users.py @@ -583,3 +583,8 @@ def get_admin_list(): admin_list = load_adminsettings()["admin_user"] return admin_list.values() +def get_no_time_users_list(): + adnin_settings = load_adminsettings() + admin_list = load_adminsettings()["no_time_users"] + return admin_list.values() + diff --git a/lib/web_ui.py b/lib/web_ui.py index 816ac23..d14864a 100644 --- a/lib/web_ui.py +++ b/lib/web_ui.py @@ -41,20 +41,23 @@ class login_mask: if username.value in get_admin_list(): current_user = user(username.value) if hash_password(password.value) == current_user.password: - with ui.dialog() as forward_dialog, ui.card(): - ui.label("Wollen Sie den Administrationsbereich oder den Datenbereich aufrufen?") - def admin_area(): - app.storage.user['admin_authenticated'] = True - ui.navigate.to('/admin') - def time_area(): - app.storage.user['active_user'] = current_user.username - ui.navigate.to(self.target) - with ui.grid(columns=2): - ui.button("Administrationsbereich", on_click=admin_area) - ui.button("Datenbereich", on_click=time_area) + if not username.value in get_no_time_users_list(): + with ui.dialog() as forward_dialog, ui.card(): + ui.label("Wollen Sie den Administrationsbereich oder den Datenbereich aufrufen?") + def admin_area(): + app.storage.user['admin_authenticated'] = True + ui.navigate.to('/admin') + def time_area(): + app.storage.user['active_user'] = current_user.username + ui.navigate.to(self.target) + with ui.grid(columns=2).classes('justify-center w-full'): + ui.button("Administrationsbereich", on_click=admin_area) + ui.button("Datenbereich", on_click=time_area) - forward_dialog.open() - #ui.navigate.to("/admin") + forward_dialog.open() + else: + app.storage.user['admin_authenticated'] = True + ui.navigate.to("/admin") else: ui.notify("Login fehlgeschlagen") else: diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 0f30e3d..0000000 --- a/requirements.txt +++ /dev/null @@ -1,131 +0,0 @@ -aiofiles==24.1.0 -aiohappyeyeballs==2.6.1 -aiohttp==3.11.16 -aiosignal==1.3.2 -altgraph==0.17.4 -annotated-types==0.7.0 -anyio==4.9.0 -attrs==25.3.0 -Babel==2.10.3 -beautifulsoup4==4.11.2 -bidict==0.23.1 -blinker==1.5 -Brlapi==0.8.4 -certifi==2025.1.31 -chardet==5.1.0 -charset-normalizer==3.0.1 -click==8.1.8 -crit==3.17.1 -cryptography==38.0.4 -cupshelpers==1.0 -dbus-python==1.3.2 -distro==1.8.0 -distro-info==1.5+deb12u1 -docker==5.0.3 -docker-compose==1.29.2 -dockerpty==0.4.1 -docopt==0.6.2 -docutils==0.21.2 -easygui==0.98.1 -fastapi==0.115.12 -feedgenerator==2.0.0 -frozenlist==1.5.0 -gitdb==4.0.9 -GitPython==3.1.30 -gpg==1.18.0 -h11==0.14.0 -html5lib==1.1 -httpcore==1.0.8 -httplib2==0.20.4 -httptools==0.6.4 -httpx==0.28.1 -idna==3.3 -ifaddr==0.2.0 -imaplib2==3.5 -invoke==2.0.0 -itsdangerous==2.2.0 -Jinja2==3.1.6 -jsonpointer==2.3 -jsonschema==4.10.3 -lazr.restfulclient==0.14.5 -lazr.uri==1.0.6 -louis==3.24.0 -lxml==4.9.2 -Mako==1.2.4.dev0 -Markdown==3.4.1 -markdown-it-py==2.1.0 -markdown2==2.5.3 -MarkupSafe==2.1.2 -mdurl==0.1.2 -menulibre==2.2.2 -multidict==6.4.3 -nicegui==2.15.0 -ntpsec==1.2.2 -numpy==2.2.5 -oauthlib==3.2.2 -olefile==0.46 -opencv-python==4.11.0.86 -orjson==3.10.16 -packaging==25.0 -pelican==4.8.0 -photocollage==1.4.5 -Pillow==9.4.0 -playsound3==3.2.3 -propcache==0.3.1 -protobuf==4.21.12 -pscript==0.7.7 -psutil==5.9.4 -pycairo==1.20.1 -pycups==2.0.1 -pydantic==2.11.3 -pydantic_core==2.33.1 -pyenchant==3.2.2 -Pygments==2.19.1 -PyGObject==3.42.2 -pygtkspellcheck==4.0.5 -pyinstaller==6.13.0 -pyinstaller-hooks-contrib==2025.4 -PyJWT==2.6.0 -pyparsing==3.0.9 -pyrsistent==0.18.1 -pysmbc==1.0.23 -python-apt==2.6.0 -python-dateutil==2.8.2 -python-dotenv==1.1.0 -python-engineio==4.12.0 -python-multipart==0.0.20 -python-socketio==5.13.0 -pytz==2022.7.1 -pyxdg==0.28 -PyYAML==6.0.2 -requests==2.32.3 -rfc3987==1.3.8 -rich==13.3.1 -roman==3.3 -segno==1.6.6 -simple-websocket==1.1.0 -six==1.16.0 -smmap==5.0.0 -sniffio==1.3.1 -soupsieve==2.3.2 -starlette==0.46.2 -texttable==1.6.7 -typing-inspection==0.4.0 -typing_extensions==4.13.2 -Unidecode==1.3.6 -uritemplate==4.1.1 -urllib3==2.4.0 -uvicorn==0.34.1 -uvloop==0.21.0 -vboxapi==1.0 -vbuild==0.8.2 -wadllib==1.3.6 -watchfiles==1.0.5 -webcolors==1.11.1 -webencodings==0.5.1 -websocket-client==1.2.3 -websockets==15.0.1 -wsproto==1.2.0 -wxPython==4.2.0 -xdg==5 -yarl==1.19.0 diff --git a/settings.json b/settings.json index 68532ee..7e32a9c 100644 --- a/settings.json +++ b/settings.json @@ -3,13 +3,16 @@ "0": "admin", "1": "admin2" }, + "no_time_users": { + "0": "admin" + }, "port": "8090", "secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise", "times_on_touchscreen": true, "photos_on_touchscreen": true, "touchscreen": true, "picture_height": 200, - "button_height": 300, + "button_height": "300", "user_notes": true, "vacation_application": true, "backup_folder": "/home/alexander/Dokumente/Python/Zeiterfassung/backup", diff --git a/settings/settings.json b/settings/settings.json new file mode 100644 index 0000000..f60cc13 --- /dev/null +++ b/settings/settings.json @@ -0,0 +1,17 @@ +{ + "admin_user": { + "0": "admin" + }, + "port": "8090", + "secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise", + "times_on_touchscreen": true, + "photos_on_touchscreen": true, + "touchscreen": true, + "picture_height": 200, + "button_height": 300, + "user_notes": true, + "vacation_application": true, + "backup_folder": "/backup", + "backup_api_key": "0b3b1d883b915bd2f05ee4d256d97a404df67a93", + "holidays": {} +} \ No newline at end of file From e94ea84337c3e9195a68ae7c298e7ceecf6d9925 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Thu, 30 Oct 2025 13:42:37 +0100 Subject: [PATCH 08/11] =?UTF-8?q?Vorbereitungen=20f=C3=BCr=20n=C3=A4chsten?= =?UTF-8?q?=20Zwischenrelease?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/definitions.py | 2 +- settings.json | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/definitions.py b/lib/definitions.py index f2438fb..31ab1ee 100644 --- a/lib/definitions.py +++ b/lib/definitions.py @@ -6,7 +6,7 @@ from pathlib import Path import hashlib app_title = "Zeiterfassung" -app_version = "beta-2025.0.1" +app_version = "beta-2025.0.2" # Standardpfade diff --git a/settings.json b/settings.json index 7e32a9c..9ac6bba 100644 --- a/settings.json +++ b/settings.json @@ -1,7 +1,6 @@ { "admin_user": { - "0": "admin", - "1": "admin2" + "0": "admin" }, "no_time_users": { "0": "admin" From 6bdaccb46e3f01cf16ca716a3f9903952a6ba3df Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Thu, 30 Oct 2025 13:45:53 +0100 Subject: [PATCH 09/11] .gitignore aktualisiert --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 53e57f5..000c5cd 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ Testplan.md .venv users/ backup/ +settings/ Archiv/ Docker/ docker-work/ From 0720e0cbe90118b8aa0e90a21da6283e5bd0f054 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Thu, 30 Oct 2025 13:48:26 +0100 Subject: [PATCH 10/11] =?UTF-8?q?Docker=20Settings=20gel=C3=B6scht?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- settings/settings.json | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 settings/settings.json diff --git a/settings/settings.json b/settings/settings.json deleted file mode 100644 index f60cc13..0000000 --- a/settings/settings.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "admin_user": { - "0": "admin" - }, - "port": "8090", - "secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise", - "times_on_touchscreen": true, - "photos_on_touchscreen": true, - "touchscreen": true, - "picture_height": 200, - "button_height": 300, - "user_notes": true, - "vacation_application": true, - "backup_folder": "/backup", - "backup_api_key": "0b3b1d883b915bd2f05ee4d256d97a404df67a93", - "holidays": {} -} \ No newline at end of file From a602aded2caca383d5137f6bda55cff219a40343 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Thu, 30 Oct 2025 14:08:15 +0100 Subject: [PATCH 11/11] Docker Dateien angepasst --- Dockerfile | 13 ++++++------- docker-compose.yml | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index ff4197a..d892ad0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,13 @@ -FROM debian:latest +FROM python:latest RUN apt update && apt upgrade -y -RUN apt install python3 python3-pip python3.11-venv locales -y +RUN apt install locales -y RUN mkdir /app RUN mkdir /.venv RUN mkdir /backup RUN mkdir /settings -RUN python3 -m venv /.venv -RUN /.venv/bin/pip install nicegui -RUN /.venv/bin/pip install segno -RUN /.venv/bin/pip install python-dateutil +RUN pip install nicegui +RUN pip install segno +RUN pip install python-dateutil RUN sed -i '/de_DE.UTF-8/s/^# //g' /etc/locale.gen && \ locale-gen @@ -19,4 +18,4 @@ ENV LC_ALL de_DE.UTF-8 COPY main.py /app/main.py COPY lib /app/lib/ EXPOSE 8090 -ENTRYPOINT ["/.venv/bin/python", "/app/main.py"] +ENTRYPOINT ["python", "/app/main.py"] diff --git a/docker-compose.yml b/docker-compose.yml index 4bf94ed..dc3a301 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: zeiterfassung: - image: gitea.am-td.de/alexander/zeiterfassung:beta-2025.0.1 + image: gitea.am-td.de/alexander/zeiterfassung:beta-2025.0.2 restart: always ports: - 8090:8090 @@ -11,4 +11,4 @@ services: - ./users:/users - ./backup:/backup - ./settings:/settings - - /etc/localtime:/etc/localtime:ro \ No newline at end of file + - /etc/localtime:/etc/localtime:ro