File size: 3,462 Bytes
b76f199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
"""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)