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/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f97154d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +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 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 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"] +#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..9695725 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +services: + test: + image: test:0 + restart: always + ports: + - 8090:8090 + volumes: + #- /home/alexander/Dokumente/Python/Zeiterfassung:/app + - /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/admin.py b/lib/admin.py index 9963585..7b1ed32 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,14 @@ def page_admin(): updates_available = ValueBinder() updates_available.value = False + 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: time_overview = ui.tab('Zeitdaten') @@ -760,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 @@ -1396,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) @@ -1408,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.") @@ -1468,7 +1479,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 +1543,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..130b709 100644 --- a/lib/definitions.py +++ b/lib/definitions.py @@ -6,12 +6,21 @@ from pathlib import Path import hashlib app_title = "Zeiterfassung" -app_version = ("0.0.0") +app_version = "0.0.0" # Standardpfade -scriptpath = str(Path(os.path.dirname(os.path.abspath(__file__))).parent.absolute()) + +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" +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 3d37674..16381cf 100644 --- a/lib/users.py +++ b/lib/users.py @@ -14,25 +14,26 @@ 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, is_docker # Benutzerklasse class user: def __init__(self, name): - self.userfolder = os.path.join(scriptpath, userfolder, name) + if not is_docker(): + 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 + #except: + # print("Fehler beim Erstellen des Datenarrays.") + # #TODO Hier muss noch Fehlerbehandlungcode hin self.password = data["password"] self.workhours = data["workhours"] @@ -301,10 +302,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)] = { } diff --git a/zeiterfassung.py b/main.py similarity index 94% rename from zeiterfassung.py rename to main.py index f6ebcb3..89c263a 100644 --- a/zeiterfassung.py +++ b/main.py @@ -15,8 +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)): @@ -37,8 +36,7 @@ def main(): def startup_message(): - Commandline_Header() - + commandline_header() url_string = "" for i in list(app.urls): url_string += f"{i}, " @@ -60,8 +58,13 @@ 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() + commandline_header() print("Lade Administrationseinstellungen") admin_settings = load_adminsettings() print("Geben Sie den neuen Benutzernamen für den Administrationsbenutzer an:") diff --git a/settings.json b/settings.json index 3a7dc0b..72a00b8 100644 --- a/settings.json +++ b/settings.json @@ -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