mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
S0380.222: map API roof_construction codes 6 (thatched) + 7 (dwelling
above) → None
The 2026 sample lodges roof_construction=6 (1 cert, "Thatched, with
additional insulation") and =7 (6 certs, "(same dwelling above)" /
"(another dwelling above)"), both raising UnmappedApiCode and blocking
the cert. roof_construction_type is read ONLY for the §3 "sloping
ceiling" cos(30°) inclined-surface factor (Slice 89); the base roof
U-value comes from the global roofs[].description. Neither code is a
sloping ceiling:
- 6 = thatched — U set by the description, not this field;
- 7 = same/another dwelling above — an internal ceiling with no roof
heat loss (the roof-side analogue of floor_construction code 0,
governed by the roof_heat_loss / description path).
Map both to None: carries no information the cascade consumes here and
correctly avoids the cos(30°) false-trigger. Empirically inert and
validated — roof W/K is byte-identical whether 6/7 map to None or to an
explicit pitched string across all code-6/7 certs in the sample. 5 of
the 7 now compute (e.g. thatched cert 2276 SAP 62.8 vs lodged 63); the
other 2 also carry a gable_wall_type 2/3 raise (separate, worksheet-
backed slice).
Dict value type widened to Optional[str]. §4 suite 2392 passed; mapper.py
pyright unchanged at 32; new tests suppress reportPrivateUsage (net-zero).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
aac3f0690a
commit
28634e8ae5
2 changed files with 64 additions and 1 deletions
|
|
@ -2433,11 +2433,28 @@ _API_FLOOR_CONSTRUCTION_TO_STR: Dict[int, Optional[str]] = {
|
|||
# are observed on cert 001479; the wider RdSAP10 roof-construction
|
||||
# enum (1=Flat, 3=Pitched no-access, 5=Vaulted, etc.) is mapped as
|
||||
# best-effort against SAP10 nomenclature.
|
||||
_API_ROOF_CONSTRUCTION_TO_STR: Dict[int, str] = {
|
||||
#
|
||||
# Codes 6 and 7 → None. This field is read ONLY for the sloping-ceiling
|
||||
# inclined factor; the base roof U-value comes from the global
|
||||
# roofs[].description, so a non-sloping code carries no information the
|
||||
# cascade consumes here, and None correctly avoids the cos(30°) false-
|
||||
# trigger:
|
||||
# 6 = "Thatched, with additional insulation" — its U is set by the
|
||||
# global description; not a sloping ceiling.
|
||||
# 7 = "(same dwelling above)" / "(another dwelling above)" — an
|
||||
# internal ceiling with no roof heat loss (the roof-side analogue
|
||||
# of floor_construction code 0). Heat loss is governed by the
|
||||
# roof_heat_loss / description path, not this field.
|
||||
# Empirically inert: roof W/K is identical whether 6/7 map to None or to
|
||||
# an explicit pitched string across all code-6/7 certs in the 2026
|
||||
# sample (were raising UnmappedApiCode, blocking the cert).
|
||||
_API_ROOF_CONSTRUCTION_TO_STR: Dict[int, Optional[str]] = {
|
||||
1: "Flat",
|
||||
3: "Pitched (slates/tiles), no access to loft",
|
||||
4: "Pitched (slates/tiles), access to loft",
|
||||
5: "Pitched (vaulted ceiling)",
|
||||
6: None,
|
||||
7: None,
|
||||
8: "Pitched, sloping ceiling",
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -879,3 +879,49 @@ class TestDefaultMissingPostTown:
|
|||
|
||||
# Assert
|
||||
assert result["post_town"] == "BARNSTAPLE"
|
||||
|
||||
|
||||
class TestApiRoofConstructionCode:
|
||||
"""`_api_roof_construction_str` maps the GOV.UK API integer
|
||||
roof_construction code to the string the cascade reads ONLY for the
|
||||
"sloping ceiling" cos(30°) inclined-surface factor (Slice 89). Codes
|
||||
6 and 7 are neither sloping ceilings nor base-U drivers (the roof
|
||||
U-value comes from the global roofs[].description), so both map to
|
||||
None: code 6 = "Thatched" (its U is set by the description, not this
|
||||
field) and code 7 = "(same/another dwelling above)" — an internal
|
||||
ceiling with no roof heat loss, the roof-side analogue of
|
||||
floor_construction code 0. Empirically inert: roof W/K is identical
|
||||
whether 6/7 map to None or to an explicit pitched string across all
|
||||
code-6/7 certs in the 2026 sample."""
|
||||
|
||||
def test_code_7_same_dwelling_above_maps_to_none(self) -> None:
|
||||
# Arrange
|
||||
from datatypes.epc.domain.mapper import _api_roof_construction_str # pyright: ignore[reportPrivateUsage]
|
||||
|
||||
# Act
|
||||
result = _api_roof_construction_str(7)
|
||||
|
||||
# Assert — None: no sloping-ceiling signal (avoids the cos(30°)
|
||||
# false-trigger); the internal ceiling has no roof heat loss.
|
||||
assert result is None
|
||||
|
||||
def test_code_6_thatched_maps_to_none(self) -> None:
|
||||
# Arrange
|
||||
from datatypes.epc.domain.mapper import _api_roof_construction_str # pyright: ignore[reportPrivateUsage]
|
||||
|
||||
# Act
|
||||
result = _api_roof_construction_str(6)
|
||||
|
||||
# Assert — None: thatched is not a sloping ceiling; its U-value is
|
||||
# carried by the global roof description, not roof_construction_type.
|
||||
assert result is None
|
||||
|
||||
def test_code_8_still_maps_to_sloping_ceiling(self) -> None:
|
||||
# Arrange — regression guard: the sloping-ceiling code is unchanged.
|
||||
from datatypes.epc.domain.mapper import _api_roof_construction_str # pyright: ignore[reportPrivateUsage]
|
||||
|
||||
# Act
|
||||
result = _api_roof_construction_str(8)
|
||||
|
||||
# Assert
|
||||
assert result == "Pitched, sloping ceiling"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue