Fehlerbehebungen
Fehlervermeidung Favicon ersetzt
This commit is contained in:
parent
1330fd569e
commit
b6a1db63bc
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
**/*.pyc
|
||||
Testplan.md
|
||||
.idea
|
||||
.nicegui
|
||||
.venv
|
||||
|
@ -7,8 +7,8 @@ services:
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
volumes:
|
||||
#- /home/alexander/Dokumente/Python/Zeiterfassung/lib:/app/lib
|
||||
#- /home/alexander/Dokumente/Python/Zeiterfassung/main.py:/app/main.py
|
||||
- /home/alexander/Dokumente/Python/Zeiterfassung/lib:/app/lib
|
||||
- /home/alexander/Dokumente/Python/Zeiterfassung/main.py:/app/main.py
|
||||
- /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
|
75
lib/admin.py
75
lib/admin.py
@ -1,4 +1,4 @@
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import dateutil.easter
|
||||
from dateutil.easter import *
|
||||
@ -20,6 +20,7 @@ import hashlib
|
||||
import calendar
|
||||
import locale
|
||||
import segno
|
||||
import shutil
|
||||
|
||||
@ui.page('/admin')
|
||||
def page_admin():
|
||||
@ -524,6 +525,7 @@ def page_admin():
|
||||
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.")
|
||||
if current_user.get_day_workhours(actual_date.year, actual_date.month, actual_date.day) > 0:
|
||||
current_user.update_absence(actual_date.year, actual_date.month, actual_date.day, absence_type)
|
||||
|
||||
actual_date = actual_date + datetime.timedelta(days=1)
|
||||
@ -589,12 +591,20 @@ def page_admin():
|
||||
|
||||
with ui.button(icon='menu').props('square') as menu_button:
|
||||
with ui.menu() as menu:
|
||||
no_contract = False
|
||||
start_of_contract = current_user.get_starting_day()
|
||||
if datetime.datetime(int(select_year.value), int(select_month.value), day) < datetime.datetime(int(start_of_contract[0]), int(start_of_contract[1]), int(start_of_contract[2])):
|
||||
no_contract = True
|
||||
|
||||
menu_item = ui.menu_item("Zeiteintrag hinzufügen", lambda day=day: add_entry(day))
|
||||
if archive_status:
|
||||
menu_item.disable()
|
||||
if datetime.datetime.now().day < day:
|
||||
menu_item.disable()
|
||||
menu_item.tooltip("Kann keine Zeiteinträge für die Zukunft vornehmen.")
|
||||
if no_contract:
|
||||
menu_item.disable()
|
||||
menu_item.tooltip("Kann keine Zeiteinträge für Zeit vor der Einstellung vornehmen")
|
||||
ui.separator()
|
||||
menu_item = ui.menu_item("Notizen bearbeiten", lambda day=day: edit_notes(day))
|
||||
if archive_status:
|
||||
@ -606,6 +616,10 @@ def page_admin():
|
||||
menu_item.disable()
|
||||
if str(day) in list(user_absent):
|
||||
menu_item.disable()
|
||||
if no_contract:
|
||||
menu_item.disable()
|
||||
menu_item.tooltip(
|
||||
"Kann keine Zeiteinträge für Zeit vor der Einstellung vornehmen")
|
||||
if archive_status:
|
||||
menu_button.disable()
|
||||
|
||||
@ -984,8 +998,21 @@ def page_admin():
|
||||
holiday_buttons_grid.refresh()
|
||||
|
||||
with ui.column():
|
||||
starting_year = ui.number(value=datetime.datetime.now().year, label="Startjahr")
|
||||
end_year = ui.number(value=starting_year.value, label="Endjahr")
|
||||
end_year_binder = ValueBinder()
|
||||
start_year_binder = ValueBinder()
|
||||
def correct_end_year():
|
||||
if starting_year.value > end_year_binder.value:
|
||||
end_year_binder.value = starting_year.value
|
||||
|
||||
def correct_start_year():
|
||||
if starting_year.value > end_year_binder.value:
|
||||
starting_year.value = end_year_binder.value
|
||||
|
||||
start_year_binder.value = datetime.datetime.now().year
|
||||
starting_year = ui.number(value=datetime.datetime.now().year, label="Startjahr", on_change=correct_end_year).bind_value(start_year_binder, 'value')
|
||||
|
||||
end_year_binder.value = starting_year.value
|
||||
end_year = ui.number(value=starting_year.value, label="Endjahr", on_change=correct_start_year).bind_value(end_year_binder, 'value')
|
||||
with ui.row():
|
||||
ui.button("Anwenden", on_click=enter_holidays)
|
||||
ui.button("Abbrechen", on_click=dialog.close)
|
||||
@ -1044,7 +1071,9 @@ def page_admin():
|
||||
reset_visibility.value = False
|
||||
timetable.refresh()
|
||||
|
||||
ui.button("Speichern", on_click=save_admin_settings).tooltip("Hiermit werden sämtliche oben gemachten Einstellungen gespeichert.")
|
||||
with ui.button("Speichern", on_click=save_admin_settings):
|
||||
with ui.tooltip():
|
||||
ui.label("Hiermit werden sämtliche oben gemachten Einstellungen gespeichert.\nGgf. müssen Sie die Seite neu laden um die Auswirkungen sichtbar zu machen.").style('white-space: pre-wrap')
|
||||
|
||||
with ui.tab_panel(users):
|
||||
ui.label("Benutzerverwaltung").classes(h3)
|
||||
@ -1341,7 +1370,7 @@ def page_admin():
|
||||
sum = float(days[i].value) + sum
|
||||
except:
|
||||
pass
|
||||
workhours_sum.value = str(sum)
|
||||
workhours_sum.text = str(sum)
|
||||
|
||||
with ui.grid(columns='auto auto auto').classes('items-baseline'):
|
||||
ui.label("gültig ab:")
|
||||
@ -1371,9 +1400,19 @@ def page_admin():
|
||||
|
||||
def add_workhours_entry():
|
||||
workhours_dict = { }
|
||||
if not use_last_entries_chkb.value:
|
||||
for i in range(1, 8):
|
||||
workhours_dict[i] = 0
|
||||
workhours_dict["vacation"] = 0
|
||||
else:
|
||||
validity_date_dt = datetime.datetime.strptime(date_picker.value, "%Y-%m-%d")
|
||||
for i in range (1, 8):
|
||||
check_date_dt = validity_date_dt - datetime.timedelta(days=i)
|
||||
weekday_of_check_date = check_date_dt.weekday() + 1
|
||||
workhours_of_check_date = current_user.get_day_workhours(check_date_dt.year, check_date_dt.month, check_date_dt.day)
|
||||
workhours_dict[weekday_of_check_date] = workhours_of_check_date
|
||||
workhours_dict["vacation"] = current_user.get_vacation_claim(validity_date_dt.year, validity_date_dt.month, validity_date_dt.day)
|
||||
|
||||
current_user.workhours[date_picker.value] = workhours_dict
|
||||
current_user.write_settings()
|
||||
|
||||
@ -1393,7 +1432,7 @@ def page_admin():
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
ui.label("Geben Sie das Gültigkeitsdatum an, ab wann die Einträge gültig sein sollen.")
|
||||
date_picker = ui.date()
|
||||
|
||||
use_last_entries_chkb = ui.checkbox("Werte von letztem gültigen Eintrag übernehmen.")
|
||||
with ui.row():
|
||||
ui.button("OK", on_click=add_workhours_entry)
|
||||
ui.button("Abbrechen", on_click=dialog.close)
|
||||
@ -1497,11 +1536,13 @@ def page_admin():
|
||||
else:
|
||||
ui.label(f'{round(size / 1_000, 2)} kB')
|
||||
ui.label(version)
|
||||
from lib.definitions import scriptpath
|
||||
ui.button(icon='download', on_click=lambda file=date_string: ui.download.file(
|
||||
os.path.join(scriptpath, backupfolder, f'{file}.zip'))).tooltip(
|
||||
"Backup herunterladen")
|
||||
|
||||
def del_backup_dialog(file):
|
||||
from lib.definitions import scriptpath
|
||||
def del_backup():
|
||||
os.remove(os.path.join(scriptpath, backupfolder, f'{file}.zip'))
|
||||
dialog.close()
|
||||
@ -1518,9 +1559,30 @@ def page_admin():
|
||||
dialog.open()
|
||||
|
||||
def restore_backup_dialog(file):
|
||||
from lib.definitions import scriptpath
|
||||
def restore_backup():
|
||||
if is_docker():
|
||||
folder_to_delete = "/users"
|
||||
else:
|
||||
folder_to_delete = os.path.join(scriptpath, userfolder)
|
||||
for file_path in os.listdir(folder_to_delete):
|
||||
delete_item = os.path.join(folder_to_delete, file_path)
|
||||
if os.path.isfile(delete_item) or os.path.islink(delete_item):
|
||||
os.unlink(delete_item)
|
||||
elif os.path.isdir(delete_item):
|
||||
shutil.rmtree(delete_item)
|
||||
|
||||
with zipfile.ZipFile(os.path.join(scriptpath, backupfolder, f'{file}.zip'), 'r') as source:
|
||||
if not is_docker():
|
||||
source.extractall(scriptpath)
|
||||
os.unlink(os.path.join(scriptpath, "app_version.txt"))
|
||||
else:
|
||||
filelist = source.namelist()
|
||||
print(filelist)
|
||||
for file_list_item in filelist:
|
||||
if file_list_item.startswith("users"):
|
||||
source.extract(file_list_item, "/")
|
||||
source.extract("settings.json", "/settings/")
|
||||
with ui.dialog() as confirm_dialog, ui.card():
|
||||
ui.label("Das Backup wurde wiederhergestellt. Um Änderungen anzuzeigen, muss die Seite neu geladen werden.")
|
||||
with ui.grid(columns=2):
|
||||
@ -1547,6 +1609,7 @@ def page_admin():
|
||||
ui.separator()
|
||||
|
||||
async def make_backup():
|
||||
from lib.definitions import scriptpath
|
||||
n = ui.notification("Backup wird erzeugt...")
|
||||
compress = zipfile.ZIP_DEFLATED
|
||||
filename = os.path.join(searchpath, datetime.datetime.now().strftime(date_format) + '.zip')
|
||||
|
@ -227,13 +227,7 @@ def homepage():
|
||||
ui.label("Fehlzeitenübersicht").classes('col-span-2 font-bold')
|
||||
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')
|
||||
ui.separator().classes('col-span-2')
|
||||
|
||||
def logout():
|
||||
app.storage.user.pop("active_user", None)
|
||||
ui.navigate.to("/")
|
||||
|
||||
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')
|
||||
@ -307,6 +301,16 @@ def homepage():
|
||||
ui.button("Speichern", on_click=save_new_password)
|
||||
ui.button("Zurücksetzen", on_click=revert_pw_inputs)
|
||||
ui.space()
|
||||
ui.space()
|
||||
with ui.column():
|
||||
ui.separator()
|
||||
|
||||
def logout():
|
||||
app.storage.user.pop("active_user", None)
|
||||
ui.navigate.to("/")
|
||||
|
||||
ui.button("Logout", on_click=logout).classes('w-full')
|
||||
ui.space()
|
||||
|
||||
else:
|
||||
login_mask()
|
||||
|
127
main.py
127
main.py
@ -54,130 +54,11 @@ def main():
|
||||
ui.toggle.default_props('rounded')
|
||||
ui.row.default_classes('items-baseline')
|
||||
|
||||
favicon = '''<?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
|
||||
>
|
||||
'''
|
||||
if not os.path.exists(os.path.join(scriptpath, "favicon.svg")):
|
||||
with open(os.path.join(scriptpath, "favicon.svg"), 'w') as favicon_file:
|
||||
favicon_file.write(favicon)
|
||||
#if not os.path.exists(os.path.join(scriptpath, "favicon.svg")):
|
||||
# with open(os.path.join(scriptpath, "favicon.svg"), 'w') as favicon_file:
|
||||
# favicon_file.write(favicon)
|
||||
|
||||
ui.run(favicon=os.path.join(scriptpath, "favicon.svg"), port=port, storage_secret=secret, language='de-DE', show_welcome_message=False)
|
||||
ui.run(favicon='⏲', port=port, storage_secret=secret, language='de-DE', show_welcome_message=False)
|
||||
|
||||
if __name__ in ("__main__", "__mp_main__"):
|
||||
parser = argparse.ArgumentParser(description=f'{app_title} {app_version}')
|
||||
|
Loading…
x
Reference in New Issue
Block a user