mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-27 23:35:01 +00:00
Overlay PasHub as-surveyed performance onto saved Site-Notes property 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
43fac40595
commit
377c030c77
2 changed files with 68 additions and 11 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import os
|
||||
from dataclasses import replace
|
||||
from datetime import datetime, timezone
|
||||
from typing import Callable, List, NamedTuple, Optional, cast
|
||||
from typing import Any, Callable, Dict, List, NamedTuple, Optional, cast
|
||||
|
||||
from backend.app.db.connection import db_session
|
||||
from infrastructure.postgres.uploaded_file_table import (
|
||||
|
|
@ -15,11 +16,13 @@ from backend.pashub_fetcher.pashub_client import (
|
|||
DownloadedFile,
|
||||
DownloadedFiles,
|
||||
PashubClient,
|
||||
RdSapSummary,
|
||||
UnauthorizedError,
|
||||
)
|
||||
from backend.pashub_fetcher.pashub_to_ara_trigger_request import (
|
||||
PashubToAraTriggerRequest,
|
||||
)
|
||||
from datatypes.epc.domain.epc import Epc
|
||||
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
||||
from utils.logger import setup_logger
|
||||
from utils.s3 import upload_file_to_s3
|
||||
|
|
@ -28,6 +31,31 @@ from utils.sharepoint.domna_sharepoint_client import DomnaSharepointClient
|
|||
logger = setup_logger()
|
||||
|
||||
|
||||
def _overlay_lodged_performance(
|
||||
epc_data: EpcPropertyData, summary: RdSapSummary
|
||||
) -> EpcPropertyData:
|
||||
"""Fill the as-surveyed current-performance block PasHub carries but the
|
||||
Site Notes PDF does not. Best-effort per field: values PasHub returns as
|
||||
null are left empty. The EPC Band is derived from the SAP score via
|
||||
`Epc.from_sap_score` rather than PasHub's opaque band code."""
|
||||
updates: Dict[str, Any] = {}
|
||||
|
||||
if summary.pre_sap_rating is not None:
|
||||
score = int(summary.pre_sap_rating)
|
||||
updates["energy_rating_current"] = score
|
||||
updates["current_energy_efficiency_band"] = Epc.from_sap_score(score)
|
||||
|
||||
if summary.pre_current_co2_emissions is not None:
|
||||
updates["co2_emissions_current"] = summary.pre_current_co2_emissions
|
||||
|
||||
if summary.pre_current_energy_consumption is not None:
|
||||
updates["energy_consumption_current"] = int(
|
||||
summary.pre_current_energy_consumption
|
||||
)
|
||||
|
||||
return replace(epc_data, **updates)
|
||||
|
||||
|
||||
class _FileUploadRecord(NamedTuple):
|
||||
file_path: str
|
||||
file_type: Optional[str]
|
||||
|
|
@ -101,7 +129,7 @@ class PashubService:
|
|||
upload_records = self._upload_to_s3_and_update_db(
|
||||
downloaded.core, uprn, hubspot_deal_id, file_source
|
||||
)
|
||||
self._save_site_notes(upload_records)
|
||||
self._save_site_notes(upload_records, active_client, job_id)
|
||||
|
||||
if downloaded.other:
|
||||
self._upload_to_s3_and_update_db(
|
||||
|
|
@ -190,17 +218,44 @@ class PashubService:
|
|||
|
||||
return upload_records
|
||||
|
||||
def _save_site_notes(self, upload_records: List[_FileUploadRecord]) -> None:
|
||||
def _fetch_rdsap_summary(
|
||||
self, active_client: PashubClient, job_id: str
|
||||
) -> RdSapSummary:
|
||||
try:
|
||||
return active_client.get_rdsap_summary_by_job_id(job_id)
|
||||
except UnauthorizedError:
|
||||
if active_client is not self._pashub_client:
|
||||
raise
|
||||
logger.info(
|
||||
f"PasHub credentials unauthorized for RdSAP Summary on job {job_id}; "
|
||||
"retrying with CoordinationHub credentials"
|
||||
)
|
||||
return self._get_coordination_client().get_rdsap_summary_by_job_id(job_id)
|
||||
|
||||
def _save_site_notes(
|
||||
self,
|
||||
upload_records: List[_FileUploadRecord],
|
||||
active_client: PashubClient,
|
||||
job_id: str,
|
||||
) -> None:
|
||||
for record in upload_records:
|
||||
if (
|
||||
record.file_type is None
|
||||
or FileTypeEnum(record.file_type) != FileTypeEnum.RD_SAP_SITE_NOTE
|
||||
):
|
||||
continue
|
||||
|
||||
# The RdSAP Summary carries the as-surveyed Lodged Performance the
|
||||
# Site Notes PDF lacks. Fetch it outside the parse try/except so a
|
||||
# missing summary fails loudly rather than persisting a Property with
|
||||
# an incomplete Lodged Performance.
|
||||
summary: RdSapSummary = self._fetch_rdsap_summary(active_client, job_id)
|
||||
|
||||
try:
|
||||
epc_data: EpcPropertyData = parse_site_notes_pdf(
|
||||
record.file_path, uprn=record.uprn
|
||||
)
|
||||
epc_data = _overlay_lodged_performance(epc_data, summary)
|
||||
with db_session() as session:
|
||||
save_epc_property_data(
|
||||
session, epc_data, uploaded_file_id=record.uploaded_file_id
|
||||
|
|
|
|||
|
|
@ -290,13 +290,15 @@ def test_run_uses_hubspot_deal_id_path_when_no_uprn() -> None:
|
|||
|
||||
|
||||
def test_run_parses_and_saves_site_notes_for_rd_sap_site_note_file() -> None:
|
||||
mock_client = MagicMock(spec=PashubClient)
|
||||
mock_client.get_uprn_by_job_id.return_value = None
|
||||
mock_client.get_evidence_files_by_job_id.return_value = make_downloaded(
|
||||
core=["/tmp/RdSAP_SiteNote_001.pdf"]
|
||||
mock_client = _site_note_client(
|
||||
summary=RdSapSummary(
|
||||
pre_sap_rating=None,
|
||||
pre_current_co2_emissions=None,
|
||||
pre_current_energy_consumption=None,
|
||||
)
|
||||
)
|
||||
|
||||
fake_epc_data = MagicMock()
|
||||
parsed = make_epc_property_data()
|
||||
fake_session = MagicMock()
|
||||
fake_uploaded_file_id = 99
|
||||
|
||||
|
|
@ -306,7 +308,7 @@ def test_run_parses_and_saves_site_notes_for_rd_sap_site_note_file() -> None:
|
|||
patch("backend.pashub_fetcher.pashub_service.upload_file_to_s3"),
|
||||
patch(
|
||||
"backend.pashub_fetcher.pashub_service.parse_site_notes_pdf",
|
||||
return_value=fake_epc_data,
|
||||
return_value=parsed,
|
||||
) as mock_parse,
|
||||
patch(
|
||||
"backend.pashub_fetcher.pashub_service.save_epc_property_data"
|
||||
|
|
@ -320,9 +322,9 @@ def test_run_parses_and_saves_site_notes_for_rd_sap_site_note_file() -> None:
|
|||
mock_db.return_value.__enter__.return_value = fake_session
|
||||
service.run(make_request(uprn="12345"))
|
||||
|
||||
mock_parse.assert_called_once_with("/tmp/RdSAP_SiteNote_001.pdf")
|
||||
mock_parse.assert_called_once_with(RD_SAP_SITE_NOTE_PATH, uprn=12345)
|
||||
mock_save.assert_called_once_with(
|
||||
fake_session, fake_epc_data, uploaded_file_id=fake_uploaded_file_id
|
||||
fake_session, parsed, uploaded_file_id=fake_uploaded_file_id
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue