diff --git a/app.py b/app.py index b4e2379..24ab722 100644 --- a/app.py +++ b/app.py @@ -75,15 +75,30 @@ with col1: 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) + val_result = validate_input_file(input_path, app_config.input_schema, app_config.month_sheet_pattern) if not val_result.is_valid: + st.markdown( + '
' + '' + '❌ Validierung fehlgeschlagen
', + unsafe_allow_html=True, + ) for err in val_result.errors: - st.error(err) + st.error(f"🔴 {err}") st.session_state.input_validated = False else: - for warn in val_result.warnings: - st.warning(warn) + if val_result.warnings: + st.markdown( + '
' + '' + '⚠️ Hinweise
', + unsafe_allow_html=True, + ) + for warn in val_result.warnings: + st.warning(warn) # Parse die Datei records = parse_input_file( @@ -130,8 +145,15 @@ with col2: tpl_result = validate_template(template_path, app_config.output_mapping) if not tpl_result.is_valid: + st.markdown( + '
' + '' + '❌ Validierung fehlgeschlagen
', + unsafe_allow_html=True, + ) for err in tpl_result.errors: - st.error(err) + st.error(f"🔴 {err}") st.session_state.template_validated = False else: st.session_state.template_validated = True @@ -139,6 +161,13 @@ with col2: total = tpl_result.info.get("total_target_cells", 0) st.success(f"✅ {found}/{total} Zielfelder gefunden") if tpl_result.warnings: + st.markdown( + '
' + '' + '⚠️ Hinweise
', + unsafe_allow_html=True, + ) for warn in tpl_result.warnings: st.warning(warn) diff --git a/src/parser.py b/src/parser.py index de3b39b..289d58a 100644 --- a/src/parser.py +++ b/src/parser.py @@ -65,7 +65,9 @@ def parse_input_file( ws = wb[sheet_name] # Header ist Zeile 1, Daten ab Zeile 2 - for row in ws.iter_rows(min_row=2, max_row=ws.max_row, values_only=True): + # min_col=1 explizit setzen für korrekte Spaltenzuordnung + schema_col_count = len(schema) + for row in ws.iter_rows(min_row=2, max_row=ws.max_row, min_col=1, max_col=schema_col_count, values_only=True): name = _to_text(row[col_map.get("Name", 0)]) if len(row) > col_map.get("Name", 0) else "" antragsnummer = _to_text(row[col_map.get("Antragsnummer", 0)]) if len(row) > col_map.get("Antragsnummer", 0) else "" diff --git a/src/validator.py b/src/validator.py index 2b038f6..4d42c81 100644 --- a/src/validator.py +++ b/src/validator.py @@ -6,18 +6,21 @@ from typing import Optional from openpyxl import load_workbook from openpyxl.utils import column_index_from_string +from .config import is_month_sheet from .models import FieldSchema, OutputMapping, ValidationResult def validate_input_file( filepath: Path, schema: list[FieldSchema], + month_sheet_pattern: str = "* 20??", ) -> ValidationResult: """ Validiert eine Input-Datei: - Datei existiert - Erwartete Spalten sind vorhanden (Header in Zeile 1) - - Mindestens ein Daten-Sheet enthält Daten + - Mindestens ein Monats-Daten-Sheet enthält Daten + - Nicht-Monats-Sheets (z. B. "Alle Rollen") werden übersprungen """ errors: list[str] = [] warnings: list[str] = [] @@ -37,14 +40,23 @@ def validate_input_file( info["sheet_count"] = sheet_count info["sheet_names"] = wb.sheetnames - # Prüfe, ob mindestens ein Sheet die erwarteten Spalten hat + # Prüfe nur Monats-Sheets (überspringe z. B. "Alle Rollen") + schema_col_count = len(schema) sheets_with_data = 0 for sheet_name in wb.sheetnames: + if not is_month_sheet(sheet_name, month_sheet_pattern): + continue + ws = wb[sheet_name] if ws.max_row < 2: continue # Kein Header + Daten - header_row = list(ws.iter_rows(min_row=1, max_row=1, values_only=True))[0] + # min_col=1 explizit setzen, um Verschiebungen bei + # ws.min_column > 1 zu vermeiden + header_row = list(ws.iter_rows( + min_row=1, max_row=1, min_col=1, max_col=schema_col_count, + values_only=True, + ))[0] missing_cols = [] for field in schema: col_idx = column_index_from_string(field.column) - 1