From f4afd4b2e50562f60c08362ae4fcbb6f1685c519 Mon Sep 17 00:00:00 2001 From: trnq1ll0 Date: Wed, 10 Jun 2026 08:33:34 +0200 Subject: [PATCH] =?UTF-8?q?input=20Validierung=20und=20UI=20handling=20f?= =?UTF-8?q?=C3=BChlbar=20beschleunigt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 104 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 77 insertions(+), 27 deletions(-) diff --git a/app.py b/app.py index 24ab722..c73f0a3 100644 --- a/app.py +++ b/app.py @@ -54,6 +54,15 @@ if "input_validated" not in st.session_state: st.session_state.input_validated = False if "template_validated" not in st.session_state: st.session_state.template_validated = False +# Caches für Validierung/Parsing – nur bei Dateiwechsel neu ausführen +if "last_input_file" not in st.session_state: + st.session_state.last_input_file = None +if "last_template_file" not in st.session_state: + st.session_state.last_template_file = None +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 # --- Layout: zwei Spalten --- col1, col2 = st.columns(2) @@ -73,9 +82,30 @@ with col1: if selected_input: input_path = Path(app_config.input_dir) / selected_input - # Validiere und parse sofort - with st.spinner("Validiere Input-Datei…"): - val_result = validate_input_file(input_path, app_config.input_schema, app_config.month_sheet_pattern) + + # Nur validieren/parsen wenn sich die Datei geändert hat + if selected_input != st.session_state.last_input_file: + with st.spinner("Validiere Input-Datei…"): + val_result = validate_input_file(input_path, app_config.input_schema, app_config.month_sheet_pattern) + + if val_result.is_valid: + records = parse_input_file( + input_path, + app_config.input_schema, + app_config.month_sheet_pattern, + app_config.skip_empty_name, + app_config.skip_empty_antragsnummer, + ) + st.session_state.parsed_records = records + st.session_state.input_validated = True + else: + st.session_state.parsed_records = [] + st.session_state.input_validated = False + + st.session_state.cached_val_result = val_result + st.session_state.last_input_file = selected_input + else: + val_result = st.session_state.cached_val_result if not val_result.is_valid: st.markdown( @@ -100,27 +130,19 @@ with col1: for warn in val_result.warnings: st.warning(warn) - # Parse die Datei - records = parse_input_file( - input_path, - app_config.input_schema, - app_config.month_sheet_pattern, - app_config.skip_empty_name, - app_config.skip_empty_antragsnummer, - ) - st.session_state.parsed_records = records - st.session_state.input_validated = True - - # Zeige Zusammenfassung + # Zeige Zusammenfassung aus gecachten Records + records = st.session_state.parsed_records employees = get_unique_employees(records) antragsnummern = get_unique_antragsnummern(records) st.success(f"✅ {len(records)} Einträge in {val_result.info.get('sheets_with_data', 0)} Monats-Sheets") - st.metric("Mitarbeiter", len(employees)) - st.metric("Antragsnummern", len(antragsnummern)) - with st.expander("Antragsnummern anzeigen"): - for an in antragsnummern: - st.text(f"• {an}") + pills = " ".join( + f'{an}' + for an in antragsnummern + ) + st.markdown(f"**Antragsnummern:**
{pills}", unsafe_allow_html=True) with col2: st.subheader("2. Template") @@ -140,9 +162,16 @@ with col2: ) if selected_template: - template_path = Path(app_config.template_dir) / selected_template - with st.spinner("Validiere Template…"): - tpl_result = validate_template(template_path, app_config.output_mapping) + # Nur validieren wenn sich das Template geändert hat + if selected_template != st.session_state.last_template_file: + template_path = Path(app_config.template_dir) / selected_template + with st.spinner("Validiere Template…"): + tpl_result = validate_template(template_path, app_config.output_mapping) + st.session_state.cached_tpl_result = tpl_result + st.session_state.last_template_file = selected_template + st.session_state.template_validated = tpl_result.is_valid + else: + tpl_result = st.session_state.cached_tpl_result if not tpl_result.is_valid: st.markdown( @@ -176,11 +205,32 @@ st.subheader("3. Mitarbeiter auswählen") if st.session_state.input_validated and st.session_state.parsed_records: employees = get_unique_employees(st.session_state.parsed_records) - selected_employees = st.multiselect( - f"{len(employees)} Mitarbeiter gefunden – wähle aus, für wen Tätigkeitsnachweise erstellt werden sollen:", - options=employees, - default=employees, + 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 + + # Mitarbeiter-Liste mit Rahmen + st.markdown( + '
', + unsafe_allow_html=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, value=st.session_state[key], key=key): + selected_employees.append(emp) + st.markdown("
", unsafe_allow_html=True) + st.caption(f"{len(selected_employees)} von {len(employees)} Mitarbeitern ausgewählt") else: selected_employees = []