From d027bedcb8654a3a8d66b19faa3e1a0247d6c302 Mon Sep 17 00:00:00 2001 From: Rob Date: Mon, 10 Aug 2026 19:37:03 +0200 Subject: [PATCH] =?UTF-8?q?Ausw=C3=A4hlbare=20Mitarbeitermonate=20in=20sep?= =?UTF-8?q?arater=20Multiselektliste=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 0_application/README.md | 5 +- 0_application/app.py | 141 +++++++++++++++++++++++++++++++++++----- 2 files changed, 126 insertions(+), 20 deletions(-) diff --git a/0_application/README.md b/0_application/README.md index 5ea09af..f49c2ea 100644 --- a/0_application/README.md +++ b/0_application/README.md @@ -15,9 +15,10 @@ Um das Programm zu starten: Doppelklick auf `Tätigkeitsnachweis-Generator.exe` - Nach Auswahl wird das Template sofort validiert - Angezeigt wird: Anzahl gefundener Zielfelder 3. **Mitarbeiter auswählen** – Multiselect-Liste aller gefundenen Mitarbeiter - - Standardmäßig sind alle ausgewählt + - Standardmäßig ist keiner ausgewählt - Nicht selektierte Mitarbeiter werden bei der Generierung übersprungen -4. **"Tätigkeitsnachweise generieren"** klicken +4. **Mitarbeitermonate auswählen** – Multiselect-Liste aller verfügbaren Monate im Dokument. Vorselektiert sind die aktuell letzten beiden Monate. +5. **"Tätigkeitsnachweise generieren"** klicken - Die generierten Dateien werden im `output/`-Ordner abgelegt ## Konfiguration diff --git a/0_application/app.py b/0_application/app.py index 1acccdc..32c8741 100644 --- a/0_application/app.py +++ b/0_application/app.py @@ -30,6 +30,26 @@ from src.parser import parse_input_file, group_by_employee, get_unique_antragsnu from src.validator import validate_input_file, validate_template from src.writer import generate_activity_reports + +def set_active_employee(employee_name: str): + """Merkt die zuletzt angeklickte Person für die Monatsliste.""" + st.session_state.active_month_employee = employee_name + + +def sync_employee_months(employee_name: str, months: list[str]): + """Übernimmt Monats-Checkboxen vor dem nächsten UI-Rendern.""" + st.session_state.employee_months[employee_name] = { + month for month in months + if st.session_state.get(f"month_{employee_name}_{month}", False) + } + + +def set_all_employee_months(employee_name: str, months: list[str], selected: bool): + """Setzt alle Monats-Checkboxen einer Person gesammelt.""" + st.session_state.employee_months[employee_name] = set(months) if selected else set() + for month in months: + st.session_state[f"month_{employee_name}_{month}"] = selected + # --- Seite konfigurieren --- st.set_page_config( page_title="Tätigkeitsnachweis-Generator", @@ -72,6 +92,8 @@ if "cached_val_result" not in st.session_state: st.session_state.cached_val_result = None if "cached_tpl_result" not in st.session_state: st.session_state.cached_tpl_result = None +if "employee_months" not in st.session_state: + st.session_state.employee_months = {} # --- Layout: zwei Spalten --- col1, col2 = st.columns(2) @@ -230,24 +252,94 @@ if st.session_state.input_validated and st.session_state.parsed_records: employees = get_unique_employees(st.session_state.parsed_records) st.caption(f"{len(employees)} Mitarbeiter gefunden – wähle aus, für wen Tätigkeitsnachweise erstellt werden sollen:") - # "Alle" / "Keine" Buttons - btn_col1, btn_col2 = st.columns(2) - if btn_col1.button("☑ Alle auswählen", use_container_width=True): - for emp in employees: - st.session_state[f"sel_{emp}"] = True - if btn_col2.button("☐ Keine auswählen", use_container_width=True): - for emp in employees: - st.session_state[f"sel_{emp}"] = False + # Monatsbezeichnungen für die kompakte Anzeige hinter dem Namen + month_labels = { + "01": "Jan", "02": "Feb", "03": "Mär", "04": "Apr", + "05": "Mai", "06": "Jun", "07": "Jul", "08": "Aug", + "09": "Sep", "10": "Okt", "11": "Nov", "12": "Dez", + } - # Mitarbeiter-Liste mit Rahmen - with st.container(border=True): - selected_employees = [] - for emp in employees: - key = f"sel_{emp}" - if key not in st.session_state: - st.session_state[key] = True - if st.checkbox(emp, key=key): - selected_employees.append(emp) + all_groups = group_by_employee(st.session_state.parsed_records) + available_months = {} + for emp in employees: + months = sorted({ + record.monat.split(".", 1)[0].zfill(2) + for record in all_groups.get(emp, []) + if record.monat and record.monat.split(".", 1)[0].isdigit() + }, key=lambda value: int(value)) + available_months[emp] = months + if emp not in st.session_state.employee_months: + st.session_state.employee_months[emp] = set(months[-2:]) + + # Mitarbeiter- und Monatsliste teilen sich die verfügbare Breite. + employee_col, month_col = st.columns(2) + with employee_col: + employee_btn_col1, employee_btn_col2 = st.columns(2) + if employee_btn_col1.button("☑ Alle auswählen", use_container_width=True): + for emp in employees: + st.session_state[f"sel_{emp}"] = True + if employee_btn_col2.button("☐ Keine auswählen", use_container_width=True): + for emp in employees: + st.session_state[f"sel_{emp}"] = False + + with st.container(border=True): + selected_employees = [] + for emp in employees: + key = f"sel_{emp}" + if key not in st.session_state: + st.session_state[key] = False + selected_months = st.session_state.employee_months.get(emp, set()) + short_months = ", ".join(month_labels[m] for m in available_months[emp] if m in selected_months) + label = f"{emp} ({short_months})" if short_months else f"{emp} (**keine Monate**)" + if st.checkbox( + label, + key=key, + on_change=set_active_employee, + args=(emp,), + ): + selected_employees.append(emp) + + with month_col: + selected_for_months = st.session_state.get("active_month_employee") + if selected_for_months not in employees: + selected_for_months = None + + month_btn_col1, month_btn_col2 = st.columns(2) + month_btn_col1.button( + "☑ Alle Monate", + use_container_width=True, + disabled=not selected_for_months, + on_click=set_all_employee_months, + args=(selected_for_months, available_months[selected_for_months], True) if selected_for_months else ("", [], True), + ) + month_btn_col2.button( + "☐ Keine Monate", + use_container_width=True, + disabled=not selected_for_months, + on_click=set_all_employee_months, + args=(selected_for_months, available_months[selected_for_months], False) if selected_for_months else ("", [], False), + ) + + with st.container(border=True): + st.caption("Monate für den angeklickten Mitarbeiter") + if selected_for_months: + st.write(selected_for_months) + month_values = {} + for month in available_months[selected_for_months]: + month_key = f"month_{selected_for_months}_{month}" + if month_key not in st.session_state: + st.session_state[month_key] = month in st.session_state.employee_months[selected_for_months] + month_values[month] = st.checkbox( + f"{month_labels[month]} ({month})", + key=month_key, + on_change=sync_employee_months, + args=(selected_for_months, available_months[selected_for_months]), + ) + st.session_state.employee_months[selected_for_months] = { + month for month, checked in month_values.items() if checked + } + else: + st.info("Einen Mitarbeiter links auswählen, um seine Monate festzulegen.") st.caption(f"{len(selected_employees)} von {len(employees)} Mitarbeitern ausgewählt") else: @@ -283,7 +375,20 @@ if generate_clicked: else: # Filtere Records auf selektierte Mitarbeiter all_groups = group_by_employee(st.session_state.parsed_records) - selected_groups = {name: all_groups[name] for name in selected_employees if name in all_groups} + selected_groups = { + name: [ + record for record in all_groups[name] + if record.monat.split(".", 1)[0].zfill(2) + in st.session_state.employee_months.get(name, set()) + ] + for name in selected_employees + if name in all_groups + } + selected_groups = {name: records for name, records in selected_groups.items() if records} + + if not selected_groups: + st.error("Für die ausgewählten Mitarbeiter sind keine Monate ausgewählt.") + st.stop() template_path = Path(app_config.template_dir) / selected_template output_dir = Path(app_config.output_dir)