Merge branch 'read-only' into web_ui

This commit is contained in:
Alexander Malzkuhn 2025-05-14 11:02:38 +02:00
commit 43b4b6bce5
10 changed files with 95 additions and 75 deletions

View File

@ -11,6 +11,8 @@ from calendar import monthrange
from web_ui import * from web_ui import *
import os.path import os.path
import os
from stat import S_IREAD, S_IRWXU
import hashlib import hashlib
import calendar import calendar
import locale import locale
@ -130,7 +132,7 @@ def page_admin():
def update_month_and_year(): def update_month_and_year():
current_user = user(time_user.value) current_user = user(time_user.value)
# Archivstatus # Archivstatus
days_with_errors = current_user.archiving_validity_check(int(select_year.value), int(select_month.value))
with ui.grid(columns='auto auto auto 1fr 1fr 1fr 1fr') as table_grid: with ui.grid(columns='auto auto auto 1fr 1fr 1fr 1fr') as table_grid:
if int(select_month.value) > 1: if int(select_month.value) > 1:
archive_status = current_user.get_archive_status(int(select_year.value), archive_status = current_user.get_archive_status(int(select_year.value),
@ -140,7 +142,11 @@ def page_admin():
def revoke_archive_status(): def revoke_archive_status():
def revoke_status(): def revoke_status():
filename = f"{current_user.userfolder}/{int(select_year.value)}-{int(select_month.value)}.json" filestring = f"{current_user.userfolder}/{int(select_year.value)}-{int(select_month.value)}"
filename = f"{filestring}.txt"
os.chmod(filename, S_IRWXU)
filename = f"{filestring}.json"
os.chmod(filename, S_IRWXU)
with open(filename, 'r') as json_file: with open(filename, 'r') as json_file:
data = json.load(json_file) data = json.load(json_file)
data["archived"] = 0 data["archived"] = 0
@ -160,7 +166,7 @@ def page_admin():
dialog.open() dialog.open()
if archive_status == True: if archive_status == True:
with ui.row().classes('text-right col-span-6 justify-center'): with ui.row().classes('text-right col-span-7 justify-center'):
ui.button("Archiviert", on_click=revoke_archive_status).classes('bg-transparent text-black') ui.button("Archiviert", on_click=revoke_archive_status).classes('bg-transparent text-black')
ui.separator() ui.separator()
calendar_card.classes('bg-yellow') calendar_card.classes('bg-yellow')
@ -304,22 +310,26 @@ Dies kann nicht rückgängig gemacht werden!''')
timestamp_button = ui.button(datetime.datetime.fromtimestamp(int(timestamps_dict[day][i])).strftime('%H:%M'), on_click=lambda t_stamp=timestamps_dict[day][i], day=day: edit_entry(t_stamp, day)) timestamp_button = ui.button(datetime.datetime.fromtimestamp(int(timestamps_dict[day][i])).strftime('%H:%M'), on_click=lambda t_stamp=timestamps_dict[day][i], day=day: edit_entry(t_stamp, day))
if archive_status: if archive_status:
timestamp_button.disable() timestamp_button.disable()
with ui.row():
# Fehlerhinweis
if day in days_with_errors:
ui.icon('warning', color='red').tooltip("Keine Schlussbuchung").classes('text-2xl')
# Notizen anzeigen # Notizen anzeigen
days_notes = current_user.get_day_notes(select_year.value, select_month.value, day) days_notes = current_user.get_day_notes(select_year.value, select_month.value, day)
if days_notes != { }: if days_notes != { }:
with ui.icon('o_description').classes('text-2xl'): with ui.icon('o_description').classes('text-2xl'):
with ui.tooltip(): with ui.tooltip():
with ui.grid(columns='auto auto'): with ui.grid(columns='auto auto'):
for username, text in days_notes.items(): for username, text in days_notes.items():
admins_name = load_adminsettings()["admin_user"] admins_name = load_adminsettings()["admin_user"]
if username == admins_name: if username == admins_name:
ui.markdown('Administrator:') ui.markdown('Administrator:')
else: else:
ui.markdown(current_user.fullname) ui.markdown(current_user.fullname)
ui.markdown(text) ui.markdown(text)
else: else:
ui.space() ui.space()
# Arbeitszeit Ist bestimmen # Arbeitszeit Ist bestimmen
@ -539,13 +549,15 @@ Dies kann nicht rückgängig gemacht werden!''')
dialog.open() dialog.open()
dialog.move(calendar_card) dialog.move(calendar_card)
with ui.button(icon='menu'): with ui.button(icon='menu') as menu_button:
with ui.menu() as menu: with ui.menu() as menu:
menu_item = ui.menu_item("Zeiteintrag hinzufügen", lambda day=day: add_entry(day)) menu_item = ui.menu_item("Zeiteintrag hinzufügen", lambda day=day: add_entry(day))
if archive_status: if archive_status:
menu_item.disable() menu_item.disable()
ui.separator() ui.separator()
menu_item = ui.menu_item("Notizen bearbeiten", lambda day=day: edit_notes(day)) menu_item = ui.menu_item("Notizen bearbeiten", lambda day=day: edit_notes(day))
if archive_status:
menu_item.disable()
ui.separator() ui.separator()
for i in list(absence_entries): for i in list(absence_entries):
menu_item = ui.menu_item(f"{absence_entries[i]['name']} eintragen", lambda absence_type=i, day=day: add_absence(absence_type, day)) menu_item = ui.menu_item(f"{absence_entries[i]['name']} eintragen", lambda absence_type=i, day=day: add_absence(absence_type, day))
@ -553,6 +565,8 @@ Dies kann nicht rückgängig gemacht werden!''')
menu_item.disable() menu_item.disable()
if str(day) in list(user_absent): if str(day) in list(user_absent):
menu_item.disable() menu_item.disable()
if archive_status:
menu_button.disable()
#ui.button("Eintrag hinzufügen", on_click=lambda day=day: add_entry(day)) #ui.button("Eintrag hinzufügen", on_click=lambda day=day: add_entry(day))
@ -576,6 +590,7 @@ Dies kann nicht rückgängig gemacht werden!''')
current_user = user(time_user.value) current_user = user(time_user.value)
month_header.set_content(f"###Buchungen für **{current_user.fullname}** für **{calendar.month_name[int(select_month.value)]} {select_year.value}**") month_header.set_content(f"###Buchungen für **{current_user.fullname}** für **{calendar.month_name[int(select_month.value)]} {select_year.value}**")
month_header.set_content(f"###Buchungen für **{current_user.fullname}** für **{calendar.month_name[int(select_month.value)]} {select_year.value}**")
timetable() timetable()
button_update = ui.button("Aktualisieren", on_click=timetable.refresh) button_update = ui.button("Aktualisieren", on_click=timetable.refresh)
button_update.move(timetable_header) button_update.move(timetable_header)

