from __future__ import annotations import re from dataclasses import dataclass from pathlib import Path from typing import Iterable, List, Optional, Tuple, Dict import pandas as pd from .docx_fill import fill_template from .io import load_pair @dataclass class GeneratedArtifact: kind: str # "collaboratori" | "manager" person: str docx_path: Path pdf_path: Optional[Path] = None notes: str = "" @dataclass class GenerateResult: produced: List[GeneratedArtifact] warnings: List[str] def _safe_filename(name: str) -> str: name = str(name).strip() name = re.sub(r"\s+", " ", name) name = re.sub(r"[^A-Za-z0-9 _-]+", "", name) return name.replace(" ", "_") def list_people(auto_path: Path, valut_path: Path) -> List[str]: """Return UNION of names found in AUTO and VALUT (dedup).""" df_auto, df_val = load_pair(auto_path, valut_path) a = set(df_auto.get("Nome e cognome", [])) b = set(df_val.get("Nome e cognome", [])) names = sorted({x for x in a.union(b) if isinstance(x, str) and x.strip()}) return names def _generate_one( *, kind: str, person_name: str, df_auto: pd.DataFrame, df_val: pd.DataFrame, template_path: Path, output_dir: Path, workdir: Path, make_pdf: bool, ) -> GeneratedArtifact: base = f"REPORT_{kind}_{_safe_filename(person_name)}" out_docx = output_dir / f"{base}.docx" out_pdf = output_dir / f"{base}.pdf" notes = "" # Fill template fill_template( template_path=template_path, out_docx=out_docx, df_auto=df_auto, df_valut=df_val, person_name=person_name, kind=kind, workdir=workdir, ) pdf_path: Optional[Path] = None if make_pdf: from .pdf_convert import docx_to_pdf try: docx_to_pdf(out_docx, out_pdf) pdf_path = out_pdf except Exception as e: notes = f"PDF non generato: {e}" pdf_path = None return GeneratedArtifact(kind=kind, person=person_name, docx_path=out_docx, pdf_path=pdf_path, notes=notes) def generate_selected( *, collab_auto: Optional[Path], collab_valut: Optional[Path], collab_template: Optional[Path], manager_auto: Optional[Path], manager_valut: Optional[Path], manager_template: Optional[Path], selected_collaboratori: Iterable[str], selected_manager: Iterable[str], output_dir: Path, make_pdf: bool, ) -> GenerateResult: produced: List[GeneratedArtifact] = [] warnings: List[str] = [] output_dir.mkdir(parents=True, exist_ok=True) workdir = output_dir / "_work" workdir.mkdir(parents=True, exist_ok=True) if collab_auto and collab_valut and collab_template: df_auto, df_val = load_pair(collab_auto, collab_valut) for person in selected_collaboratori: try: produced.append( _generate_one( kind="collaboratori", person_name=person, df_auto=df_auto, df_val=df_val, template_path=collab_template, output_dir=output_dir, workdir=workdir / "collaboratori", make_pdf=make_pdf, ) ) except Exception as e: warnings.append(f"[Collaboratori] {person}: errore generazione ({e})") else: if any([collab_auto, collab_valut, collab_template]): warnings.append("Collaboratori: mancano uno o più file (AUTO/VALUT/TEMPLATE).") if manager_auto and manager_valut and manager_template: df_auto, df_val = load_pair(manager_auto, manager_valut) for person in selected_manager: try: produced.append( _generate_one( kind="manager", person_name=person, df_auto=df_auto, df_val=df_val, template_path=manager_template, output_dir=output_dir, workdir=workdir / "manager", make_pdf=make_pdf, ) ) except Exception as e: warnings.append(f"[Manager] {person}: errore generazione ({e})") else: if any([manager_auto, manager_valut, manager_template]): warnings.append("Manager: mancano uno o più file (AUTO/VALUT/TEMPLATE).") return GenerateResult(produced=produced, warnings=warnings)