Logging plus invoke locally bypassing subtask handler

This commit is contained in:
Daniel Roth 2026-06-08 13:31:45 +00:00
parent bd4ad9022c
commit aaeb339254
6 changed files with 86 additions and 20 deletions

View file

@ -1,5 +1,5 @@
import os
from typing import Any
from typing import Any, Optional
import boto3
@ -16,7 +16,7 @@ logger = setup_logger()
@subtask_handler()
def handler(body: dict[str, Any], context: Any) -> str:
def handler(body: dict[str, Any], context: Any) -> Optional[str]:
config = MagicPlanConfig.from_env(os.environ)
payload = MagicPlanTriggerRequest.model_validate(body)
client = MagicPlanClient(
@ -29,11 +29,14 @@ def handler(body: dict[str, Any], context: Any) -> str:
s3_client = S3Client(
boto_s3_client=boto_s3, bucket="retrofit-energy-assessments-dev"
)
# TODO: read s3_bucket from env var so staging/prod use the correct bucket
plan: Plan = MagicPlanOrchestrator(client, s3_client).run(payload)
logger.info("Saved MagicPlan plan uid=%s", plan.uid)
return plan.uid
plan: Optional[Plan] = MagicPlanOrchestrator(client, s3_client).run(payload)
if plan:
logger.info("Saved MagicPlan plan uid=%s", plan.uid)
return plan.uid
return None
if __name__ == "__main__":

View file

@ -4,7 +4,7 @@ services:
ecmk-fetcher-lambda:
build:
context: ../../../
dockerfile: backend/magic_plan/handler/Dockerfile
dockerfile: applications/magic_plan/handler/Dockerfile
ports:
- "9000:8080"
env_file:

View file

@ -12,11 +12,18 @@ payload = {
{
"messageId": "test-message-id",
"body": json.dumps(
{
# "task_id": "00000000-0000-0000-0000-000000000001",
# "sub_task_id": "00000000-0000-0000-0000-000000000002",
"address": "63 Dunkery Road, Wythenshawe, M22 0WR | EPC",
"hubspot_deal_id": "501851906250",
}
# {
# "address": "2 Laburnum Way, Rombley, BR2 8BZ | Retrofit Assessment",
# "hubspot_deal_id": "500262906061",
# "task_id": "00000000-0000-0000-0000-000000000001",
# "sub_task_id": "00000000-0000-0000-0000-000000000002",
# "address": "33 Wallaby Way, Sydney",
# "hubspot_deal_id": "123456789",
# }
{"address": "33 Wallaby Way, Sydney", "hubspot_deal_id": "123456789"}
),
}
]

View file

@ -0,0 +1,48 @@
#!/usr/bin/env python3
"""Run MagicPlanOrchestrator directly, bypassing @subtask_handler.
Loads credentials from the repo-root .env file so no DB task/subtask rows
are needed.
"""
import os
import sys
from pathlib import Path
import boto3
from dotenv import load_dotenv
from utilities.logger import setup_logger
setup_logger()
REPO_ROOT = Path(__file__).resolve().parents[3]
load_dotenv(REPO_ROOT / ".env")
sys.path.insert(0, str(REPO_ROOT))
from infrastructure.magic_plan.config import MagicPlanConfig
from infrastructure.magic_plan.magic_plan_client import MagicPlanClient
from infrastructure.s3.s3_client import S3Client
from orchestration.magic_plan_orchestrator import MagicPlanOrchestrator
from applications.magic_plan.magic_plan_trigger_request import MagicPlanTriggerRequest
ADDRESS = "63 Dunkery Road, Wythenshawe, M22 0WR | EPC"
HUBSPOT_DEAL_ID = "501851906250"
# ADDRESS = "33 Wallaby Way, Sydney"
# HUBSPOT_DEAL_ID = "123456789"
config = MagicPlanConfig.from_env(os.environ)
client = MagicPlanClient(customer_id=config.customer_id, api_key=config.api_key)
boto3_client = boto3.client # type: ignore[attr-defined]
s3_client = S3Client(
boto_s3_client=boto3_client("s3"),
bucket="retrofit-energy-assessments-dev",
)
request = MagicPlanTriggerRequest(address=ADDRESS, hubspot_deal_id=HUBSPOT_DEAL_ID)
print(f"Running MagicPlanOrchestrator for: {ADDRESS!r}")
plan = MagicPlanOrchestrator(client, s3_client).run(request)
print(f"Done")

View file

@ -84,25 +84,25 @@ def _map_window_ventilation(
return None
by_label = {f.label: f for f in fields}
def _str(label: str) -> Optional[str]:
def _try_get_str_value(label: str) -> Optional[str]:
f = by_label.get(label)
if f is None or not f.value.has_value:
return None
v = f.value.value
return v[0] if isinstance(v, list) else v
def _int(label: str) -> Optional[int]:
raw = _str(label)
def _try_get_int_value(label: str) -> Optional[int]:
raw = _try_get_str_value(label)
return int(raw) if raw is not None else None
return WindowVentilation(
opening_type=_str("Opening Type"),
num_openings=_int("Number of Openings (In Same Window)"),
pct_openable=_int("% of Window Openable"),
trickle_vent_area_mm2=_int(
opening_type=_try_get_str_value("Opening Type"),
num_openings=_try_get_int_value("Number of Openings (In Same Window)"),
pct_openable=_try_get_int_value("% of Window Openable"),
trickle_vent_area_mm2=_try_get_int_value(
"Trickle Vent Effective Area (mm2) (No Code Then Width x Height)"
),
num_trickle_vents=_int("No. of Trickle Vents"),
num_trickle_vents=_try_get_int_value("No. of Trickle Vents"),
)

View file

@ -35,7 +35,7 @@ class MagicPlanOrchestrator:
# self._s3_bucket = s3_bucket
self._s3_client = s3_client
def run(self, request: MagicPlanTriggerRequest) -> Plan:
def run(self, request: MagicPlanTriggerRequest) -> Optional[Plan]:
address = request.address
uprn = request.uprn
@ -43,23 +43,30 @@ class MagicPlanOrchestrator:
logger.info("MagicPlanService.run uprn=%s", uprn)
plans: list[PlanSummary] = self._api_client.get_plans()
logger.info("Got list of Plans from MagicPlan")
matched: Optional[PlanSummary] = find_matching_plan(plans, address)
if matched is None:
raise ValueError(f"No MagicPlan found for address: {address!r}")
logger.warning(f"No MagicPlan found for address: {address!r}")
return None
raw_bytes: bytes = self._api_client.get_plan_raw(matched.id)
logger.info(f"Got data for Plan {matched.id} from MagicPlan")
magic_plan: MagicPlanPlan = MagicPlanPlan.model_validate(
json.loads(raw_bytes)["data"]
)
plan: Plan = map_plan(magic_plan)
logger.info("Successfully mapped to Plan object")
uploaded_file: UploadedFile = self._upload_raw_plan_json(
plan_id=matched.id,
raw_bytes=raw_bytes,
uprn=uprn,
hubspot_deal_id=request.hubspot_deal_id,
)
logger.info("Successfully save Plan json")
engine = make_engine(PostgresConfig.from_env(os.environ))
session = make_session(engine)
@ -69,6 +76,7 @@ class MagicPlanOrchestrator:
MagicPlanPostgresRepository(session).save(
plan, cast(int, uploaded_file.id)
) # TODO: refactor to use postgres Unit of Work
logger.info("Successfully saved Plan to database")
return plan