Model/domain/magicplan/mapper.py
2026-06-05 14:07:28 +00:00

129 lines
3.7 KiB
Python

from typing import Optional
import domain.magicplan.api.response as api
from domain.magicplan.api.response import MagicPlanPlan
from domain.magicplan.models import (
Door,
DoorVentilation,
Floor,
Plan,
Room,
Window,
WindowVentilation,
)
def map_plan(mp: MagicPlanPlan) -> Plan:
return Plan(
uid=mp.plan.id,
name=mp.plan.name,
address=map_address(mp.plan.address),
postcode=mp.plan.address.postal_code if mp.plan.address else None,
floors=[_map_floor(f) for f in mp.plan_detail.plan.floors],
)
def map_address(addr: Optional[api.Address]) -> Optional[str]:
if addr is None:
return None
street = " ".join(p for p in [addr.street_number, addr.street] if p) or None
parts = [p for p in [street, addr.city, addr.country] if p]
return ", ".join(parts) if parts else None
def _map_floor(f: api.Floor) -> Floor:
return Floor(
level=f.level,
name=f.name,
rooms=[_map_room(r) for r in f.rooms],
)
def _map_room(r: api.Room) -> Room:
width, length = _parse_dimensions(r.dimensions)
return Room(
name=r.name,
width_m=width,
length_m=length,
area_m2=round(r.area, 2),
windows=[
_map_window(wi)
for wi in r.wall_items
if wi.symbol.id.startswith("window") or wi.symbol.id == "doorglass"
],
doors=[
_map_door(wi)
for wi in r.wall_items
if wi.symbol.id.startswith("door") and wi.symbol.id != "doorglass"
],
)
def _parse_dimensions(dimensions: Optional[str]) -> tuple[float, float]:
if not dimensions:
return 0.0, 0.0
parts = dimensions.split(" x ")
width = round(float(parts[0].split(" m")[0]), 2)
length = round(float(parts[1].split(" m")[0]), 2)
return width, length
def _map_window(wi: api.WallItem) -> Window:
return Window(
width_m=round(wi.size.x, 2),
height_m=round(wi.size.z, 2),
area_m2=round(wi.size.x * wi.size.z, 2),
opening_type=wi.symbol.id.removeprefix("window"),
ventilation=_map_window_ventilation(wi.custom_displayable_fields),
)
def _map_window_ventilation(
fields: list[api.SurveyField],
) -> Optional[WindowVentilation]:
if not fields:
return None
by_label = {f.label: f for f in fields}
def _str(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)
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(
"Trickle Vent Effective Area (mm2) (No Code Then Width x Height)"
),
num_trickle_vents=_int("No. of Trickle Vents"),
)
def _map_door(wi: api.WallItem) -> Door:
return Door(
width_mm=round(wi.size.x * 1000, 0),
height_mm=round(wi.size.z * 1000, 0),
ventilation=_map_door_ventilation(wi.custom_displayable_fields),
)
def _map_door_ventilation(
fields: list[api.SurveyField],
) -> Optional[DoorVentilation]:
if not fields:
return None
by_label = {f.label: f for f in fields}
f = by_label.get("Door Undercut (mm)")
if f is None or not f.value.has_value:
return None
raw = f.value.value
undercut = float(raw[0] if isinstance(raw, list) else raw)
return DoorVentilation(undercut_mm=undercut)