154 lines
5.2 KiB
Python
154 lines
5.2 KiB
Python
"""Template-based output workbook writer."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections import defaultdict
|
|
from datetime import date
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from openpyxl import Workbook, load_workbook
|
|
|
|
from .models import MatchedAssignment
|
|
|
|
|
|
def _split_name_for_filename(name: str) -> tuple[str, str]:
|
|
cleaned = str(name or "").strip()
|
|
if "," in cleaned:
|
|
last_name, first_name = [part.strip() for part in cleaned.split(",", 1)]
|
|
return first_name or "Unknown", last_name or "Unknown"
|
|
|
|
parts = cleaned.split()
|
|
if len(parts) >= 2:
|
|
return parts[0], parts[-1]
|
|
if len(parts) == 1:
|
|
return parts[0], "Unknown"
|
|
return "Unknown", "Unknown"
|
|
|
|
|
|
def _normalize_sheet_title(raw_title: str, fallback_index: int) -> str:
|
|
cleaned = (raw_title or "").strip().replace("[", "(").replace("]", ")")
|
|
if not cleaned:
|
|
cleaned = f"Assignment_{fallback_index}"
|
|
return cleaned[:31]
|
|
|
|
|
|
def _set_target_value(worksheet: Any, target_cell: str, value: Any) -> None:
|
|
if not target_cell:
|
|
return
|
|
worksheet[target_cell] = value
|
|
|
|
|
|
def build_employee_workbooks(
|
|
config: dict[str, Any],
|
|
matched_assignments: list[MatchedAssignment],
|
|
report_month: int,
|
|
report_year: int,
|
|
project_root: Path,
|
|
) -> dict[str, Workbook]:
|
|
paths_cfg = config["paths"]
|
|
files_cfg = config["files"]
|
|
output_cfg = config["output"]
|
|
targets = config.get("template_targets", {})
|
|
|
|
template_path = (
|
|
project_root
|
|
/ paths_cfg["template_dir"]
|
|
/ files_cfg["template_workbook"]
|
|
)
|
|
|
|
assignments_by_employee: dict[str, list[MatchedAssignment]] = defaultdict(list)
|
|
for assignment in matched_assignments:
|
|
assignments_by_employee[assignment.allocation.employee_name].append(assignment)
|
|
|
|
workbooks: dict[str, Workbook] = {}
|
|
month_label = f"{report_month:02d}/{report_year}"
|
|
today_label = date.today().strftime("%d.%m.%Y")
|
|
|
|
for employee_name, assignments in assignments_by_employee.items():
|
|
workbook = load_workbook(template_path)
|
|
template_sheet_name = config["sheets"].get("template_source", "")
|
|
if template_sheet_name and template_sheet_name in workbook.sheetnames:
|
|
base_sheet = workbook[template_sheet_name]
|
|
else:
|
|
base_sheet = workbook.worksheets[0]
|
|
|
|
for index, assignment in enumerate(assignments, start=1):
|
|
if index == 1:
|
|
sheet = base_sheet
|
|
else:
|
|
sheet = workbook.copy_worksheet(base_sheet)
|
|
|
|
sheet_name_pattern = output_cfg.get("sheet_name_pattern", "{index}_{funktion}")
|
|
raw_sheet_name = sheet_name_pattern.format(
|
|
index=index,
|
|
funktion=assignment.masterdata.funktion,
|
|
)
|
|
sheet.title = _normalize_sheet_title(raw_sheet_name, index)
|
|
|
|
_set_target_value(sheet, targets.get("antragsnummer", ""), assignment.masterdata.antragsnummer)
|
|
_set_target_value(sheet, targets.get("name_vorname", ""), assignment.allocation.employee_name)
|
|
_set_target_value(sheet, targets.get("qualifikation", ""), assignment.masterdata.qualifikation)
|
|
_set_target_value(sheet, targets.get("funktion", ""), assignment.masterdata.funktion)
|
|
_set_target_value(sheet, targets.get("taetigkeiten", ""), assignment.masterdata.taetigkeit)
|
|
_set_target_value(sheet, targets.get("reporting_month_label", ""), month_label)
|
|
_set_target_value(sheet, targets.get("foerderquote", ""), f"{assignment.foerderquote_percent:.2f} %")
|
|
_set_target_value(sheet, targets.get("generated_on", ""), today_label)
|
|
|
|
workbooks[employee_name] = workbook
|
|
|
|
return workbooks
|
|
|
|
|
|
def build_output_filename(
|
|
filename_pattern: str,
|
|
employee_name: str,
|
|
report_year: int,
|
|
report_month: int,
|
|
) -> str:
|
|
first_name, last_name = _split_name_for_filename(employee_name)
|
|
safe_first = first_name.replace(" ", "_")
|
|
safe_last = last_name.replace(" ", "_")
|
|
return filename_pattern.format(
|
|
year=report_year,
|
|
month=f"{report_month:02d}",
|
|
first_name=safe_first,
|
|
last_name=safe_last,
|
|
)
|
|
|
|
|
|
def save_employee_workbooks(
|
|
config: dict[str, Any],
|
|
employee_workbooks: dict[str, Workbook],
|
|
report_month: int,
|
|
report_year: int,
|
|
project_root: Path,
|
|
) -> list[Path]:
|
|
paths_cfg = config["paths"]
|
|
output_cfg = config["output"]
|
|
filename_pattern = output_cfg["filename_pattern"]
|
|
|
|
output_dir = project_root / paths_cfg["output_dir"]
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
saved_paths: list[Path] = []
|
|
used_names: set[str] = set()
|
|
|
|
for employee_name, workbook in employee_workbooks.items():
|
|
filename = build_output_filename(
|
|
filename_pattern=filename_pattern,
|
|
employee_name=employee_name,
|
|
report_year=report_year,
|
|
report_month=report_month,
|
|
)
|
|
if filename in used_names:
|
|
raise ValueError(f"Output filename collision detected: {filename}")
|
|
|
|
used_names.add(filename)
|
|
output_path = output_dir / filename
|
|
workbook.save(output_path)
|
|
workbook.close()
|
|
saved_paths.append(output_path)
|
|
|
|
return saved_paths
|