18
api.py
View File

@ -21,6 +21,7 @@ def page_overview_month(username: str, year: int, month: int):
try: try:
current_user = user(username) current_user = user(username)
days_with_errors = current_user.archiving_validity_check(year, month)
ui.page_title(f"Bericht für {current_user.fullname} für {calendar.month_name[month]} {year}") ui.page_title(f"Bericht für {current_user.fullname} für {calendar.month_name[month]} {year}")
if current_user.get_archive_status(year, month): if current_user.get_archive_status(year, month):
with ui.column().classes('w-full items-end gap-0'): with ui.column().classes('w-full items-end gap-0'):
@ -80,7 +81,8 @@ def page_overview_month(username: str, year: int, month: int):
color_day = color_weekend color_day = color_weekend
current_day_date = f"{datetime(year, month, day).strftime('%a')}, {day}.{month}.{year}" current_day_date = f"{datetime(year, month, day).strftime('%a')}, {day}.{month}.{year}"
day_text_element = ui.markdown(current_day_date).classes(f'border px-{pad_x} py-{pad_y} bg-{color_day}') with ui.link_target(day):
ui.markdown(current_day_date).classes(f'border px-{pad_x} py-{pad_y} bg-{color_day}')
# Abwesenheitseinträge # Abwesenheitseinträge
@ -100,11 +102,11 @@ def page_overview_month(username: str, year: int, month: int):
for i in range(0, len(timestamps_dict[day]), 2): for i in range(0, len(timestamps_dict[day]), 2):
try: try:
temp_pair = [timestamps_dict[day][i], timestamps_dict[day][i + 1]] temp_pair = [timestamps_dict[day][i], timestamps_dict[day][i + 1]]
booking_text = booking_text + str(datetime.fromtimestamp(temp_pair[0]).strftime('%H:%M')) + "-" + str(datetime.fromtimestamp(temp_pair[1]).strftime('%H:%M')) + "<br>" booking_text = booking_text + str(datetime.fromtimestamp(temp_pair[0]).strftime('%H:%M')) + " - " + str(datetime.fromtimestamp(temp_pair[1]).strftime('%H:%M')) + "<br>"
except: except:
if len(timestamps_dict[day]) % 2 != 0: if len(timestamps_dict[day]) % 2 != 0:
booking_text += datetime.fromtimestamp(int(timestamps_dict[day][i])).strftime('%H:%M') booking_text += datetime.fromtimestamp(int(timestamps_dict[day][i])).strftime('%H:%M') + " - ***Buchung fehlt!***"
booking_text_element = ui.markdown(booking_text).classes(f'border px-{pad_x} py-{pad_y} bg-{booking_color} text-{booking_text_color}') booking_text_element = ui.markdown(booking_text).classes(f'border px-{pad_x} py-{pad_y} bg-{booking_color} text-{booking_text_color}')
@ -258,7 +260,15 @@ def page_overview_month(username: str, year: int, month: int):
dialog.open() dialog.open()
if archivable == True: if archivable == True:
ui.button("Archivieren", on_click=archive_dialog) if len(days_with_errors) > 0:
ui.label("Es gibt Inkonsistenzen in den Buchungen. Folgende Tage müssen überprüft werden:")
with ui.grid(columns=len(days_with_errors)):
for i in days_with_errors:
ui.link(f"{i}.", f'#{i}')
archive_button = ui.button("Archivieren", on_click=archive_dialog)
if len(days_with_errors) > 0:
archive_button.disable()
archive() archive()

