Spaces:
Running
Running
| """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 | |