Merge branch 'urlaubsantrag'

This commit is contained in:
Alexander Malzkuhn 2025-05-28 12:46:44 +02:00
commit ad9b6d6be6
10 changed files with 777 additions and 550 deletions

File diff suppressed because it is too large Load Diff

View File

@ -525,7 +525,7 @@ def json_info(api_key: str):
data["time"]["overall"] = time_saldo data["time"]["overall"] = time_saldo
data["vacation"] = { } data["vacation"] = { }
data["vacation"]["claim"] = current_user.get_vacation_claim(now_dt.year, now_dt.month, now_dt.day) data["vacation"]["claim"] = current_user.get_vacation_claim(now_dt.year, now_dt.month, now_dt.day)
data["vacation"]["used"] = current_user.count_vacation_days(now_dt.year) data["vacation"]["used"] = current_user.count_absence_days("U", now_dt.year)
data["vacation"]["remaining"] = data["vacation"]["claim"] - data["vacation"]["used"] data["vacation"]["remaining"] = data["vacation"]["claim"] - data["vacation"]["used"]
return data return data
break break

View File

@ -17,6 +17,7 @@ backupfolder = str(os.path.join(scriptpath, "backup"))
usersettingsfilename = "settings.json" usersettingsfilename = "settings.json"
photofilename = "photo.jpg" photofilename = "photo.jpg"
va_file = "vacation_application.json"
# Status # Status

View File

@ -187,38 +187,92 @@ def homepage():
ui.separator() ui.separator()
with ui.grid(columns='1fr auto 1fr').classes('w-full justify-center'): with ui.tabs().classes('w-full items-center') as tabs:
overviews = ui.tab('Übersichten')
absence = ui.tab('Urlaubsantrag')
with ui.grid(columns='1fr auto 1fr').classes('w-full items-center'):
ui.space() ui.space()
with ui.tab_panels(tabs, value=overviews):
with ui.tab_panel(overviews):
def activate_vacation(): def activate_vacation():
binder_vacation.value = True binder_vacation.value = True
def activate_absence(): def activate_absence():
binder_absence.value = True 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')
month_year_select = ui.select(list(reversed(available_years)), label="Jahr", on_change=update_month).bind_value_to(binder_available_years, 'value') month_year_select = ui.select(list(reversed(available_years)), label="Jahr", on_change=update_month).bind_value_to(binder_available_years, 'value')
month_month_select = ui.select(available_months, label="Monat", on_change=enable_month) month_month_select = ui.select(available_months, label="Monat", on_change=enable_month)
month_month_select.disable() month_month_select.disable()
ui.space() ui.space()
month_button = ui.button("Anzeigen", on_click=lambda: ui.navigate.to(f"/api/month/{current_user.username}/{month_year_select.value}-{month_month_select.value}", new_tab=True)).bind_enabled_from(binder_month_button, 'value') month_button = ui.button("Anzeigen", on_click=lambda: ui.navigate.to(f"/api/month/{current_user.username}/{month_year_select.value}-{month_month_select.value}", new_tab=True)).bind_enabled_from(binder_month_button, 'value')
ui.markdown("**Urlaubsanspruch**").classes('col-span-2') ui.markdown("**Urlaubsanspruch**").classes('col-span-2')
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)), on_change=activate_absence) absences_select = ui.select(list(reversed(available_years)), on_change=activate_absence)
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') 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():
app.storage.user.pop("active_user", None) app.storage.user.pop("active_user", None)
ui.navigate.to("/") ui.navigate.to("/")
ui.button("Logout", on_click=logout).classes('col-span-2') ui.button("Logout", on_click=logout).classes('col-span-2')
with ui.tab_panel(absence):
ui.label("Urlaub für folgenden Zeitraum beantragen:")
vacation_date = ui.date().props('range today-btn')
def vacation_submission():
if vacation_date.value == None:
return None
try:
current_user.vacation_application(vacation_date.value["from"], vacation_date.value["to"])
except TypeError:
current_user.vacation_application(vacation_date.value, vacation_date.value)
vacation_date.value = ""
with ui.dialog() as dialog, ui.card():
ui.label("Urlaubsantrag wurde abgeschickt")
ui.button("OK", on_click=dialog.close)
open_vacation_applications.refresh()
dialog.open()
ui.button("Einreichen", on_click=vacation_submission).classes('w-full items-center').tooltip("Hiermit reichen Sie einen Urlaubsantrag für den oben markierten Zeitraum ein.")
@ui.refreshable
def open_vacation_applications():
open_applications = current_user.get_open_vacation_applications()
if len(list(open_applications)) > 0:
ui.separator()
ui.label("Offene Urlaubsanträge:").classes('font-bold')
va_columns = [ {'label': 'Index', 'name': 'index', 'field': 'index', 'classes': 'hidden', 'headerClasses': 'hidden'},
{'label': 'Start', 'name': 'start', 'field': 'start'},
{'label': 'Ende', 'name': 'end', 'field': 'end'}]
va_rows = [ ]
date_string = '%d.%m.%Y'
for i, dates in open_applications.items():
startdate_dt = datetime.datetime.strptime(dates[0], '%Y-%m-%d')
enddate_dt = datetime.datetime.strptime(dates[1], '%Y-%m-%d')
va_rows.append({'index': i, 'start': startdate_dt.strftime(date_string), 'end': enddate_dt.strftime(date_string)})
va_table = ui.table(columns=va_columns, rows=va_rows, selection="single", row_key="index").classes('w-full')
def retract_va():
try:
retract_result = current_user.revoke_vacation_application(va_table.selected[0]["index"])
open_vacation_applications.refresh()
if retract_result == 0:
ui.notify("Urlaubsantrag zurückgezogen")
except IndexError:
ui.notify("Kein Urlaubsanstrag ausgewählt")
ui.button("Zurückziehen", on_click=retract_va).tooltip("Hiermit wird der oben gewählte Urlaubsantrag zurückgezogen.").classes('w-full')
open_vacation_applications()
ui.space() ui.space()
else: else:

