"""Seed sekcji wniosku z regulaminu (z fallback na szablony DB).""" from __future__ import annotations import re import unicodedata import uuid from typing import Any from core.projects.models import ProjectSection, ProjectSectionTemplate _PROGRAM_TYPE_ALIASES: dict[str, str] = { "PARP": "PARP", "SMART": "SMART", "FENG": "SMART", "NCBR": "SMART", "ARIMR": "ARIMR", "ZUS": "ZUS_BHP", "ZUS_BHP": "ZUS_BHP", "BGK": "SMART", "NFOSIGW": "SMART", "NFOŚIGW": "SMART", "REGIONAL": "REGIONAL", } def normalize_program_type(program_type: str) -> str: """Mapuje operator / typ programu na identyfikator szablonu sekcji.""" key = (program_type or "SMART").strip().upper() if key in _PROGRAM_TYPE_ALIASES: return _PROGRAM_TYPE_ALIASES[key] for alias, target in _PROGRAM_TYPE_ALIASES.items(): if alias in key: return target return key if key else "SMART" def section_title_to_slug(title: str) -> str: """Konwertuje tytuł sekcji z regulaminu na slug section_type.""" text = unicodedata.normalize("NFKD", title or "") text = "".join(c for c in text if not unicodedata.combining(c)) text = text.lower().strip() text = re.sub(r"[^a-z0-9]+", "_", text) text = re.sub(r"_+", "_", text).strip("_") return text[:60] or "section" def seed_sections_from_regulation( db, project_id: str, required_sections: list[str], program_type: str, ) -> tuple[list[ProjectSection], dict[str, str]]: """ Tworzy ProjectSection z listy required_sections regulaminu. Zwraca (utworzone_sekcje, mapa slug→tytuł). """ titles_map: dict[str, str] = {} created: list[ProjectSection] = [] used_slugs: set[str] = set() for idx, title in enumerate(required_sections or [], start=1): title = (title or "").strip() if not title: continue slug = section_title_to_slug(title) base_slug = slug n = 2 while slug in used_slugs: slug = f"{base_slug}_{n}" n += 1 used_slugs.add(slug) titles_map[slug] = title sec = ProjectSection( id=str(uuid.uuid4()), project_id=project_id, section_type=slug, order=idx, content="", is_approved=False, generated_by_ai=False, ) db.add(sec) created.append(sec) return created, titles_map def seed_sections_from_templates(db, project_id: str, program_type: str) -> list[ProjectSection]: """Fallback: sekcje z ProjectSectionTemplate (SMART/ARIMR/ZUS_BHP).""" norm_type = normalize_program_type(program_type) templates = ( db.query(ProjectSectionTemplate) .filter(ProjectSectionTemplate.program_type == norm_type) .order_by(ProjectSectionTemplate.order.asc()) .all() ) if not templates and norm_type != "SMART": templates = ( db.query(ProjectSectionTemplate) .filter(ProjectSectionTemplate.program_type == "SMART") .order_by(ProjectSectionTemplate.order.asc()) .all() ) if not templates: from scripts.seed_section_templates import TEMPLATES dicts = [t for t in TEMPLATES if t.get("program_type") == norm_type] if not dicts: dicts = [t for t in TEMPLATES if t.get("program_type") == "SMART"] created = [] for tmpl in dicts: sec = ProjectSection( id=str(uuid.uuid4()), project_id=project_id, section_type=tmpl.get("section_type"), order=tmpl.get("order"), content="", is_approved=False, generated_by_ai=False, ) db.add(sec) created.append(sec) return created created = [] for tmpl in templates: sec = ProjectSection( id=str(uuid.uuid4()), project_id=project_id, section_type=tmpl.section_type, order=tmpl.order, content="", is_approved=False, generated_by_ai=False, ) db.add(sec) created.append(sec) return created def reseed_empty_sections_from_regulation( db, project_id: str, required_sections: list[str], ) -> dict[str, str]: """Dodaje brakujące sekcje z regulaminu — nie nadpisuje wypełnionych.""" existing = ( db.query(ProjectSection) .filter(ProjectSection.project_id == project_id) .order_by(ProjectSection.order.asc()) .all() ) existing_types = {s.section_type for s in existing} existing_titles_lower = { (s.section_type or "").replace("_", " ").lower() for s in existing } max_order = max((s.order for s in existing), default=0) titles_map: dict[str, str] = {} order = max_order for title in required_sections or []: title = (title or "").strip() if not title: continue slug = section_title_to_slug(title) title_lower = title.lower() if slug in existing_types: continue if title_lower in existing_titles_lower: continue # Pomiń sekcje z treścią (fuzzy: ten sam slug lub podobny tytuł) fuzzy_hit = any( (s.content or "").strip() and ( s.section_type == slug or title_lower in (s.section_type or "").replace("_", " ").lower() ) for s in existing ) if fuzzy_hit: continue order += 1 sec = ProjectSection( id=str(uuid.uuid4()), project_id=project_id, section_type=slug, order=order, content="", is_approved=False, generated_by_ai=False, ) db.add(sec) titles_map[slug] = title existing_types.add(slug) return titles_map