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