"""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-17.0/uprn_10023444320" ) 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())