"""Capture a real EPC certificate by UPRN for the SAP accuracy test suite. Resolves a UPRN to its latest lodged certificate via the GOV.UK EPB register, downloads the full ``data`` payload (the exact shape ``EpcPropertyDataMapper.from_api_response`` consumes), and freezes it under the schema-bucketed sample tree the accuracy test reads: backend/epc_api/json_samples/real_life_examples//uprn_/epc.json It also prints the lodged SAP rating and what ``Sap10Calculator`` currently produces, so a new case can be added to ``tests/domain/sap10_calculator/test_real_cert_sap_accuracy.py`` with the right ``schema`` / ``sap_score`` straight away. USAGE ----- PYTHONPATH=/workspaces/model python scripts/fetch_real_life_epc_sample.py [ ...] Token is read from ``backend/.env`` (``OPEN_EPC_API_TOKEN``, falling back to ``EPC_AUTH_TOKEN``). Re-running overwrites the sample. """ from __future__ import annotations import json import os import pathlib import sys from typing import Any import httpx from dotenv import load_dotenv _BASE = "https://api.get-energy-performance-data.communities.gov.uk" _SAMPLES_ROOT = pathlib.Path( "backend/epc_api/json_samples/real_life_examples" ) def _headers() -> dict[str, str]: load_dotenv("backend/.env") token = os.environ.get("OPEN_EPC_API_TOKEN") or os.environ["EPC_AUTH_TOKEN"] return {"Authorization": f"Bearer {token}", "Accept": "application/json"} def _latest_cert_number(uprn: int, headers: dict[str, str]) -> str: resp = httpx.get( f"{_BASE}/api/domestic/search", params={"uprn": uprn}, headers=headers, timeout=30.0, ) resp.raise_for_status() rows: list[dict[str, Any]] = resp.json().get("data", []) if not rows: raise SystemExit(f"UPRN {uprn}: no certificates found") latest = max(rows, key=lambda r: r["registrationDate"]) return str(latest["certificateNumber"]) def _fetch_cert_data(cert_num: str, headers: dict[str, str]) -> dict[str, Any]: resp = httpx.get( f"{_BASE}/api/certificate", params={"certificate_number": cert_num}, headers=headers, timeout=30.0, ) resp.raise_for_status() data: dict[str, Any] = resp.json()["data"] return data def _report(uprn: int, cert_num: str, data: dict[str, Any]) -> None: """Print lodged rating + current calculator output for the captured cert.""" from infrastructure.epc_client.epc_client_service import EpcClientService from domain.sap10_calculator.calculator import Sap10Calculator from unittest.mock import patch def _mock(*_a: object, **_k: object) -> httpx.Response: return httpx.Response( 200, json={"data": data}, request=httpx.Request("GET", "x") ) print(f" schema_type : {data.get('schema_type')}") print(f" lodged rating : {data.get('energy_rating_current')}") service = EpcClientService(auth_token="test-token") try: with patch("httpx.get", side_effect=_mock): epc = service.get_by_certificate_number(cert_num) except ValueError as exc: # Full-SAP (vs RdSAP) certs aren't supported by the mapper, so the # calculator front-end can't consume them. Captured for reference # but NOT addable to the RdSAP accuracy suite. print(f" NOT MAPPABLE : {exc}") return result = Sap10Calculator().calculate(epc) print(f" calc sap_score : {result.sap_score}") print(f" space_heating_kwh : {result.space_heating_kwh_per_yr:.4f}") print(f" main_heating_kwh : {result.main_heating_fuel_kwh_per_yr:.4f}") print(f" hot_water_kwh : {result.hot_water_kwh_per_yr:.4f}") print(f" co2_kg_per_yr : {result.co2_kg_per_yr:.4f}") def capture(uprn: int) -> None: headers = _headers() cert_num = _latest_cert_number(uprn, headers) data = _fetch_cert_data(cert_num, headers) schema_type = str(data.get("schema_type") or "unknown-schema") out_dir = _SAMPLES_ROOT / schema_type / f"uprn_{uprn}" out_dir.mkdir(parents=True, exist_ok=True) out = out_dir / "epc.json" out.write_text(json.dumps(data, indent=2)) print(f"UPRN {uprn} -> cert {cert_num}") print(f" wrote : {out}") _report(uprn, cert_num, data) def main() -> None: if len(sys.argv) < 2: raise SystemExit(__doc__) for arg in sys.argv[1:]: capture(int(arg)) if __name__ == "__main__": main()