Correct overclaim: rating 0 means N/A, not confirmed party wall

Reviewer pushback: the gov EPC code table (epc_codes.csv) defines
energy_efficiency_rating 0 generically as "N/A", not specifically
"party wall". The payload carries no other identifying field, so the
actual cause can't be determined from the data alone. The fix itself
is unaffected (still calc-neutral regardless of cause) — just
correcting comments/test naming to not assert an unconfirmed fact.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Jun-te Kim 2026-07-14 16:25:02 +00:00
parent 7f7dcbc059
commit 4bc042f56a
2 changed files with 24 additions and 18 deletions

View file

@ -3916,16 +3916,20 @@ def _normalize_sap_schema_16_x(data: Dict[str, Any]) -> Dict[str, Any]:
# Every "Top-floor flat" SAP-Schema-15.0 cert we've seen (confirmed on 21
# real certs from task 9d271e98-96e6-4be6-bb50-e9be6f954003, portfolio
# 796 — e.g. cert 8204-4998-7729-4026-5693, UPRN 100061740136) lodges a
# second `walls[]` entry that is a bare zero-rated party-wall boundary
# element — `{"energy_efficiency_rating": 0,
# "environmental_efficiency_rating": 0}`, no `description` key at all —
# unlike the sibling `floors`/`roofs` "(other premises above/below)"
# sentinel convention, which DOES carry text. RdSapSchema17_1 requires
# `description`. Default it to "" rather than fabricate construction
# text: `_joined_descriptions` (heat_transmission.py) already filters out
# falsy descriptions, so a rating-0/no-description element contributes
# nothing to the wall U-value derivation either way — this default is
# calc-neutral, purely unblocking the parse.
# second `walls[]` entry with both ratings 0 and no `description` key at
# all — `{"energy_efficiency_rating": 0,
# "environmental_efficiency_rating": 0}`. Per the gov EPC code table
# (epc_codes.csv: energy_efficiency_rating 0 == "N/A"), rating 0 just
# means "not applicable" generically — the payload carries no other
# field (no wall type/construction code), so we cannot tell from the
# data alone *why* it's N/A (a party wall to a neighbour is a plausible
# cause on a top-floor flat, but unconfirmed). Whatever the cause,
# RdSapSchema17_1 requires `description`, so default it to "" rather
# than fabricate construction text: `_joined_descriptions`
# (heat_transmission.py) already filters out falsy descriptions, so an
# N/A-rated element with no description contributes nothing to the wall
# U-value derivation either way — this default is calc-neutral, purely
# unblocking the parse.
for elements_key in ("roofs", "walls", "floors", "windows"):
for element in _dicts(d.get(elements_key)):
if (

View file

@ -141,17 +141,19 @@ class TestFromSapSchema15_0:
assert epc.has_hot_water_cylinder is False
assert epc.sap_heating.cylinder_size == 1
def test_defaults_description_on_a_bare_zero_rated_party_wall(self) -> None:
def test_defaults_description_on_a_bare_n_a_rated_wall_element(self) -> None:
# Task 9d271e98-96e6-4be6-bb50-e9be6f954003 (portfolio 796 / scenario
# 1268) failed 21 "Top-floor flat" properties with "EnergyElement:
# missing required field 'description'": every one lodges a second
# `walls[]` entry that is a bare zero-rated party-wall boundary
# element (`{"energy_efficiency_rating": 0,
# "environmental_efficiency_rating": 0}`, no description key at all)
# — property_id 735232 / UPRN 100061740136 / cert
# 8204-4998-7729-4026-5693 here. Must default to "" rather than fail
# loud (calc-neutral — `_joined_descriptions` already drops falsy
# descriptions).
# `walls[]` entry with both ratings 0 and no description key at all
# (`{"energy_efficiency_rating": 0, "environmental_efficiency_rating":
# 0}`) — property_id 735232 / UPRN 100061740136 / cert
# 8204-4998-7729-4026-5693 here. Rating 0 == "N/A" per the gov EPC
# code table (epc_codes.csv); the payload carries no other field, so
# the specific cause of the N/A (party wall? something else?) isn't
# determinable from the data. Must default to "" rather than fail
# loud regardless of cause (calc-neutral — `_joined_descriptions`
# already drops falsy descriptions).
epc = EpcPropertyDataMapper.from_api_response(
load("sap_15_0_uprn_100061740136.json")
)