Model/backend/magic_plan/xml_parser.py
2026-04-28 15:11:03 +00:00

224 lines
8.5 KiB
Python

import xml.etree.ElementTree as ET
from backend.magic_plan.models import (
MagicPlanDoor,
MagicPlanExploded,
MagicPlanExplodedOpening,
MagicPlanExplodedWall,
MagicPlanFloor,
MagicPlanFurniture,
MagicPlanMainDimension,
MagicPlanPlan,
MagicPlanRoom,
MagicPlanRoomPoint,
MagicPlanSymbolInstance,
MagicPlanWallPoint,
MagicPlanWindow,
)
def _values(el: ET.Element) -> dict[str, str]:
return {v.get("key", ""): (v.text or "") for v in el.findall("values/value")}
def _parse_room_point(el: ET.Element) -> MagicPlanRoomPoint:
return MagicPlanRoomPoint(
snapped_x=float(el.get("snappedX", "0")),
snapped_y=float(el.get("snappedY", "0")),
height=float(el.get("height", "0")),
uid=el.get("uid", ""),
values=_values(el),
)
def _parse_wall_point(el: ET.Element) -> MagicPlanWallPoint:
return MagicPlanWallPoint(
x=float(el.get("x", "0")),
y=float(el.get("y", "0")),
height=float(el.get("height", "0")),
)
def _parse_door(el: ET.Element) -> MagicPlanDoor:
return MagicPlanDoor(
wall_point_index=int(el.get("point", "0")),
type=el.get("type", ""),
u=float(el.get("u", "0")),
width=float(el.get("width", "0")),
depth=float(el.get("depth", "0")),
height=float(el.get("height", "0")),
orientation=int(el.get("orientation", "0")),
snapped_type=el.get("snappedType", ""),
snapped_position=float(el.get("snappedPosition", "0")),
snapped_width=float(el.get("snappedWidth", "0")),
snapped_depth=float(el.get("snappedDepth", "0")),
snapped_height=float(el.get("snappedHeight", "0")),
snapped_orientation=int(el.get("snappedOrientation", "0")),
inset_x=float(el.get("insetX", "0")),
inset_y=float(el.get("insetY", "0")),
inset_z=float(el.get("insetZ", "0")),
symbol_instance=el.get("symbolInstance", ""),
twin_wall_item_uid=el.get("twinWallItemUid"),
)
def _parse_window(el: ET.Element) -> MagicPlanWindow:
return MagicPlanWindow(
wall_point_index=int(el.get("point", "0")),
type=el.get("type", ""),
u=float(el.get("u", "0")),
width=float(el.get("width", "0")),
depth=float(el.get("depth", "0")),
height=float(el.get("height", "0")),
orientation=int(el.get("orientation", "0")),
snapped_type=el.get("snappedType", ""),
snapped_position=float(el.get("snappedPosition", "0")),
snapped_width=float(el.get("snappedWidth", "0")),
snapped_depth=float(el.get("snappedDepth", "0")),
snapped_height=float(el.get("snappedHeight", "0")),
snapped_orientation=int(el.get("snappedOrientation", "0")),
inset_x=float(el.get("insetX", "0")),
inset_y=float(el.get("insetY", "0")),
inset_z=float(el.get("insetZ", "0")),
symbol_instance=el.get("symbolInstance", ""),
)
def _parse_exploded_opening(el: ET.Element) -> MagicPlanExplodedOpening:
return MagicPlanExplodedOpening(
type=el.get("type", ""),
x1=float(el.get("x1", "0")),
y1=float(el.get("y1", "0")),
x2=float(el.get("x2", "0")),
y2=float(el.get("y2", "0")),
width=float(el.get("width", "0")),
depth=float(el.get("depth", "0")),
height=float(el.get("height", "0")),
inset_x=float(el.get("insetX", "0")),
inset_y=float(el.get("insetY", "0")),
orientation=int(el.get("orientation", "0")),
symbol_instance=el.get("symbolInstance", ""),
)
def _parse_furniture(el: ET.Element) -> MagicPlanFurniture:
return MagicPlanFurniture(
type=el.get("type", ""),
x=float(el.get("x", "0")),
y=float(el.get("y", "0")),
snapped_x=float(el.get("snappedX", "0")),
snapped_y=float(el.get("snappedY", "0")),
angle=float(el.get("angle", "0")),
width=float(el.get("width", "0")),
depth=float(el.get("depth", "0")),
height=float(el.get("height", "0")),
snapped_width=float(el.get("snappedWidth", "0")),
snapped_depth=float(el.get("snappedDepth", "0")),
snapped_height=float(el.get("snappedHeight", "0")),
size_lock_0=el.get("sizeLock0", ""),
size_lock_1=el.get("sizeLock1", ""),
size_lock_2=el.get("sizeLock2", ""),
symbol_instance=el.get("symbolInstance", ""),
)
def _parse_main_dimension(el: ET.Element) -> MagicPlanMainDimension:
return MagicPlanMainDimension(
from_point=int(el.get("from", "0")),
to_point=int(el.get("to", "0")),
dir_x=float(el.get("dir.x", "0")),
dir_y=float(el.get("dir.y", "0")),
value=float(el.get("value", "0")),
actual_value=float(el.get("actualValue", "0")),
is_set=el.get("isSet", "0") == "1",
)
def _parse_exploded_wall(el: ET.Element) -> MagicPlanExplodedWall:
type_el = el.find("type")
return MagicPlanExplodedWall(
wall_type=(type_el.text or "") if type_el is not None else "",
points=[_parse_wall_point(p) for p in el.findall("point")],
)
def _parse_exploded(el: ET.Element) -> MagicPlanExploded:
return MagicPlanExploded(
walls=[_parse_exploded_wall(w) for w in el.findall("wall")],
doors=[_parse_exploded_opening(d) for d in el.findall("door")],
windows=[_parse_exploded_opening(w) for w in el.findall("window")],
furniture=[_parse_furniture(f) for f in el.findall("furniture")],
)
def _parse_symbol_instance(el: ET.Element) -> MagicPlanSymbolInstance:
return MagicPlanSymbolInstance(
id=el.get("id", ""),
uid=el.get("uid", ""),
parent_uid=el.get("parentUid", ""),
symbol=el.get("symbol", ""),
values=_values(el),
)
def _parse_floor(el: ET.Element) -> MagicPlanFloor:
si_el = el.find("symbolInstance")
exploded_el = el.find("exploded")
return MagicPlanFloor(
uid=el.get("uid", ""),
name=el.findtext("name") or "",
floor_type=el.get("floorType", "0"),
rotation=float(el.get("rotation", "0")),
compass_angle=float(el.get("compassAngle", "0")),
area_without_walls=float(el.get("areaWithoutWalls", "0")),
area_with_interior_walls_only=float(el.get("areaWithInteriorWallsOnly", "0")),
area_with_walls=float(el.get("areaWithWalls", "0")),
symbol_instance=_parse_symbol_instance(si_el) if si_el is not None
else MagicPlanSymbolInstance(id="", uid="", parent_uid="", symbol="", values={}),
rooms=[_parse_room(r) for r in el.findall("floorRoom")],
furniture=[_parse_furniture(f) for f in el.findall("furniture")],
exploded=_parse_exploded(exploded_el) if exploded_el is not None
else MagicPlanExploded(walls=[], doors=[], windows=[], furniture=[]),
)
def _parse_room(el: ET.Element) -> MagicPlanRoom:
return MagicPlanRoom(
uid=el.get("uid", ""),
type=el.get("type", ""),
x=float(el.get("x", "0")),
y=float(el.get("y", "0")),
rotation=float(el.get("rotation", "0")),
was_modified=el.get("wasModified", "0") == "1",
linked_room_0=el.get("linkedRoom0", "-1"),
linked_room_1=el.get("linkedRoom1", "-1"),
area=float(el.get("area", "0")),
perimeter=float(el.get("perimeter", "0")),
values=_values(el),
points=[_parse_room_point(p) for p in el.findall("point")],
doors=[_parse_door(d) for d in el.findall("door")],
windows=[_parse_window(w) for w in el.findall("window")],
furniture=[_parse_furniture(f) for f in el.findall("furniture")],
main_dimensions=[_parse_main_dimension(d) for d in el.findall("mainDimension")],
)
def parse_magicplan_xml(xml_str: str) -> MagicPlanPlan:
root = ET.fromstring(xml_str)
irp_el = root.find("interiorRoomPoints")
return MagicPlanPlan(
id=root.get("id", ""),
uid=root.get("uid", ""),
name=root.get("name", ""),
type=root.get("type", ""),
interior_wall_width=float(root.get("interiorWallWidth", "0")),
exterior_wall_width=float(root.get("exteriorWallWidth", "0")),
schematic=root.get("schematic", "0") == "1",
has_land_survey_address=root.get("hasLandSurveyAddress", "0") == "1",
last_patch_identifier=root.get("lastPatchIdentifier", ""),
last_roll_identifier=root.get("lastRollIdentifier", ""),
values=_values(root),
floors=[_parse_floor(f) for f in root.findall("floor")],
interior_room_floors=[_parse_floor(f) for f in irp_el.findall("floor")]
if irp_el is not None else [],
)