137 lines
4.0 KiB
Python
137 lines
4.0 KiB
Python
"""Validierung von Input-Dateien und Templates."""
|
|
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
from openpyxl import load_workbook
|
|
from openpyxl.utils import column_index_from_string
|
|
|
|
from .models import FieldSchema, OutputMapping, ValidationResult
|
|
|
|
|
|
def validate_input_file(
|
|
filepath: Path,
|
|
schema: list[FieldSchema],
|
|
) -> ValidationResult:
|
|
"""
|
|
Validiert eine Input-Datei:
|
|
- Datei existiert
|
|
- Erwartete Spalten sind vorhanden (Header in Zeile 1)
|
|
- Mindestens ein Daten-Sheet enthält Daten
|
|
"""
|
|
errors: list[str] = []
|
|
warnings: list[str] = []
|
|
info: dict = {}
|
|
|
|
if not filepath.exists():
|
|
errors.append(f"Input-Datei nicht gefunden: {filepath}")
|
|
return ValidationResult(is_valid=False, errors=errors)
|
|
|
|
try:
|
|
wb = load_workbook(filepath, data_only=True)
|
|
except Exception as e:
|
|
errors.append(f"Datei konnte nicht geöffnet werden: {e}")
|
|
return ValidationResult(is_valid=False, errors=errors)
|
|
|
|
sheet_count = len(wb.sheetnames)
|
|
info["sheet_count"] = sheet_count
|
|
info["sheet_names"] = wb.sheetnames
|
|
|
|
# Prüfe, ob mindestens ein Sheet die erwarteten Spalten hat
|
|
sheets_with_data = 0
|
|
for sheet_name in wb.sheetnames:
|
|
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]
|
|
missing_cols = []
|
|
for field in schema:
|
|
col_idx = column_index_from_string(field.column) - 1
|
|
if col_idx >= len(header_row):
|
|
missing_cols.append(f"{field.column} ({field.field_name})")
|
|
continue
|
|
header_val = str(header_row[col_idx]).strip() if header_row[col_idx] is not None else ""
|
|
if header_val != field.field_name:
|
|
warnings.append(
|
|
f"Sheet '{sheet_name}', Spalte {field.column}: "
|
|
f"Erwartet '{field.field_name}', gefunden '{header_val}'"
|
|
)
|
|
|
|
if not missing_cols:
|
|
sheets_with_data += 1
|
|
|
|
# Zähle Datenzeilen
|
|
data_rows = ws.max_row - 1
|
|
info[f"rows_{sheet_name}"] = data_rows
|
|
|
|
info["sheets_with_data"] = sheets_with_data
|
|
wb.close()
|
|
|
|
if sheets_with_data == 0:
|
|
errors.append("Kein Sheet mit den erwarteten Spalten gefunden.")
|
|
|
|
return ValidationResult(
|
|
is_valid=len(errors) == 0,
|
|
errors=errors,
|
|
warnings=warnings,
|
|
info=info,
|
|
)
|
|
|
|
|
|
def validate_template(
|
|
template_path: Path,
|
|
mapping: list[OutputMapping],
|
|
) -> ValidationResult:
|
|
"""
|
|
Validiert ein Template:
|
|
- Datei existiert
|
|
- Alle target_cells aus dem Mapping sind im Template vorhanden
|
|
"""
|
|
errors: list[str] = []
|
|
warnings: list[str] = []
|
|
info: dict = {}
|
|
|
|
if not template_path.exists():
|
|
errors.append(f"Template-Datei nicht gefunden: {template_path}")
|
|
return ValidationResult(is_valid=False, errors=errors)
|
|
|
|
try:
|
|
wb = load_workbook(template_path, data_only=True)
|
|
except Exception as e:
|
|
errors.append(f"Template konnte nicht geöffnet werden: {e}")
|
|
return ValidationResult(is_valid=False, errors=errors)
|
|
|
|
info["sheet_names"] = wb.sheetnames
|
|
|
|
# Wir validieren gegen das erste Sheet (üblicherweise das Haupt-Template)
|
|
if not wb.sheetnames:
|
|
errors.append("Template enthält keine Sheets.")
|
|
wb.close()
|
|
return ValidationResult(is_valid=False, errors=errors)
|
|
|
|
ws = wb[wb.sheetnames[0]]
|
|
|
|
found_cells = 0
|
|
missing_cells = []
|
|
for m in mapping:
|
|
try:
|
|
cell = ws[m.target_cell]
|
|
found_cells += 1
|
|
except (ValueError, KeyError):
|
|
missing_cells.append(m.target_cell)
|
|
|
|
info["found_target_cells"] = found_cells
|
|
info["total_target_cells"] = len(mapping)
|
|
|
|
if missing_cells:
|
|
errors.append(f"Zielfelder nicht im Template gefunden: {', '.join(missing_cells)}")
|
|
|
|
wb.close()
|
|
return ValidationResult(
|
|
is_valid=len(errors) == 0,
|
|
errors=errors,
|
|
warnings=warnings,
|
|
info=info,
|
|
)
|