From e54a210d6986baa4ecc26cccf4af2d31004d9a5a Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Mon, 2 Jun 2025 11:32:41 +0200 Subject: [PATCH 1/3] =?UTF-8?q?Erste=20Anpassungen=20f=C3=BCr=20Dockercont?= =?UTF-8?q?ainer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 23 +++++++++++++++++++++++ docker-compose.yml | 10 ++++++++++ lib/admin.py | 11 ++++++++--- lib/definitions.py | 5 ++++- lib/users.py | 20 ++++++++++++-------- settings.json => settings.json_local | 3 ++- users/testuser10/2025-5.json | 5 +++-- zeiterfassung.py | 1 - 8 files changed, 62 insertions(+), 16 deletions(-) create mode 100644 Dockerfile create mode 100644 docker-compose.yml rename settings.json => settings.json_local (97%) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..59e4c5a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM debian:latest +RUN apt update && apt upgrade -y +RUN apt install python3 python3-pip python3.11-venv locales -y +RUN mkdir /app +RUN mkdir /.venv +RUN mkdir /backup +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 sed -i '/de_DE.UTF-8/s/^# //g' /etc/locale.gen && \ + locale-gen +ENV LANG de_DE.UTF-8 +ENV LANGUAGE de_DE:de +ENV LC_ALL de_DE.UTF-8 + +COPY main.py /app/main.py +COPY favicon.svg /app/favicon.svg +COPY lib /app/lib/ +EXPOSE 8090 +ENTRYPOINT ["/.venv/bin/python", "/app/main.py", "--docker"] +#ENTRYPOINT exec /app/.venv/bin/python /app/main.py --docker \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..56a3f5c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +services: + test: + image: test:0 + restart: always + ports: + - 8090:8090 + volumes: + - /home/alexander/Dokumente/Python/Zeiterfassung:/app + - /home/alexander/Dokumente/Python/Zeiterfassung/users-docker:/users + - /home/alexander/Dokumente/Python/Zeiterfassung/backup:/backup \ No newline at end of file diff --git a/lib/admin.py b/lib/admin.py index 9963585..6b3c32e 100644 --- a/lib/admin.py +++ b/lib/admin.py @@ -1,7 +1,6 @@ from datetime import datetime import dateutil.easter -from PIL.SpiderImagePlugin import isInt from dateutil.easter import * from nicegui import ui, app, events @@ -45,6 +44,9 @@ def page_admin(): updates_available = ValueBinder() updates_available.value = False + is_docker = ValueBinder + print(docker) + with ui.tabs() as tabs: time_overview = ui.tab('Zeitdaten') @@ -1468,7 +1470,10 @@ Dies kann nicht rückgängig gemacht werden!''') except ValueError: button_string = date_string ui.markdown(button_string) - ui.markdown(f'{round(size/1_000_000,2)} MB') + if size > 1_000_000: + ui.markdown(f'{round(size/1_000_000,2)} MB') + else: + ui.markdown(f'{round(size / 1_000, 2)} kB') ui.markdown(version) ui.button(icon='download', on_click=lambda file=date_string: ui.download.file( os.path.join(scriptpath, backupfolder, f'{file}.zip'))).tooltip( @@ -1529,7 +1534,7 @@ Dies kann nicht rückgängig gemacht werden!''') for file in files: add = os.path.join(root, file) target.write(add) - target.write(usersettingsfilename) + target.write(os.path.join(scriptpath, usersettingsfilename), arcname=usersettingsfilename) target.writestr("app_version.txt", data=app_version) backup_list.refresh() n.dismiss() diff --git a/lib/definitions.py b/lib/definitions.py index 6310a59..ebef833 100644 --- a/lib/definitions.py +++ b/lib/definitions.py @@ -6,9 +6,12 @@ from pathlib import Path import hashlib app_title = "Zeiterfassung" -app_version = ("0.0.0") +app_version = "0.0.0" + +docker = False # Standardpfade + scriptpath = str(Path(os.path.dirname(os.path.abspath(__file__))).parent.absolute()) userfolder = "users" backupfolder = str(os.path.join(scriptpath, "backup")) diff --git a/lib/users.py b/lib/users.py index 3d37674..4a05b8d 100644 --- a/lib/users.py +++ b/lib/users.py @@ -14,25 +14,28 @@ import shutil import re from lib.definitions import userfolder, scriptpath, usersettingsfilename, photofilename, status_in, status_out, \ - standard_adminsettings, standard_usersettings, va_file + standard_adminsettings, standard_usersettings, va_file, docker # Benutzerklasse class user: def __init__(self, name): - self.userfolder = os.path.join(scriptpath, userfolder, name) + if not scriptpath == "/app": + self.userfolder = os.path.join(scriptpath, userfolder, name) + else: + self.userfolder = os.path.join("/users", name) self.settingsfile = os.path.join(self.userfolder, usersettingsfilename) self.photofile = os.path.join(self.userfolder, photofilename) # Stammdaten einlesen - try: - with open(self.settingsfile) as json_file: + #try: + with open(self.settingsfile) as json_file: data = json.load(json_file) - - except: - print("Fehler beim Erstellen des Datenarrays.") - #Hier muss noch Fehlerbehandlungcode hin + print(data) + #except: + # print("Fehler beim Erstellen des Datenarrays.") + # #TODO Hier muss noch Fehlerbehandlungcode hin self.password = data["password"] self.workhours = data["workhours"] @@ -534,6 +537,7 @@ def new_user(username: str): def load_adminsettings(): # Settingsdatei einlesen settings_filename = os.path.join(scriptpath, usersettingsfilename) + print(settings_filename) if not os.path.exists(settings_filename): print("Keine Einstellungsdatei gefunden. Lege Standarddatei an.") with open(settings_filename, 'w') as json_file: diff --git a/settings.json b/settings.json_local similarity index 97% rename from settings.json rename to settings.json_local index 3a7dc0b..72a00b8 100644 --- a/settings.json +++ b/settings.json_local @@ -73,6 +73,7 @@ "2030-10-30": "Reformationstag", "2030-12-25": "1. Weihnachtsfeiertag", "2030-12-26": "2. Weihnachtsfeiertag", - "2025-06-11": "Testeintrag" + "2025-06-11": "Testeintrag", + "2025-05-31": "Testeintrag" } } \ No newline at end of file diff --git a/users/testuser10/2025-5.json b/users/testuser10/2025-5.json index ece2b10..942539d 100644 --- a/users/testuser10/2025-5.json +++ b/users/testuser10/2025-5.json @@ -1,5 +1,6 @@ { - "archived": 0, + "archived": 1, "total_hours": 0, - "absence": {} + "absence": {}, + "overtime": -406441 } \ No newline at end of file diff --git a/zeiterfassung.py b/zeiterfassung.py index f6ebcb3..06752bc 100644 --- a/zeiterfassung.py +++ b/zeiterfassung.py @@ -15,7 +15,6 @@ import argparse from lib.web_ui import hash_password - class Commandline_Header: message_string = f"{app_title} {app_version}" underline = "" From c3996c83e488c033f83d68f0a2b80a27ca9ea309 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Mon, 2 Jun 2025 14:11:58 +0200 Subject: [PATCH 2/3] =?UTF-8?q?Dockercheck=20eingef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 3 ++- docker-compose.yml | 5 +++-- lib/admin.py | 19 ++++++++++++++----- lib/definitions.py | 11 +++++++---- lib/users.py | 9 +++------ lib/web_ui.py | 6 +++++- zeiterfassung.py => main.py | 5 +++++ settings.json_local => settings.json | 0 8 files changed, 39 insertions(+), 19 deletions(-) rename zeiterfassung.py => main.py (97%) rename settings.json_local => settings.json (100%) diff --git a/Dockerfile b/Dockerfile index 59e4c5a..f97154d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,7 @@ RUN apt install python3 python3-pip python3.11-venv 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 @@ -19,5 +20,5 @@ COPY main.py /app/main.py COPY favicon.svg /app/favicon.svg COPY lib /app/lib/ EXPOSE 8090 -ENTRYPOINT ["/.venv/bin/python", "/app/main.py", "--docker"] +ENTRYPOINT ["/.venv/bin/python", "/app/main.py"] #ENTRYPOINT exec /app/.venv/bin/python /app/main.py --docker \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 56a3f5c..1bb6566 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,7 @@ services: ports: - 8090:8090 volumes: - - /home/alexander/Dokumente/Python/Zeiterfassung:/app + #- /home/alexander/Dokumente/Python/Zeiterfassung:/app - /home/alexander/Dokumente/Python/Zeiterfassung/users-docker:/users - - /home/alexander/Dokumente/Python/Zeiterfassung/backup:/backup \ No newline at end of file + - /home/alexander/Dokumente/Python/Zeiterfassung/backup:/backup + - /home/alexander/Dokumente/Python/Zeiterfassung/settings:/settings \ No newline at end of file diff --git a/lib/admin.py b/lib/admin.py index 6b3c32e..7b1ed32 100644 --- a/lib/admin.py +++ b/lib/admin.py @@ -44,8 +44,13 @@ def page_admin(): updates_available = ValueBinder() updates_available.value = False - is_docker = ValueBinder - print(docker) + enabled_because_not_docker = ValueBinder + if is_docker(): + enabled_because_not_docker.value = False + scriptpath = "/app" + backupfolder = "/backup" + else: + enabled_because_not_docker.value = True with ui.tabs() as tabs: @@ -762,7 +767,9 @@ Dies kann nicht rückgängig gemacht werden!''') 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') + "Portnummer zu klein": lambda value: len(value)>=2}).tooltip("Geben Sie hier die Portnummer ein, unter der die Zeiterfassung erreichbar ist.").props('size=5').bind_enabled_from(enabled_because_not_docker, 'value') + if is_docker(): + port.tooltip("Diese Einstellung ist beim Einsatz von Docker deaktiviert.") old_port = data["port"] port.value = old_port @@ -1398,7 +1405,7 @@ Dies kann nicht rückgängig gemacht werden!''') ui.label("Backupeinstellungen").classes('font-bold') with ui.grid(columns='auto auto auto'): ui.markdown("Backupordner:") - backupfolder_input = ui.input(value=backupfolder).props(f"size={len(backupfolder)}") + backupfolder_input = ui.input(value=backupfolder).props(f"size={len(backupfolder)}").bind_enabled_from(enabled_because_not_docker, 'value') def save_new_folder_name(): if os.path.exists(backupfolder_input.value): write_adminsetting("backup_folder", backupfolder_input.value) @@ -1410,7 +1417,9 @@ Dies kann nicht rückgängig gemacht werden!''') ui.label("exisitiert nicht und kann daher nicht verwendet werden.") ui.button("OK", on_click=dialog.close) dialog.open() - ui.button("Speichern", on_click=save_new_folder_name).tooltip("Hiermit können Sie das Backupverzeichnis ändeern") + save_backup_folder_button = ui.button("Speichern", on_click=save_new_folder_name).tooltip("Hiermit können Sie das Backupverzeichnis ändeern").bind_enabled_from(enabled_because_not_docker, 'value') + if is_docker(): + save_backup_folder_button.tooltip("Diese Einstellung ist beim Einsatz von Docker deaktiviert.") ui.markdown("API-Schlüssel:") backup_api_key_input = ui.input(value=api_key).tooltip("Hier den API-Schlüssel eintragen, der für Backuperzeugung mittels API-Aufruf verwendet werden soll.") diff --git a/lib/definitions.py b/lib/definitions.py index ebef833..318d9c0 100644 --- a/lib/definitions.py +++ b/lib/definitions.py @@ -3,18 +3,21 @@ import os from pathlib import Path +from lib.web_ui import is_docker import hashlib app_title = "Zeiterfassung" app_version = "0.0.0" -docker = False - # Standardpfade -scriptpath = str(Path(os.path.dirname(os.path.abspath(__file__))).parent.absolute()) +if is_docker(): + scriptpath = "/settings" + backupfolder = "/backup" +else: + scriptpath = str(Path(os.path.dirname(os.path.abspath(__file__))).parent.absolute()) + backupfolder = str(os.path.join(scriptpath, "backup")) userfolder = "users" -backupfolder = str(os.path.join(scriptpath, "backup")) # Dateinamen diff --git a/lib/users.py b/lib/users.py index 4a05b8d..d6acfd0 100644 --- a/lib/users.py +++ b/lib/users.py @@ -14,14 +14,15 @@ import shutil import re from lib.definitions import userfolder, scriptpath, usersettingsfilename, photofilename, status_in, status_out, \ - standard_adminsettings, standard_usersettings, va_file, docker + standard_adminsettings, standard_usersettings, va_file +from web_ui import is_docker # Benutzerklasse class user: def __init__(self, name): - if not scriptpath == "/app": + if not is_docker(): self.userfolder = os.path.join(scriptpath, userfolder, name) else: self.userfolder = os.path.join("/users", name) @@ -32,7 +33,6 @@ class user: #try: with open(self.settingsfile) as json_file: data = json.load(json_file) - print(data) #except: # print("Fehler beim Erstellen des Datenarrays.") # #TODO Hier muss noch Fehlerbehandlungcode hin @@ -304,10 +304,8 @@ class user: return { } def write_notes(self, year, month, day, note_dict): - print(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json")) with open(os.path.join(self.userfolder, f"{int(year)}-{int(month)}.json"), "r") as json_file: json_data = json.load(json_file) - print(json_data) if len(note_dict) == 1: user_info = list(note_dict)[0] json_data["notes"][str(day)] = { } @@ -537,7 +535,6 @@ def new_user(username: str): def load_adminsettings(): # Settingsdatei einlesen settings_filename = os.path.join(scriptpath, usersettingsfilename) - print(settings_filename) if not os.path.exists(settings_filename): print("Keine Einstellungsdatei gefunden. Lege Standarddatei an.") with open(settings_filename, 'w') as json_file: diff --git a/lib/web_ui.py b/lib/web_ui.py index f7f6ace..1050cc4 100644 --- a/lib/web_ui.py +++ b/lib/web_ui.py @@ -118,4 +118,8 @@ def login_is_valid(user = -1): else: return False except: - return False \ No newline at end of file + return False + +def is_docker(): + cgroup = Path('/proc/self/cgroup') + return Path('/.dockerenv').is_file() or (cgroup.is_file() and 'docker' in cgroup.read_text()) \ No newline at end of file diff --git a/zeiterfassung.py b/main.py similarity index 97% rename from zeiterfassung.py rename to main.py index 06752bc..c37808d 100644 --- a/zeiterfassung.py +++ b/main.py @@ -59,6 +59,11 @@ 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 is_docker(): + scriptpath = "/app" + backupfolder = "/backup" + if args.admin_access: Commandline_Header() print("Lade Administrationseinstellungen") diff --git a/settings.json_local b/settings.json similarity index 100% rename from settings.json_local rename to settings.json From daff30b6acac960714d23540d70dadd4f13c8430 Mon Sep 17 00:00:00 2001 From: Alexander Malzkuhn Date: Tue, 3 Jun 2025 13:29:55 +0200 Subject: [PATCH 3/3] Docker Check verschoben commandline_header angepasst --- .gitignore | 1 + docker-compose.yml | 6 +++--- lib/definitions.py | 5 ++++- lib/users.py | 4 +--- lib/web_ui.py | 6 +----- main.py | 7 +++---- 6 files changed, 13 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 1b7ef99..7b5fe5d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ users/ backup/ Archiv/ Docker/ +docker-work/ \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 1bb6566..9695725 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,6 +6,6 @@ services: - 8090:8090 volumes: #- /home/alexander/Dokumente/Python/Zeiterfassung:/app - - /home/alexander/Dokumente/Python/Zeiterfassung/users-docker:/users - - /home/alexander/Dokumente/Python/Zeiterfassung/backup:/backup - - /home/alexander/Dokumente/Python/Zeiterfassung/settings:/settings \ No newline at end of file + - /home/alexander/Dokumente/Python/Zeiterfassung/docker-work/users:/users + - /home/alexander/Dokumente/Python/Zeiterfassung/docker-work/backup:/backup + - /home/alexander/Dokumente/Python/Zeiterfassung/docker-work/settings:/settings \ No newline at end of file diff --git a/lib/definitions.py b/lib/definitions.py index 318d9c0..130b709 100644 --- a/lib/definitions.py +++ b/lib/definitions.py @@ -3,7 +3,6 @@ import os from pathlib import Path -from lib.web_ui import is_docker import hashlib app_title = "Zeiterfassung" @@ -11,6 +10,10 @@ app_version = "0.0.0" # Standardpfade +def is_docker(): + cgroup = Path('/proc/self/cgroup') + return Path('/.dockerenv').is_file() or (cgroup.is_file() and 'docker' in cgroup.read_text()) + if is_docker(): scriptpath = "/settings" backupfolder = "/backup" diff --git a/lib/users.py b/lib/users.py index d6acfd0..16381cf 100644 --- a/lib/users.py +++ b/lib/users.py @@ -14,9 +14,7 @@ import shutil import re from lib.definitions import userfolder, scriptpath, usersettingsfilename, photofilename, status_in, status_out, \ - standard_adminsettings, standard_usersettings, va_file - -from web_ui import is_docker + standard_adminsettings, standard_usersettings, va_file, is_docker # Benutzerklasse diff --git a/lib/web_ui.py b/lib/web_ui.py index 1050cc4..f7f6ace 100644 --- a/lib/web_ui.py +++ b/lib/web_ui.py @@ -118,8 +118,4 @@ def login_is_valid(user = -1): else: return False except: - return False - -def is_docker(): - cgroup = Path('/proc/self/cgroup') - return Path('/.dockerenv').is_file() or (cgroup.is_file() and 'docker' in cgroup.read_text()) \ No newline at end of file + return False \ No newline at end of file diff --git a/main.py b/main.py index c37808d..89c263a 100644 --- a/main.py +++ b/main.py @@ -15,7 +15,7 @@ import argparse from lib.web_ui import hash_password -class Commandline_Header: +def commandline_header(): message_string = f"{app_title} {app_version}" underline = "" for i in range(len(message_string)): @@ -36,8 +36,7 @@ def main(): def startup_message(): - Commandline_Header() - + commandline_header() url_string = "" for i in list(app.urls): url_string += f"{i}, " @@ -65,7 +64,7 @@ if __name__ in ("__main__", "__mp_main__"): backupfolder = "/backup" if args.admin_access: - Commandline_Header() + commandline_header() print("Lade Administrationseinstellungen") admin_settings = load_adminsettings() print("Geben Sie den neuen Benutzernamen für den Administrationsbenutzer an:")