Model/scripts/hyde/elmhurst_download.py
Jun-te Kim a7d3e00178 32
2026-06-23 15:42:00 +00:00

109 lines
4.2 KiB
Python

"""Download the Input Summary + SAP Worksheets PDFs from Elmhurst (POC).
Closes the validate-cert loop's only manual export: pulls the two PDFs off the
Energy Report Summary page and saves them where `compare_epc_paths.py` reads:
* Input Summary -> elmhurst_summary.pdf (inputs; parse_site_notes_pdf -> EpcPropertyData)
* SAP Worksheets -> elmhurst_worksheet.pdf (the SAP calculation)
DISPLAY=:99 python scripts/hyde/elmhurst_download.py
SAFETY: this script clicks ONLY the two report buttons below. It NEVER clicks
Submit (`LinkButtonSubmitEPR`) — submitting lodges the assessment. The allow-list
is enforced in code.
"""
from __future__ import annotations
import sys
from pathlib import Path
from playwright.sync_api import (
Page,
TimeoutError as PlaywrightTimeoutError,
sync_playwright,
)
HERE = Path(__file__).parent
SESSION_DIR = HERE / ".elmhurst-session"
SAMPLE_DIR = (
HERE.parent.parent
/ "backend/epc_api/json_samples/real_life_examples"
/ "SAP-Schema-16.0/uprn_10070004512"
)
ASSESSMENT_GUID = "B44A0DB4-4C08-4241-B818-86F060172105"
ENTRY_URL = (
"https://rdsap10online.elmhurstenergy.co.uk/Processing/WebFormAddress.aspx"
f"?Guid={ASSESSMENT_GUID}"
)
# (button id, output filename). The ONLY buttons this script may click.
REPORTS: list[tuple[str, str]] = [
("ContentBody_ContentPlaceHolder1_LinkButtonSummary", "elmhurst_summary.pdf"),
("ContentBody_ContentPlaceHolder1_ButtonDebugInforPdf", "elmhurst_worksheet.pdf"),
]
# Buttons this script must NEVER touch (belt-and-braces).
_FORBIDDEN = {"ContentBody_ContentPlaceHolder1_LinkButtonSubmitEPR"}
def _download_one(page: Page, button_id: str, out: Path) -> bool:
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:
# Some report buttons open the PDF in a popup rather than a download.
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 download_reports() -> int:
SAMPLE_DIR.mkdir(parents=True, exist_ok=True)
ok = True
with sync_playwright() as p:
context = p.chromium.launch_persistent_context(
user_data_dir=str(SESSION_DIR),
headless=False,
accept_downloads=True,
viewport={"width": 1400, "height": 1000},
)
page = context.pages[0] if context.pages else context.new_page()
page.on("dialog", lambda d: d.accept())
print(f"entering assessment {ASSESSMENT_GUID} ...", flush=True)
page.goto(ENTRY_URL, wait_until="networkidle", timeout=60_000)
# The Summary nav only fires AFTER the Recommendations validation gate has
# been visited — clicking Summary straight from the Address page is a no-op.
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)
context.close()
print("DONE." if ok else "DONE (with errors — see above).", flush=True)
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(download_reports())