Model/backend/documents_parser
Khalim Conn-Kowlessar 7992154ffd Slice 100c: API path — surface PV arrays + gap-aware glazing lookup
Two final API gaps to close cert 9501 at 1e-4:

(a) PV array surfacing — third shape variant:
    Schema-21 EPCs carry `photovoltaic_supply` as one of three shapes:
    - legacy `{"none_or_no_details": {...}}` (PV absent / roof-only)
    - nested list `[[{...}], ...]` (cohort cert 2130)
    - dict wrapper `{"pv_arrays": [{...}]}` (cert 9501)
    The schema's `PhotovoltaicSupply` modelled only `none_or_no_details`
    — cert 9501's measured arrays under `pv_arrays` were silently
    dropped (Δ -£250 PV credit → -9.32 SAP). Added
    `SchemaPhotovoltaicArray` dataclass + `pv_arrays:
    Optional[List[...]]` sibling field on `PhotovoltaicSupply`; updated
    `_map_schema_21_pv` to dispatch on the new shape.

(b) Gap-aware glazing lookup (RdSAP 10 Table 24 row 2):
    DG pre-2002 spec U varies by gap: 6mm=3.1 / 12mm=2.8 / 16+=2.7.
    The mapper's flat `_API_GLAZING_TYPE_TO_TRANSMISSION[3]` returned
    U=2.8 unconditionally — cert 9501 lodges `glazing_gap="16+"` so
    the worksheet uses 2.7. Added `_API_GLAZING_TYPE_GAP_TO_
    TRANSMISSION` keyed by (type, gap) with the spec-table values for
    code 3; `_api_glazing_transmission` consults the per-gap dict
    first, falling back to type-only when no gap entry exists.
    Refactored the inline `SapWindow(...)` build into
    `_api_sap_window` helper (also nets one pyright error: net-zero
    actually improved 33 → 32 on mapper.py).

Effect on cert 9501 API path:
- sap_continuous 59.20 → **68.525161** (= worksheet 68.5252 exact;
  Δ -0.000039 — well within 1e-4)
- total_fuel_cost £1101 → £849.21 (= worksheet 849.21 exact)
- pv_export_credit £0 → £250.02 (= worksheet 250.02 exact)

Re-pinned residuals (5 cohort certs with glazing_gap="16+" or 6 now
pick up the spec-correct DG-pre-2002 U):
- 0300: PE +8.44 → +8.28, CO2 -0.23 → -0.25
- 6035: PE +48.30 → +47.85, CO2 +1.10 → +1.09
- 7536: PE -6.51 → -7.08, CO2 -0.17 → -0.19
- 8135: PE -5.31 → -3.66 (gap=6 spec U=3.1), CO2 -0.07 → -0.04
- 2130: PE -38.18 → -38.63, CO2 +0.30 → +0.30

Layer 4 chain test `test_api_9501_full_chain_sap_matches_worksheet
_pdf_exactly` added — third production gate after cert 001479 +
cert 0330. First flat-shaped cert in the production gate set.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 22:13:48 +00:00
..
handler address JTK review comments 2026-04-20 15:11:17 +00:00
tests Slice 100c: API path — surface PV arrays + gap-aware glazing lookup 2026-05-26 22:13:48 +00:00
__init__.py Map to RdSapSiteNotes from site notes JSON 🟥 2026-04-16 13:54:03 +00:00
db_writer.py include updating epc_property_data to pashub to ara workflow 2026-04-29 09:55:14 +00:00
elmhurst_extractor.py Slice 99d: surface PV array from Elmhurst Summary §19.0 2026-05-26 21:38:14 +00:00
extractor.py Handle wall thickness "Unmeasurable" 🟩 2026-04-30 16:41:16 +00:00
local_runner.py update local runner to work for elmhurst 2026-04-24 14:01:36 +00:00
parser.py load ecmk site notes to db 2026-04-29 11:20:47 +00:00
pdf.py update local runner to work for elmhurst 2026-04-24 14:01:36 +00:00