View File

@ -1,6 +1,17 @@
from nicegui import ui import json
import urllib.request
from nicegui import ui, app
import segno import segno
@app.get("/data")
async def deliver_data():
with open("settings.json") as json_file:
data = json.load(json_file)
return data
string = "" string = ""
for i in range(1000): for i in range(1000):
string += str(i) string += str(i)

View File

@ -3,6 +3,8 @@ import hashlib
# User bezogene Funktionen # User bezogene Funktionen
import os import os
from calendar import monthrange
from stat import S_IREAD, S_IWUSR
import datetime import datetime
import time import time
import json import json
@ -222,6 +224,25 @@ class user:
except: except:
return -1 return -1
def archiving_validity_check(self, year: int, month: int):
timestampfilename = f"{self.userfolder}/{year}-{month}.txt"
try:
with open(timestampfilename) as timestampfile:
timestamps = timestampfile.readlines()
timestamps.sort()
days_with_errors = [ ]
for day in range(1, monthrange(year, month)[1] + 1):
day_dt = datetime.datetime(year, month, day)
timestamps_of_this_day = [ ]
for i in timestamps:
i_dt = datetime.datetime.fromtimestamp(int(i))
if day_dt.year == i_dt.year and day_dt.month == i_dt.month and day_dt.day == i_dt.day:
timestamps_of_this_day.append(i)
if len(timestamps_of_this_day) % 2 != 0:
days_with_errors.append(day)
return days_with_errors
except:
return [ ]
def archive_hours(self, year, month, overtime: int): def archive_hours(self, year, month, overtime: int):
filename = f"{self.userfolder}/{year}-{month}.json" filename = f"{self.userfolder}/{year}-{month}.json"
@ -230,11 +251,14 @@ class user:
data["archived"] = 1 data["archived"] = 1
data["overtime"] = overtime data["overtime"] = overtime
json_dict = json.dumps(data) json_dict = json.dumps(data, indent=4)
with open(filename, "w") as outputfile: with open(filename, "w") as outputfile:
outputfile.write(json_dict) outputfile.write(json_dict)
# Dateien auf readonly setzen
os.chmod(filename, S_IREAD)
filename_txt = f"{self.userfolder}/{year}-{month}.txt"
os.chmod(filename_txt, S_IREAD)
def get_last_months_overtime(self, year, month): def get_last_months_overtime(self, year, month):
try: try:
if int(month) == 1: if int(month) == 1:

2
users/testuser1/2025-3.json Normal file → Executable file
View File

@ -1 +1 @@
{"archived": 1, "overtime": -528928} {"archived": 0, "overtime": -528928}

32
users/testuser1/2025-3.txt Normal file → Executable file
View File

@ -1,30 +1,4 @@
1743965819
1743965909
1743966022
1743966045
1743966047
1743966049
1743967346
1744889948
1744889966
1744989797
1744989827
1744989830
1744989883
1744989909
1744989914
1744989916
1744991169
1744991171
1744991288
1744991291
1744991473
1744991477
1744991770
1744991777
1745181046
1745181050
1745240760
1745240762
1740996000 1740996000
1740997800 1742460540
1741038540
1742464500

View File

@ -1,6 +1,6 @@
{ {
"archived": 0, "archived": 1,
"overtime": 0, "overtime": -877154,
"absence": { "absence": {
"7": "U", "7": "U",
"8": "K", "8": "K",

View File

@ -1,21 +1,5 @@
1744889948 1744889948
1744890300 1744890300
1744989797
1744989827
1744989830
1744989883
1744989909
1744989914
1744989916
1744991169
1744991171
1744991288
1744991291
1744991473
1744991477
1744991770
1745215200
1745229600
1745390818 1745390818
1745390894 1745390894
1745390894 1745390894

View File

@ -2,7 +2,6 @@
"archived": 0, "archived": 0,
"overtime": 0, "overtime": 0,
"absence": { "absence": {
"14": "U",
"2": "SO" "2": "SO"
}, },
"notes": { "notes": {

View File

@ -6,3 +6,6 @@
1746608922 1746608922
1746609024 1746609024
1746609037 1746609037
1747206908
1747207022
1747813500