Model/scripts/hyde/download_100010086084.py
Jun-te Kim 283b311fcd Real Elmhurst RdSAP10 ground truth for one SAP-Schema-15.0 cert
Built UPRN 100010086084 (the largest gov-API-vs-lodged outlier from the
20-cert batch) in Elmhurst's accredited RdSAP10 tool to get real ground
truth, per feedback that lodged rating isn't a valid comparison target.

Elmhurst worksheet: SAP 46-48 (orientation-dependent), vs this engine's
53 on gov-API inputs -- a real ~5-7pt residual gap. Ruled out with
direct empirical tests: total_floor_area, missing party_wall_length,
region/climate (SAP rating always uses UK-average weather per Appendix
U -- confirmed inert), boiler/secondary efficiency (exact match, 66%/
63% both sides). Confirmed real but NOT blind-fixed (need more than one
cert's evidence, and a wall-U-value patch tested the wrong direction in
isolation): a wall-thickness-unknown U-value fallback mismatch (this
engine 1.70, Elmhurst's own "unknown" resolution 1.40) and a window
U-value divergence (1.85 vs 2.52 W/m2K for the same nominal glazing
pick). Single-window orientation confirmed a ~2pt partial contributor.

Also fixes a small, separate, evidenced gap the build surfaced: the
Elmhurst summary parser didn't recognise the live tool's "Medium
(131-170L)" cylinder-size label (only the bare "Medium").

Full write-up is in the RealCertExpectation comment for this cert in
test_real_cert_sap_accuracy.py and the worklist. Residual gap is
intentionally left open for follow-up, not force-closed.
2026-07-09 22:31:01 +00:00

71 lines
2.8 KiB
Python

"""Download the Input Summary + SAP Worksheets PDFs for UPRN 100010086084,
via a fresh-login `E.session()` (elmhurst_download.py's persistent-context
login is stale per elmhurst_lib's own documented auth gotcha).
SAFETY: clicks ONLY the two allow-listed report buttons below. NEVER clicks
Submit (`LinkButtonSubmitEPR`).
DISPLAY=:99 python scripts/hyde/download_100010086084.py
"""
from __future__ import annotations
from pathlib import Path
import elmhurst_lib as E
from playwright.sync_api import TimeoutError as PlaywrightTimeoutError
SAMPLE_DIR = (
Path(__file__).parent.parent.parent
/ "backend/epc_api/json_samples/real_life_examples"
/ "SAP-Schema-15.0/uprn_100010086084"
)
REPORTS = [
("ContentBody_ContentPlaceHolder1_LinkButtonSummary", "elmhurst_summary.pdf"),
("ContentBody_ContentPlaceHolder1_ButtonDebugInforPdf", "elmhurst_worksheet.pdf"),
]
_FORBIDDEN = {"ContentBody_ContentPlaceHolder1_LinkButtonSubmitEPR"}
def _download_one(page, button_id, out):
assert button_id not in _FORBIDDEN, f"refusing to click forbidden {button_id}"
print(f" clicking {button_id} -> {out.name} ...", flush=True)
try:
with page.expect_download(timeout=60_000) as dl:
page.click(f"#{button_id}", timeout=20_000)
dl.value.save_as(str(out))
except PlaywrightTimeoutError:
try:
with page.expect_popup(timeout=10_000) as pop:
page.click(f"#{button_id}", timeout=20_000)
popup = pop.value
with popup.expect_download(timeout=30_000) as dl2:
pass
dl2.value.save_as(str(out))
except PlaywrightTimeoutError:
print(f" !! no download/popup for {button_id}", flush=True)
return False
size = out.stat().st_size if out.exists() else 0
print(f" saved {out} ({size:,} bytes)", flush=True)
return out.exists() and size > 0
def main():
SAMPLE_DIR.mkdir(parents=True, exist_ok=True)
ok = True
with E.session() as (ctx, page):
print("navigating via Recommendations ...", flush=True)
with page.expect_navigation(wait_until="networkidle", timeout=60_000):
page.click("#ContentBody_buttonActionRecommendations_Link", timeout=15_000)
page.wait_for_timeout(600)
print("navigating to Energy Report Summary ...", flush=True)
with page.expect_navigation(wait_until="networkidle", timeout=60_000):
page.click("#ContentBody_buttonActionSummary_Link", timeout=15_000)
page.wait_for_timeout(600)
for button_id, fname in REPORTS:
ok = _download_one(page, button_id, SAMPLE_DIR / fname) and ok
page.wait_for_timeout(600)
print("DONE." if ok else "DONE (with errors).", flush=True)
return 0 if ok else 1
if __name__ == "__main__":
raise SystemExit(main())