"""Survey product tier → section templates, order, and export labelling. Resolves :func:`get_template` and section order by ``Report.survey_level`` (1/2/3). ``None`` is treated as **Level 3** for template purposes (legacy reports). """ from __future__ import annotations from dataclasses import dataclass from typing import TYPE_CHECKING, Final from app.templates.rics_templates import ( LEVEL_3_DOCX_TITLE, LEVEL_3_PRODUCT_LABEL, SectionTemplate, get_level3_templates, ) from app.templates.rics_templates_l1 import ( LEVEL1_GROUP_LABELS, LEVEL1_TEMPLATES, LEVEL_1_DOCX_TITLE, LEVEL_1_PRODUCT_LABEL, ) from app.templates.rics_templates_l2 import ( LEVEL2_GROUP_LABELS, LEVEL2_TEMPLATES, LEVEL_2_DOCX_TITLE, LEVEL_2_PRODUCT_LABEL, ) from app.templates.rics_templates import GROUP_LABELS as LEVEL3_GROUP_LABELS if TYPE_CHECKING: pass @dataclass(frozen=True, slots=True) class SurveyTemplatePack: """All metadata needed to render and generate a report for one RICS product tier.""" level: int product_label: str docx_title: str section_order: tuple[str, ...] group_labels: dict[str, str] _by_code: dict[str, SectionTemplate] def get(self, code: str) -> SectionTemplate | None: return self._by_code.get(code) def resolve_survey_level(survey_level: int | None) -> int: """Map DB ``survey_level`` to a pack id. ``None`` → 3 (Building Survey / legacy).""" if survey_level is None: return 3 return max(1, min(3, int(survey_level))) def _build_pack( level: int, product_label: str, docx_title: str, templates: list[SectionTemplate], group_labels: dict[str, str], ) -> SurveyTemplatePack: by_code = {t.code: t for t in templates} order = tuple(t.code for t in templates) return SurveyTemplatePack( level=level, product_label=product_label, docx_title=docx_title, section_order=order, group_labels=dict(group_labels), _by_code=by_code, ) _PACK1 = _build_pack(1, LEVEL_1_PRODUCT_LABEL, LEVEL_1_DOCX_TITLE, LEVEL1_TEMPLATES, LEVEL1_GROUP_LABELS) _PACK2 = _build_pack(2, LEVEL_2_PRODUCT_LABEL, LEVEL_2_DOCX_TITLE, LEVEL2_TEMPLATES, LEVEL2_GROUP_LABELS) _PACK3 = _build_pack( 3, LEVEL_3_PRODUCT_LABEL, LEVEL_3_DOCX_TITLE, get_level3_templates(), LEVEL3_GROUP_LABELS, ) _SURVEY_PACKS: Final[dict[int, SurveyTemplatePack]] = { 1: _PACK1, 2: _PACK2, 3: _PACK3, } def get_survey_pack(survey_level: int | None) -> SurveyTemplatePack: """Return the template pack for ``survey_level`` (``None`` → Level 3).""" return _SURVEY_PACKS[resolve_survey_level(survey_level)] def get_template(code: str, survey_level: int | None = None) -> SectionTemplate | None: """Resolve a section template by code and survey product tier.""" return get_survey_pack(survey_level).get(code) def section_order_for_survey(survey_level: int | None) -> list[str]: """Ordered section codes for export and UI.""" return list(get_survey_pack(survey_level).section_order) # Union of every section code ever used across packs (API validation). ALL_VALID_SECTION_CODES: frozenset[str] = frozenset( set(_PACK1.section_order) | set(_PACK2.section_order) | set(_PACK3.section_order) ) # Legacy export: Level 3 order only (many tests / callers expect full BS list length). SECTION_ORDER_LEVEL3: list[str] = list(_PACK3.section_order)