View File

@ -5,13 +5,17 @@ import hashlib
import os import os
from calendar import monthrange from calendar import monthrange
from stat import S_IREAD, S_IWUSR from stat import S_IREAD, S_IWUSR
from nicegui import ui
import datetime import datetime
import time import time
import json import json
import shutil import shutil
import re import re
from lib.definitions import userfolder, scriptpath, usersettingsfilename, photofilename, status_in, status_out, standard_adminsettings, standard_usersettings from lib.definitions import userfolder, scriptpath, usersettingsfilename, photofilename, status_in, status_out, \
standard_adminsettings, standard_usersettings, va_file
# Benutzerklasse # Benutzerklasse
@ -59,7 +63,6 @@ class user:
with open(filename, 'a') as file: with open(filename, 'a') as file:
# Schreibe den Timestamp in die Datei und füge einen Zeilenumbruch hinzu # Schreibe den Timestamp in die Datei und füge einen Zeilenumbruch hinzu
file.write(f"{timestamp}\n") file.write(f"{timestamp}\n")
file.close()
except FileNotFoundError: except FileNotFoundError:
# Fehlende Verzeichnisse anlegen # Fehlende Verzeichnisse anlegen
folder_path = os.path.dirname(filename) folder_path = os.path.dirname(filename)
@ -261,7 +264,7 @@ class user:
filename_txt = os.path.join(self.userfolder, f"{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=datetime.datetime.now().year, month=datetime.datetime.now().month):
try: try:
if int(month) == 1: if int(month) == 1:
year = str(int(year) - 1) year = str(int(year) - 1)
@ -385,7 +388,7 @@ class user:
hours_to_work = -1 hours_to_work = -1
return hours_to_work return hours_to_work
def get_vacation_claim(self, year, month, day): def get_vacation_claim(self, year=datetime.datetime.now().year, month=datetime.datetime.now().month, day=datetime.datetime.now().day):
workhour_entries = list(self.workhours) workhour_entries = list(self.workhours)
workhour_entries.sort() workhour_entries.sort()
day_to_check = datetime.datetime(int(year), int(month), int(day)) day_to_check = datetime.datetime(int(year), int(month), int(day))
@ -403,18 +406,18 @@ class user:
return int(claim) return int(claim)
def count_vacation_days(self, year): def count_absence_days(self, absence_code: str, year=datetime.datetime.now().year):
vacation_used = 0 absence_days = 0
for month in range(0, 13): for month in range(0, 13):
try: try:
absence_dict = self.get_absence(year, month) absence_dict = self.get_absence(year, month)
for entry, absence_type in absence_dict.items(): for entry, absence_type in absence_dict.items():
if absence_type == "U": if absence_type == absence_code:
vacation_used += 1 absence_days += 1
except: except:
pass pass
return vacation_used return absence_days
def delete_photo(self): def delete_photo(self):
os.remove(self.photofile) os.remove(self.photofile)
@ -451,6 +454,44 @@ class user:
return [total_time, in_time_stamp] return [total_time, in_time_stamp]
def vacation_application(self, startdate, enddate):
application_file = os.path.join(self.userfolder, va_file)
try:
with open(application_file, 'r') as json_file:
applications = json.load(json_file)
except FileNotFoundError:
applications = { }
applications[str(len(list(applications)))] = (startdate, enddate)
with open(application_file, 'w') as json_file:
json_file.write(json.dumps(applications, indent=4))
def get_open_vacation_applications(self):
application_file = os.path.join(self.userfolder, va_file)
try:
with open(application_file, 'r') as json_file:
applications = json.load(json_file)
except FileNotFoundError:
applications = { }
return applications
def revoke_vacation_application(self, index):
application_file = os.path.join(self.userfolder, va_file)
with open(application_file, 'r') as json_file:
applications = json.load(json_file)
try:
del(applications[index])
new_applications = { }
new_index = 0
for index, dates in applications.items():
new_applications[new_index] = dates
new_index += 1
with open(application_file, 'w') as json_file:
json_file.write(json.dumps(new_applications, indent=4))
return 0
except KeyError:
ui.notify("Urlaubsantrag wurde schon bearbeitet")
return -1
# Benutzer auflisten # Benutzer auflisten
def list_users(): def list_users():

View File

@ -71,6 +71,7 @@
"2030-10-03": "Tag der deutschen Einheit", "2030-10-03": "Tag der deutschen Einheit",
"2030-10-30": "Reformationstag", "2030-10-30": "Reformationstag",
"2030-12-25": "1. Weihnachtsfeiertag", "2030-12-25": "1. Weihnachtsfeiertag",
"2030-12-26": "2. Weihnachtsfeiertag" "2030-12-26": "2. Weihnachtsfeiertag",
"2025-06-11": "Testeintrag"
} }
} }

