import xml.etree.ElementTree as ET from backend.magic_plan.models import ( MagicPlanXMLDoor, MagicPlanXMLExploded, MagicPlanXMLExplodedOpening, MagicPlanXMLExplodedWall, MagicPlanXMLFloor, MagicPlanXMLFurniture, MagicPlanXMLMainDimension, MagicPlanXMLPlan, MagicPlanXMLRoom, MagicPlanXMLRoomPoint, MagicPlanXMLSymbolInstance, MagicPlanXMLWallPoint, MagicPlanXMLWindow, ) 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) -> MagicPlanXMLRoomPoint: return MagicPlanXMLRoomPoint( 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) -> MagicPlanXMLWallPoint: return MagicPlanXMLWallPoint( x=float(el.get("x", "0")), y=float(el.get("y", "0")), height=float(el.get("height", "0")), ) def _parse_door(el: ET.Element) -> MagicPlanXMLDoor: return MagicPlanXMLDoor( 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) -> MagicPlanXMLWindow: return MagicPlanXMLWindow( 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) -> MagicPlanXMLExplodedOpening: return MagicPlanXMLExplodedOpening( 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) -> MagicPlanXMLFurniture: return MagicPlanXMLFurniture( 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) -> MagicPlanXMLMainDimension: return MagicPlanXMLMainDimension( 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) -> MagicPlanXMLExplodedWall: type_el = el.find("type") return MagicPlanXMLExplodedWall( 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) -> MagicPlanXMLExploded: return MagicPlanXMLExploded( 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) -> MagicPlanXMLSymbolInstance: return MagicPlanXMLSymbolInstance( 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) -> MagicPlanXMLFloor: si_el = el.find("symbolInstance") exploded_el = el.find("exploded") return MagicPlanXMLFloor( 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 MagicPlanXMLSymbolInstance(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 MagicPlanXMLExploded(walls=[], doors=[], windows=[], furniture=[]), ) def _parse_room(el: ET.Element) -> MagicPlanXMLRoom: return MagicPlanXMLRoom( 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) -> MagicPlanXMLPlan: root = ET.fromstring(xml_str) irp_el = root.find("interiorRoomPoints") return MagicPlanXMLPlan( 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 [], )