Archivhandling erweitert
Archivstatus hervorgehoben Archivstatus aufhebbar für Admin Favicon eingefügt
This commit is contained in:
parent
446e588d70
commit
327b4f1666
648
admin.py
648
admin.py
@ -112,344 +112,392 @@ def page_admin():
|
||||
month_header = ui.markdown(f"###Buchungen für **{current_user.fullname}** für **{calendar.month_name[int(select_month.value)]} {select_year.value}**")
|
||||
|
||||
# Tabelle aufbauen
|
||||
with ui.card() as calendar_card:
|
||||
def update_month_and_year():
|
||||
|
||||
with ui.grid(columns='auto auto 1fr 1fr 1fr 1fr') as table_grid:
|
||||
ui.markdown("**Datum**")
|
||||
ui.markdown("**Buchungen**")
|
||||
ui.markdown("**Ist**")
|
||||
ui.markdown("**Soll**")
|
||||
ui.markdown("**Saldo**")
|
||||
ui.space()
|
||||
|
||||
|
||||
@ui.refreshable
|
||||
def timetable():
|
||||
with ui.card() as calendar_card:
|
||||
def update_month_and_year():
|
||||
current_user = user(time_user.value)
|
||||
timestamps = current_user.get_timestamps(year=select_year.value, month=select_month.value)
|
||||
user_absent = current_user.get_absence(year=select_year.value, month=select_month.value)
|
||||
# Dictionary für sortierte Timestamps
|
||||
timestamps_dict = { }
|
||||
# Dictionary mit zunächst leeren Tageinträgen befüllen
|
||||
for day in range(1, monthrange(int(select_year.value), int(select_month.value))[1] + 1):
|
||||
# Jeder Tag bekommt eine leere Liste
|
||||
timestamps_dict[day] = [ ]
|
||||
# Archivstatus
|
||||
|
||||
# Alle Timestamps durchgehen und sie den Dictionaryeinträgen zuordnen:
|
||||
for stamp in timestamps:
|
||||
day_of_month_of_timestamp = int(datetime.datetime.fromtimestamp(int(stamp)).strftime("%-d"))
|
||||
timestamps_dict[day_of_month_of_timestamp].append(int(stamp))
|
||||
|
||||
general_saldo = 0
|
||||
|
||||
for day in list(timestamps_dict):
|
||||
# Datum für Tabelle konstruieren
|
||||
day_in_list = datetime.datetime(int(select_year.value), int(select_month.value), day)
|
||||
class_content = ""
|
||||
if day_in_list.date() == datetime.datetime.now().date():
|
||||
class_content = 'font-bold text-red-700 uppercase'
|
||||
ui.markdown(f"{day_in_list.strftime('%a')}., {day}. {calendar.month_name[int(select_month.value)]}").classes(class_content)
|
||||
|
||||
# Buchungen
|
||||
|
||||
with ui.row():
|
||||
def delete_absence(day, absence_type):
|
||||
def execute_deletion():
|
||||
current_user.del_absence(select_year.value, select_month.value, day)
|
||||
calendar_card.clear()
|
||||
update_month_and_year()
|
||||
dialog.close()
|
||||
ui.notify("Abwesenheitseintrag gelöscht")
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
ui.markdown(f'''Soll der Eintrag **{absence_type}** für den **{day}. {calendar.month_name[int(select_month.value)]} {select_year.value}** gelöscht werden?
|
||||
|
||||
Dies kann nicht rückgägig gemacht werden!''')
|
||||
with ui.grid(columns=3):
|
||||
ui.button("Ja", on_click=execute_deletion)
|
||||
ui.space()
|
||||
ui.button("Nein", on_click=dialog.close)
|
||||
dialog.open()
|
||||
|
||||
try:
|
||||
for i in list(user_absent):
|
||||
if int(i) == day:
|
||||
ui.button(absence_entries[user_absent[i]]["name"], on_click=lambda i=i, day=day: delete_absence(day, absence_entries[user_absent[i]]["name"])).props(f'color={absence_entries[user_absent[i]]["color"]}')
|
||||
except:
|
||||
pass
|
||||
|
||||
day_type = ui.markdown("Kein Arbeitstag")
|
||||
day_type.set_visibility(False)
|
||||
|
||||
# Hier werden nur die Tage mit Timestamps behandelt
|
||||
if len(timestamps_dict[day]) > 0:
|
||||
timestamps_dict[day].sort()
|
||||
|
||||
def edit_entry(t_stamp, day):
|
||||
|
||||
with ui.dialog() as edit_dialog, ui.card():
|
||||
ui.markdown("**Eintrag bearbeiten**")
|
||||
timestamp = datetime.datetime.fromtimestamp(int(t_stamp))
|
||||
input_time = ui.time().props('format24h now-btn').classes('w-full justify-center')
|
||||
|
||||
input_time.value = timestamp.strftime('%H:%M')
|
||||
|
||||
def save_entry(day):
|
||||
nonlocal t_stamp
|
||||
t_stamp = f"{t_stamp}\n"
|
||||
position = timestamps.index(t_stamp)
|
||||
new_time_stamp = datetime.datetime(int(select_year.value),
|
||||
int(select_month.value), day,
|
||||
int(input_time.value[:2]),
|
||||
int(input_time.value[-2:]))
|
||||
timestamps[position] = str(
|
||||
int(new_time_stamp.timestamp())) + "\n"
|
||||
|
||||
current_user = user(time_user.value)
|
||||
current_user.write_edited_timestamps(timestamps,
|
||||
select_year.value,
|
||||
select_month.value)
|
||||
edit_dialog.close()
|
||||
calendar_card.clear()
|
||||
update_month_and_year()
|
||||
month_header.set_content(
|
||||
f"###Buchungen für {calendar.month_name[int(select_month.value)]} {select_year.value}")
|
||||
ui.notify("Eintrag gespeichert")
|
||||
|
||||
def del_entry():
|
||||
nonlocal t_stamp
|
||||
t_stamp = f"{t_stamp}\n"
|
||||
timestamps.remove(t_stamp)
|
||||
timestamps.sort()
|
||||
current_user = user(time_user.value)
|
||||
current_user.write_edited_timestamps(timestamps,
|
||||
select_year.value,
|
||||
select_month.value)
|
||||
edit_dialog.close()
|
||||
calendar_card.clear()
|
||||
update_month_and_year()
|
||||
month_header.set_content(
|
||||
f"###Buchungen für {calendar.month_name[int(select_month.value)]} {select_year.value}")
|
||||
ui.notify("Eintrag gelöscht")
|
||||
|
||||
with ui.row():
|
||||
ui.button("Speichern",
|
||||
on_click=lambda day=day: save_entry(day))
|
||||
ui.button("Löschen", on_click=del_entry)
|
||||
ui.button("Abbrechen", on_click=edit_dialog.close)
|
||||
|
||||
edit_dialog.open()
|
||||
for i in range(len(timestamps_dict[day])):
|
||||
try:
|
||||
temp_pair = [ timestamps_dict[day][i] , timestamps_dict[day][i+1] ]
|
||||
with ui.card():
|
||||
with ui.row():
|
||||
for j in temp_pair:
|
||||
ui.button(datetime.datetime.fromtimestamp(int(j)).strftime('%H:%M'), on_click=lambda t_stamp=j, day=day: edit_entry(t_stamp, day))
|
||||
except:
|
||||
if len(timestamps_dict[day]) % 2 != 0:
|
||||
with ui.card():
|
||||
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))
|
||||
|
||||
# Arbeitszeit Ist bestimmen
|
||||
|
||||
timestamps_of_this_day = []
|
||||
|
||||
# Suche mir alle timestamps für diesen Tag
|
||||
for i in timestamps:
|
||||
actual_timestamp = datetime.datetime.fromtimestamp(int(i))
|
||||
timestamp_day = actual_timestamp.strftime('%-d')
|
||||
|
||||
if int(timestamp_day) == int(day):
|
||||
timestamps_of_this_day.append(i)
|
||||
|
||||
timestamps_of_this_day.sort()
|
||||
|
||||
time_sum = 0
|
||||
if len(timestamps_of_this_day) > 1:
|
||||
|
||||
if len(timestamps_of_this_day) % 2 == 0:
|
||||
for i in range(0, len(timestamps_of_this_day), 2):
|
||||
time_delta = int(
|
||||
timestamps_of_this_day[i + 1]) - int(
|
||||
timestamps_of_this_day[i])
|
||||
time_sum = time_sum + time_delta
|
||||
else:
|
||||
for i in range(0, len(timestamps_of_this_day) - 1, 2):
|
||||
time_delta = int(
|
||||
timestamps_of_this_day[i + 1]) - int(
|
||||
timestamps_of_this_day[i])
|
||||
time_sum = time_sum + time_delta
|
||||
|
||||
ui.markdown(convert_seconds_to_hours(time_sum)).classes('text-right')
|
||||
with ui.grid(columns='auto auto 1fr 1fr 1fr 1fr') as table_grid:
|
||||
if int(select_month.value) > 1:
|
||||
archive_status = current_user.get_archive_status(int(select_year.value),
|
||||
int(select_month.value))
|
||||
else:
|
||||
ui.markdown("Kein")
|
||||
archive_status = current_user.get_archive_status(int(select_year.value) - 1, 12)
|
||||
|
||||
# Arbeitszeitsoll bestimmen
|
||||
def revoke_archive_status():
|
||||
def revoke_status():
|
||||
filename = f"{current_user.userfolder}/{int(select_year.value)}-{int(select_month.value)}.json"
|
||||
with open(filename, 'r') as json_file:
|
||||
data = json.load(json_file)
|
||||
data["archived"] = 0
|
||||
|
||||
hours_to_work = int(current_user.get_day_workhours(select_year.value, select_month.value, day))
|
||||
if hours_to_work < 0:
|
||||
ui.space()
|
||||
else:
|
||||
ui.markdown(f"{convert_seconds_to_hours(int(hours_to_work) * 3600)}").classes('text-right')
|
||||
if int(hours_to_work) == 0:
|
||||
day_type.content = "**Kein Arbeitstag**"
|
||||
day_type.set_visibility(True)
|
||||
json_dict = json.dumps(data)
|
||||
|
||||
if day_in_list.strftime("%Y-%m-%d") in data["holidays"]:
|
||||
day_type.content = f'**{data["holidays"][day_in_list.strftime("%Y-%m-%d")]}**'
|
||||
with open(filename, "w") as outputfile:
|
||||
outputfile.write(json_dict)
|
||||
timetable.refresh()
|
||||
dialog.close()
|
||||
|
||||
# Saldo für den Tag berechnen
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
ui.label("Soll der Archivstatus für den aktuellen Monat aufgehoben werden, damit Änderungen vorgenommen werden können?")
|
||||
with ui.grid(columns=2):
|
||||
ui.button("Ja", on_click=revoke_status)
|
||||
ui.button("Nein", on_click=dialog.close)
|
||||
dialog.open()
|
||||
|
||||
if time.time() > day_in_list.timestamp():
|
||||
if archive_status:
|
||||
with ui.row().classes('text-right col-span-6 justify-center'):
|
||||
ui.button("Archiviert", on_click=revoke_archive_status).classes('bg-transparent text-black')
|
||||
ui.separator()
|
||||
calendar_card.classes('bg-yellow')
|
||||
# Überschriften
|
||||
ui.markdown("**Datum**")
|
||||
ui.markdown("**Buchungen**")
|
||||
ui.markdown("**Ist**")
|
||||
ui.markdown("**Soll**")
|
||||
ui.markdown("**Saldo**")
|
||||
ui.space()
|
||||
|
||||
timestamps = current_user.get_timestamps(year=select_year.value, month=select_month.value)
|
||||
user_absent = current_user.get_absence(year=select_year.value, month=select_month.value)
|
||||
# Dictionary für sortierte Timestamps
|
||||
timestamps_dict = { }
|
||||
# Dictionary mit zunächst leeren Tageinträgen befüllen
|
||||
for day in range(1, monthrange(int(select_year.value), int(select_month.value))[1] + 1):
|
||||
# Jeder Tag bekommt eine leere Liste
|
||||
timestamps_dict[day] = [ ]
|
||||
|
||||
# Alle Timestamps durchgehen und sie den Dictionaryeinträgen zuordnen:
|
||||
for stamp in timestamps:
|
||||
day_of_month_of_timestamp = int(datetime.datetime.fromtimestamp(int(stamp)).strftime("%-d"))
|
||||
timestamps_dict[day_of_month_of_timestamp].append(int(stamp))
|
||||
|
||||
general_saldo = 0
|
||||
|
||||
for day in list(timestamps_dict):
|
||||
# Datum für Tabelle konstruieren
|
||||
day_in_list = datetime.datetime(int(select_year.value), int(select_month.value), day)
|
||||
class_content = ""
|
||||
if day_in_list.date() == datetime.datetime.now().date():
|
||||
class_content = 'font-bold text-red-700 uppercase'
|
||||
ui.markdown(f"{day_in_list.strftime('%a')}., {day}. {calendar.month_name[int(select_month.value)]}").classes(class_content)
|
||||
|
||||
# Buchungen
|
||||
|
||||
with ui.row():
|
||||
def delete_absence(day, absence_type):
|
||||
def execute_deletion():
|
||||
current_user.del_absence(select_year.value, select_month.value, day)
|
||||
calendar_card.clear()
|
||||
update_month_and_year()
|
||||
dialog.close()
|
||||
ui.notify("Abwesenheitseintrag gelöscht")
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
ui.markdown(f'''Soll der Eintrag **{absence_type}** für den **{day}. {calendar.month_name[int(select_month.value)]} {select_year.value}** gelöscht werden?
|
||||
|
||||
Dies kann nicht rückgägig gemacht werden!''')
|
||||
with ui.grid(columns=3):
|
||||
ui.button("Ja", on_click=execute_deletion)
|
||||
ui.space()
|
||||
ui.button("Nein", on_click=dialog.close)
|
||||
dialog.open()
|
||||
|
||||
time_duty = int(current_user.get_day_workhours(select_year.value, select_month.value, day)) * 3600
|
||||
if time_duty < 0:
|
||||
ui.space()
|
||||
else:
|
||||
saldo = int(time_sum) - int(time_duty)
|
||||
# Nach Abwesenheitseinträgen suchen
|
||||
try:
|
||||
for i in list(user_absent):
|
||||
if int(i) == day and user_absent[i] != "UU":
|
||||
saldo = 0
|
||||
if int(i) == day:
|
||||
absence_button = ui.button(absence_entries[user_absent[i]]["name"], on_click=lambda i=i, day=day: delete_absence(day, absence_entries[user_absent[i]]["name"])).props(f'color={absence_entries[user_absent[i]]["color"]}')
|
||||
if archive_status:
|
||||
absence_button.disable()
|
||||
except:
|
||||
pass
|
||||
|
||||
general_saldo = general_saldo + saldo
|
||||
ui.markdown(convert_seconds_to_hours(saldo)).classes('text-right')
|
||||
else:
|
||||
ui.markdown("-").classes('text-center')
|
||||
day_type = ui.markdown("Kein Arbeitstag")
|
||||
day_type.set_visibility(False)
|
||||
|
||||
def add_entry(day):
|
||||
with ui.dialog() as add_dialog, ui.card():
|
||||
ui.markdown("###Eintrag hinzufügen")
|
||||
input_time = ui.time().classes('w-full justify-center')
|
||||
# Hier werden nur die Tage mit Timestamps behandelt
|
||||
if len(timestamps_dict[day]) > 0:
|
||||
timestamps_dict[day].sort()
|
||||
|
||||
def add_entry_save():
|
||||
if input_time.value == None:
|
||||
ui.notify("Bitte eine Uhrzeit auswählen.")
|
||||
return
|
||||
def edit_entry(t_stamp, day):
|
||||
|
||||
new_time_stamp = datetime.datetime(int(year_binder.value),
|
||||
int(month_binder.value), day,
|
||||
int(input_time.value[:2]),
|
||||
int(input_time.value[-2:])).timestamp()
|
||||
current_user = user(time_user.value)
|
||||
current_user.timestamp(stamptime=int(new_time_stamp))
|
||||
calendar_card.clear()
|
||||
update_month_and_year()
|
||||
add_dialog.close()
|
||||
ui.notify("Eintrag hinzugefügt")
|
||||
with ui.grid(columns=3):
|
||||
ui.button("Speichern", on_click=add_entry_save)
|
||||
with ui.dialog() as edit_dialog, ui.card():
|
||||
ui.markdown("**Eintrag bearbeiten**")
|
||||
timestamp = datetime.datetime.fromtimestamp(int(t_stamp))
|
||||
input_time = ui.time().props('format24h now-btn').classes('w-full justify-center')
|
||||
|
||||
input_time.value = timestamp.strftime('%H:%M')
|
||||
|
||||
def save_entry(day):
|
||||
nonlocal t_stamp
|
||||
t_stamp = f"{t_stamp}\n"
|
||||
position = timestamps.index(t_stamp)
|
||||
new_time_stamp = datetime.datetime(int(select_year.value),
|
||||
int(select_month.value), day,
|
||||
int(input_time.value[:2]),
|
||||
int(input_time.value[-2:]))
|
||||
timestamps[position] = str(
|
||||
int(new_time_stamp.timestamp())) + "\n"
|
||||
|
||||
current_user = user(time_user.value)
|
||||
current_user.write_edited_timestamps(timestamps,
|
||||
select_year.value,
|
||||
select_month.value)
|
||||
edit_dialog.close()
|
||||
calendar_card.clear()
|
||||
update_month_and_year()
|
||||
month_header.set_content(
|
||||
f"###Buchungen für {calendar.month_name[int(select_month.value)]} {select_year.value}")
|
||||
ui.notify("Eintrag gespeichert")
|
||||
|
||||
def del_entry():
|
||||
nonlocal t_stamp
|
||||
t_stamp = f"{t_stamp}\n"
|
||||
timestamps.remove(t_stamp)
|
||||
timestamps.sort()
|
||||
current_user = user(time_user.value)
|
||||
current_user.write_edited_timestamps(timestamps,
|
||||
select_year.value,
|
||||
select_month.value)
|
||||
edit_dialog.close()
|
||||
calendar_card.clear()
|
||||
update_month_and_year()
|
||||
month_header.set_content(
|
||||
f"###Buchungen für {calendar.month_name[int(select_month.value)]} {select_year.value}")
|
||||
ui.notify("Eintrag gelöscht")
|
||||
|
||||
with ui.row():
|
||||
ui.button("Speichern",
|
||||
on_click=lambda day=day: save_entry(day))
|
||||
ui.button("Löschen", on_click=del_entry)
|
||||
ui.button("Abbrechen", on_click=edit_dialog.close)
|
||||
|
||||
edit_dialog.open()
|
||||
for i in range(len(timestamps_dict[day])):
|
||||
try:
|
||||
temp_pair = [ timestamps_dict[day][i] , timestamps_dict[day][i+1] ]
|
||||
with ui.card().classes('bg-inherit'):
|
||||
with ui.row():
|
||||
for j in temp_pair:
|
||||
timestamp_button = ui.button(datetime.datetime.fromtimestamp(int(j)).strftime('%H:%M'), on_click=lambda t_stamp=j, day=day: edit_entry(t_stamp, day))
|
||||
if archive_status:
|
||||
timestamp_button.disable()
|
||||
except:
|
||||
if len(timestamps_dict[day]) % 2 != 0:
|
||||
with ui.card().classes('bg-inherit'):
|
||||
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:
|
||||
timestamp_button.disable()
|
||||
|
||||
# Arbeitszeit Ist bestimmen
|
||||
|
||||
timestamps_of_this_day = []
|
||||
|
||||
# Suche mir alle timestamps für diesen Tag
|
||||
for i in timestamps:
|
||||
actual_timestamp = datetime.datetime.fromtimestamp(int(i))
|
||||
timestamp_day = actual_timestamp.strftime('%-d')
|
||||
|
||||
if int(timestamp_day) == int(day):
|
||||
timestamps_of_this_day.append(i)
|
||||
|
||||
timestamps_of_this_day.sort()
|
||||
|
||||
time_sum = 0
|
||||
if len(timestamps_of_this_day) > 1:
|
||||
|
||||
if len(timestamps_of_this_day) % 2 == 0:
|
||||
for i in range(0, len(timestamps_of_this_day), 2):
|
||||
time_delta = int(
|
||||
timestamps_of_this_day[i + 1]) - int(
|
||||
timestamps_of_this_day[i])
|
||||
time_sum = time_sum + time_delta
|
||||
else:
|
||||
for i in range(0, len(timestamps_of_this_day) - 1, 2):
|
||||
time_delta = int(
|
||||
timestamps_of_this_day[i + 1]) - int(
|
||||
timestamps_of_this_day[i])
|
||||
time_sum = time_sum + time_delta
|
||||
|
||||
ui.markdown(convert_seconds_to_hours(time_sum)).classes('text-right')
|
||||
else:
|
||||
ui.markdown("Kein")
|
||||
|
||||
# Arbeitszeitsoll bestimmen
|
||||
|
||||
hours_to_work = int(current_user.get_day_workhours(select_year.value, select_month.value, day))
|
||||
if hours_to_work < 0:
|
||||
ui.space()
|
||||
else:
|
||||
ui.markdown(f"{convert_seconds_to_hours(int(hours_to_work) * 3600)}").classes('text-right')
|
||||
if int(hours_to_work) == 0:
|
||||
day_type.content = "**Kein Arbeitstag**"
|
||||
day_type.set_visibility(True)
|
||||
|
||||
if day_in_list.strftime("%Y-%m-%d") in data["holidays"]:
|
||||
day_type.content = f'**{data["holidays"][day_in_list.strftime("%Y-%m-%d")]}**'
|
||||
|
||||
# Saldo für den Tag berechnen
|
||||
|
||||
if time.time() > day_in_list.timestamp():
|
||||
|
||||
time_duty = int(current_user.get_day_workhours(select_year.value, select_month.value, day)) * 3600
|
||||
if time_duty < 0:
|
||||
ui.space()
|
||||
ui.button("Abbrechen", on_click=add_dialog.close)
|
||||
add_dialog.open()
|
||||
add_dialog.move(calendar_card)
|
||||
|
||||
def add_absence(absence_type, day):
|
||||
with ui.dialog() as dialog, ui.card().classes('w-[350px]'):
|
||||
ui.markdown(f'Für welchen Zeitraum soll *{absence_entries[absence_type]["name"]}* eingetragen werden?').classes('w-full')
|
||||
absence_dates = ui.date().props('range today-btn').classes('w-full justify-center')
|
||||
absence_dates._props['locale'] = {'daysShort': ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'],
|
||||
'months': ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
|
||||
'monthsShort': ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez']}
|
||||
absence_dates._props['title'] = absence_entries[absence_type]["name"]
|
||||
absence_dates._props['minimal'] = True
|
||||
|
||||
if day < 10:
|
||||
day = f"0{str(day)}"
|
||||
else:
|
||||
day = str(day)
|
||||
if int(select_month.value) < 10:
|
||||
month = f"0{select_month.value}"
|
||||
else:
|
||||
month = select_month.value
|
||||
absence_dates.value = f"{select_year.value}-{month}-{day}"
|
||||
saldo = int(time_sum) - int(time_duty)
|
||||
# Nach Abwesenheitseinträgen suchen
|
||||
try:
|
||||
for i in list(user_absent):
|
||||
if int(i) == day and user_absent[i] != "UU":
|
||||
saldo = 0
|
||||
except:
|
||||
pass
|
||||
|
||||
def add_absence_save():
|
||||
# Bei nur einem Datum, direkt schreiben
|
||||
if isinstance(absence_dates.value, str):
|
||||
absence_date = absence_dates.value.split("-")
|
||||
current_user.update_absence(absence_date[0], absence_date[1], absence_date[2], absence_type)
|
||||
current_sel_month = select_month.value
|
||||
current_sel_year = select_year.value
|
||||
update_user()
|
||||
update_months()
|
||||
select_year.value = current_sel_year
|
||||
select_month.value = current_sel_month
|
||||
general_saldo = general_saldo + saldo
|
||||
ui.markdown(convert_seconds_to_hours(saldo)).classes('text-right')
|
||||
else:
|
||||
ui.markdown("-").classes('text-center')
|
||||
|
||||
def add_entry(day):
|
||||
with ui.dialog() as add_dialog, ui.card():
|
||||
ui.markdown("###Eintrag hinzufügen")
|
||||
input_time = ui.time().classes('w-full justify-center')
|
||||
|
||||
def add_entry_save():
|
||||
if input_time.value == None:
|
||||
ui.notify("Bitte eine Uhrzeit auswählen.")
|
||||
return
|
||||
|
||||
new_time_stamp = datetime.datetime(int(year_binder.value),
|
||||
int(month_binder.value), day,
|
||||
int(input_time.value[:2]),
|
||||
int(input_time.value[-2:])).timestamp()
|
||||
current_user = user(time_user.value)
|
||||
current_user.timestamp(stamptime=int(new_time_stamp))
|
||||
calendar_card.clear()
|
||||
update_month_and_year()
|
||||
add_dialog.close()
|
||||
ui.notify("Eintrag hinzugefügt")
|
||||
with ui.grid(columns=3):
|
||||
ui.button("Speichern", on_click=add_entry_save)
|
||||
ui.space()
|
||||
ui.button("Abbrechen", on_click=add_dialog.close)
|
||||
add_dialog.open()
|
||||
add_dialog.move(calendar_card)
|
||||
|
||||
# Bei Zeitbereich, aufteilen
|
||||
elif isinstance(absence_dates.value, dict):
|
||||
start_date = absence_dates.value["from"]
|
||||
end_date = absence_dates.value["to"]
|
||||
start_date = start_date.split("-")
|
||||
end_date = end_date.split("-")
|
||||
def add_absence(absence_type, day):
|
||||
with ui.dialog() as dialog, ui.card().classes('w-[350px]'):
|
||||
ui.markdown(f'Für welchen Zeitraum soll *{absence_entries[absence_type]["name"]}* eingetragen werden?').classes('w-full')
|
||||
absence_dates = ui.date().props('range today-btn').classes('w-full justify-center')
|
||||
absence_dates._props['locale'] = {'daysShort': ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'],
|
||||
'months': ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
|
||||
'monthsShort': ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez']}
|
||||
absence_dates._props['title'] = absence_entries[absence_type]["name"]
|
||||
absence_dates._props['minimal'] = True
|
||||
|
||||
start_year = int(start_date[0])
|
||||
end_year = int(end_date[0])
|
||||
start_month = int(start_date[1])
|
||||
end_month = int(end_date[1])
|
||||
start_day = int(start_date[2])
|
||||
end_day = int(end_date[2])
|
||||
if day < 10:
|
||||
day = f"0{str(day)}"
|
||||
else:
|
||||
day = str(day)
|
||||
if int(select_month.value) < 10:
|
||||
month = f"0{select_month.value}"
|
||||
else:
|
||||
month = select_month.value
|
||||
absence_dates.value = f"{select_year.value}-{month}-{day}"
|
||||
|
||||
start_date = datetime.datetime(start_year, start_month, start_day)
|
||||
end_date = datetime.datetime(end_year, end_month, end_day)
|
||||
actual_date = start_date
|
||||
def add_absence_save():
|
||||
# Bei nur einem Datum, direkt schreiben
|
||||
if isinstance(absence_dates.value, str):
|
||||
absence_date = absence_dates.value.split("-")
|
||||
current_user.update_absence(absence_date[0], absence_date[1], absence_date[2], absence_type)
|
||||
current_sel_month = select_month.value
|
||||
current_sel_year = select_year.value
|
||||
update_user()
|
||||
update_months()
|
||||
select_year.value = current_sel_year
|
||||
select_month.value = current_sel_month
|
||||
calendar_card.clear()
|
||||
update_month_and_year()
|
||||
|
||||
while actual_date <= end_date:
|
||||
absences = current_user.get_absence(actual_date.year, actual_date.month)
|
||||
# Bei Zeitbereich, aufteilen
|
||||
elif isinstance(absence_dates.value, dict):
|
||||
start_date = absence_dates.value["from"]
|
||||
end_date = absence_dates.value["to"]
|
||||
start_date = start_date.split("-")
|
||||
end_date = end_date.split("-")
|
||||
|
||||
if str(actual_date.day) in list(absences):
|
||||
current_user.del_absence(actual_date.year, actual_date.month, actual_date.day)
|
||||
ui.notify(f"Eintrag {absence_entries[absences[str(actual_date.day)]]['name']} am {actual_date.day}.{actual_date.month}.{actual_date.year} überschrieben.")
|
||||
current_user.update_absence(actual_date.year, actual_date.month, actual_date.day, absence_type)
|
||||
start_year = int(start_date[0])
|
||||
end_year = int(end_date[0])
|
||||
start_month = int(start_date[1])
|
||||
end_month = int(end_date[1])
|
||||
start_day = int(start_date[2])
|
||||
end_day = int(end_date[2])
|
||||
|
||||
actual_date = actual_date + datetime.timedelta(days=1)
|
||||
clear_card()
|
||||
ui.notify("Einträge vorgenomomen")
|
||||
dialog.close()
|
||||
start_date = datetime.datetime(start_year, start_month, start_day)
|
||||
end_date = datetime.datetime(end_year, end_month, end_day)
|
||||
actual_date = start_date
|
||||
|
||||
while actual_date <= end_date:
|
||||
absences = current_user.get_absence(actual_date.year, actual_date.month)
|
||||
|
||||
if str(actual_date.day) in list(absences):
|
||||
current_user.del_absence(actual_date.year, actual_date.month, actual_date.day)
|
||||
ui.notify(f"Eintrag {absence_entries[absences[str(actual_date.day)]]['name']} am {actual_date.day}.{actual_date.month}.{actual_date.year} überschrieben.")
|
||||
current_user.update_absence(actual_date.year, actual_date.month, actual_date.day, absence_type)
|
||||
|
||||
actual_date = actual_date + datetime.timedelta(days=1)
|
||||
clear_card()
|
||||
ui.notify("Einträge vorgenomomen")
|
||||
dialog.close()
|
||||
|
||||
|
||||
with ui.grid(columns=3).classes('w-full justify-center'):
|
||||
ui.button("Speichern", on_click=add_absence_save)
|
||||
ui.space()
|
||||
ui.button("Abbrechen", on_click=dialog.close)
|
||||
with ui.grid(columns=3).classes('w-full justify-center'):
|
||||
ui.button("Speichern", on_click=add_absence_save)
|
||||
ui.space()
|
||||
ui.button("Abbrechen", on_click=dialog.close)
|
||||
|
||||
dialog.open()
|
||||
dialog.move(calendar_card)
|
||||
dialog.open()
|
||||
dialog.move(calendar_card)
|
||||
|
||||
with ui.button(icon='menu'):
|
||||
with ui.menu() as menu:
|
||||
ui.menu_item("Zeiteintrag hinzufügen", lambda day=day: add_entry(day))
|
||||
ui.separator()
|
||||
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))
|
||||
if str(day) in list(user_absent):
|
||||
with ui.button(icon='menu'):
|
||||
with ui.menu() as menu:
|
||||
menu_item = ui.menu_item("Zeiteintrag hinzufügen", lambda day=day: add_entry(day))
|
||||
if archive_status:
|
||||
menu_item.disable()
|
||||
ui.separator()
|
||||
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))
|
||||
if archive_status:
|
||||
menu_item.disable()
|
||||
if str(day) in list(user_absent):
|
||||
menu_item.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))
|
||||
|
||||
#4x leer und dann Gesamtsaldo
|
||||
ui.space().classes('col-span-4')
|
||||
ui.markdown(f"{convert_seconds_to_hours(general_saldo)}").classes('text-right')
|
||||
ui.markdown("Stunden aus Vormonat").classes('col-span-4 text-right')
|
||||
last_months_overtime = current_user.get_last_months_overtime(select_year.value, select_month.value)
|
||||
ui.markdown(f"{convert_seconds_to_hours(last_months_overtime)}").classes('text-right')
|
||||
ui.markdown("Gesamtsaldo").classes('col-span-4 text-right')
|
||||
ui.markdown(f"**<ins>{convert_seconds_to_hours(general_saldo + last_months_overtime)}</ins>**").classes('text-right')
|
||||
table_grid.move(calendar_card)
|
||||
update_month_and_year()
|
||||
#4x leer und dann Gesamtsaldo
|
||||
ui.space().classes('col-span-4')
|
||||
ui.markdown(f"{convert_seconds_to_hours(general_saldo)}").classes('text-right')
|
||||
ui.markdown("Stunden aus Vormonat").classes('col-span-4 text-right')
|
||||
last_months_overtime = current_user.get_last_months_overtime(select_year.value, select_month.value)
|
||||
ui.markdown(f"{convert_seconds_to_hours(last_months_overtime)}").classes('text-right')
|
||||
ui.markdown("Gesamtsaldo").classes('col-span-4 text-right')
|
||||
ui.markdown(f"**<ins>{convert_seconds_to_hours(general_saldo + last_months_overtime)}</ins>**").classes('text-right')
|
||||
|
||||
table_grid.move(calendar_card)
|
||||
|
||||
def clear_card():
|
||||
calendar_card.clear()
|
||||
update_month_and_year()
|
||||
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}**")
|
||||
|
||||
button_update = ui.button("Aktualisieren", on_click=clear_card)
|
||||
button_update.move(timetable_header)
|
||||
def clear_card():
|
||||
calendar_card.clear()
|
||||
update_month_and_year()
|
||||
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}**")
|
||||
|
||||
button_update = ui.button("Aktualisieren", on_click=clear_card)
|
||||
button_update.move(timetable_header)
|
||||
timetable()
|
||||
|
||||
with ui.tab_panel(settings):
|
||||
with ui.card():
|
||||
|
16
api.py
16
api.py
@ -22,8 +22,14 @@ def page_overview_month(username: str, year: int, month: int):
|
||||
try:
|
||||
current_user = user(username)
|
||||
ui.page_title(f"Bericht für {current_user.fullname} für {calendar.month_name[month]} {year}")
|
||||
ui.label(datetime.now().strftime('%d.%m.%Y')).classes('absolute top-5 right-5')
|
||||
ui.space()
|
||||
if current_user.get_archive_status(year, month):
|
||||
with ui.column().classes('w-full items-end gap-0'):
|
||||
ui.label(f"Bericht erstellt am {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}")
|
||||
ui.label('Archiviert').classes('italic').classes('text-red text-bold text-xl')
|
||||
#ui.add_head_html('<style>body {background-color: #FFF7B1; }</style>')
|
||||
else:
|
||||
with ui.column().classes('w-full items-end gap-0'):
|
||||
ui.label(f"Bericht erstellt am {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}")
|
||||
ui.markdown(f'#Bericht für {current_user.fullname} für {calendar.month_name[month]} {year}')
|
||||
|
||||
pad_x = 4
|
||||
@ -55,7 +61,11 @@ def page_overview_month(username: str, year: int, month: int):
|
||||
|
||||
general_saldo = 0
|
||||
|
||||
with ui.grid(columns='auto auto 1fr 1fr 1fr').classes(f'gap-0 border px-0 py-0'):
|
||||
bg_color = ''
|
||||
if current_user.get_archive_status(year, month):
|
||||
bg_color = ' bg-yellow-100'
|
||||
|
||||
with ui.grid(columns='auto auto 1fr 1fr 1fr').classes(f'gap-0 border px-0 py-0{bg_color}'):
|
||||
ui.markdown("**Datum**").classes(f'border px-{pad_x} py-{pad_y}')
|
||||
ui.markdown("**Buchungen**").classes(f'border px-{pad_x} py-{pad_y}')
|
||||
ui.markdown("**Ist**").classes(f'border px-{pad_x} py-{pad_y}')
|
||||
|
118
favicon.svg
Normal file
118
favicon.svg
Normal file
@ -0,0 +1,118 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:ns1="http://sozi.baierouge.fr"
|
||||
xmlns:cc="http://web.resource.org/cc/"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
id="svg1"
|
||||
sodipodi:docname="timeedit.svg"
|
||||
viewBox="0 0 60 60"
|
||||
sodipodi:version="0.32"
|
||||
_SVGFile__filename="oldscale/actions/todo.svg"
|
||||
version="1.0"
|
||||
y="0"
|
||||
x="0"
|
||||
inkscape:version="0.40"
|
||||
sodipodi:docbase="/home/danny/work/flat/SVG/mono/EXTRA/kexi"
|
||||
>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
bordercolor="#666666"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-y="0"
|
||||
pagecolor="#ffffff"
|
||||
inkscape:window-height="698"
|
||||
inkscape:zoom="5.5342875"
|
||||
inkscape:window-x="0"
|
||||
borderopacity="1.0"
|
||||
inkscape:current-layer="svg1"
|
||||
inkscape:cx="36.490405"
|
||||
inkscape:cy="19.560172"
|
||||
inkscape:window-width="1024"
|
||||
inkscape:pageopacity="0.0"
|
||||
/>
|
||||
<g
|
||||
id="g1113"
|
||||
transform="matrix(1.8141 0 0 1.8141 -24.352 -32.241)"
|
||||
>
|
||||
<path
|
||||
id="path1894"
|
||||
style="stroke-linejoin:round;stroke:#ffffff;stroke-linecap:round;stroke-width:5.5124;fill:none"
|
||||
d="m43.399 34.31c0 7.418-6.02 13.438-13.438 13.438s-13.438-6.02-13.438-13.438 6.02-13.438 13.438-13.438 13.438 6.02 13.438 13.438z"
|
||||
/>
|
||||
<path
|
||||
id="path741"
|
||||
style="stroke-linejoin:round;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-width:2.7562;fill:#ffffff"
|
||||
d="m43.399 34.31c0 7.418-6.02 13.438-13.438 13.438s-13.438-6.02-13.438-13.438 6.02-13.438 13.438-13.438 13.438 6.02 13.438 13.438z"
|
||||
/>
|
||||
<path
|
||||
id="path743"
|
||||
sodipodi:nodetypes="cc"
|
||||
style="stroke-linejoin:round;stroke:#000000;stroke-linecap:round;stroke-width:2.7562;fill:none"
|
||||
d="m29.961 34.169v-8.723"
|
||||
/>
|
||||
<path
|
||||
id="path744"
|
||||
style="stroke-linejoin:round;stroke:#000000;stroke-linecap:round;stroke-width:2.7562;fill:none"
|
||||
d="m30.185 34.066l5.869 3.388"
|
||||
/>
|
||||
<path
|
||||
id="path742"
|
||||
style="stroke-linejoin:round;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-width:1.7226;fill:#000000"
|
||||
d="m31.178 34.31c0 0.672-0.545 1.217-1.217 1.217s-1.217-0.545-1.217-1.217 0.545-1.218 1.217-1.218 1.217 0.546 1.217 1.218z"
|
||||
/>
|
||||
</g
|
||||
>
|
||||
<metadata
|
||||
>
|
||||
<rdf:RDF
|
||||
>
|
||||
<cc:Work
|
||||
>
|
||||
<dc:format
|
||||
>image/svg+xml</dc:format
|
||||
>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage"
|
||||
/>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/publicdomain/"
|
||||
/>
|
||||
<dc:publisher
|
||||
>
|
||||
<cc:Agent
|
||||
rdf:about="http://openclipart.org/"
|
||||
>
|
||||
<dc:title
|
||||
>Openclipart</dc:title
|
||||
>
|
||||
</cc:Agent
|
||||
>
|
||||
</dc:publisher
|
||||
>
|
||||
</cc:Work
|
||||
>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/publicdomain/"
|
||||
>
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction"
|
||||
/>
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution"
|
||||
/>
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks"
|
||||
/>
|
||||
</cc:License
|
||||
>
|
||||
</rdf:RDF
|
||||
>
|
||||
</metadata
|
||||
>
|
||||
</svg
|
||||
>
|
After Width: | Height: | Size: 3.6 KiB |
18
main.py
18
main.py
@ -22,7 +22,23 @@ def main():
|
||||
|
||||
homepage()
|
||||
|
||||
ui.run(port=port, storage_secret=secret, language='de-DE')
|
||||
def startup_message():
|
||||
|
||||
message_string = f"{app_title} {app_version}"
|
||||
underline = ""
|
||||
for i in range(len(message_string)):
|
||||
underline += "-"
|
||||
print(message_string)
|
||||
print(underline)
|
||||
|
||||
url_string = ""
|
||||
for i in list(app.urls):
|
||||
url_string += f"{i}, "
|
||||
url_string = url_string[0:-2]
|
||||
print("Weboberfläche erreichbar unter: " + url_string)
|
||||
|
||||
app.on_startup(startup_message)
|
||||
ui.run(favicon="favicon.svg", port=port, storage_secret=secret, language='de-DE', show_welcome_message=False)
|
||||
|
||||
if __name__ in ("__main__", "__mp_main__"):
|
||||
main()
|
||||
|
@ -1,2 +1,2 @@
|
||||
1746385124
|
||||
1746385127
|
||||
1746388680
|
||||
|
Loading…
x
Reference in New Issue
Block a user