View File

@ -0,0 +1 @@
{}

View File

@ -1,8 +1,7 @@
{ {
"username": "testuser10", "username": "testuser10",
"fullname": "Diego Dieci", "fullname": "Diego Dieci",
"password": "123456789", "password": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"api_key": "807518cd5bd85c1e4855d340f9b77b23eac21b7f",
"workhours": { "workhours": {
"2024-04-01": { "2024-04-01": {
"1": "1", "1": "1",
@ -14,5 +13,6 @@
"7": "7", "7": "7",
"vacation": "30" "vacation": "30"
} }
} },
"api_key": "807518cd5bd85c1e4855d340f9b77b23eac21b7f"
} }

View File

@ -1,14 +1,6 @@
{ {
"0": [ "0": [
"2025-05-05", "2025-06-09",
"2025-05-05" "2025-06-19"
],
"1": [
"2025-05-06",
"2025-05-14"
],
"2": [
"2025-05-19",
"2025-05-22"
] ]
} }

View File

@ -0,0 +1,18 @@
{
"username": "testuser2",
"fullname": "testuser2",
"password": "37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f",
"api_key": "84799b1cbb92514f047bc2186cb4b4aafb352d69",
"workhours": {
"2025-05-27": {
"1": 0,
"2": 0,
"3": 0,
"4": 0,
"5": 0,
"6": 0,
"7": 0,
"vacation": 0
}
}
}