start mapping json

This commit is contained in:
Daniel Roth 2026-05-01 16:20:55 +00:00
parent 9e7d8c004f
commit ff67297646
7 changed files with 137258 additions and 231 deletions

View file

@ -1,14 +1,51 @@
from backend.magic_plan.models import MagicPlanDetail, MagicPlanSummary
import requests
from backend.magic_plan.models import (
MagicPlanPlan,
MagicPlanSummary,
MagicPlanXMLDetail,
MagicPlanXMLSummary,
)
from backend.magic_plan.xml_parser import parse_magicplan_xml
def _parse_xml_summary(data: dict[str, object]) -> MagicPlanXMLSummary:
return MagicPlanXMLSummary(
id=str(data.get("id", "")),
project_id=str(data.get("project_id", "")),
name=str(data.get("name", "")),
address=str(data.get("address", "")),
creation_date=str(data.get("creation_date", "")),
update_date=str(data.get("update_date", "")),
workgroup_id=str(data.get("workgroup_id", "")),
team_id=str(data.get("team_id", "")),
created_by=str(data.get("created_by", "")),
thumbnail_url=str(data.get("thumbnail_url", "")),
public_url=str(data.get("public_url", "")),
cloud_url=str(data.get("cloud_url", "")),
url_3d=str(data.get("3d_url", "")),
)
class MagicPlanClient:
BASE_URL = "https://cloud.magicplan.app/api/v2"
def __init__(self, _api_key: str) -> None:
raise NotImplementedError
def __init__(self, api_key: str) -> None:
self._session = requests.Session()
self._session.headers.update({"Authorization": f"Bearer {api_key}"})
def get_plans(self, _project_id: str) -> list[MagicPlanSummary]:
raise NotImplementedError
def get_plan_xml(self, _plan_id: str) -> MagicPlanDetail:
def get_plan_xml(self, plan_id: str) -> MagicPlanXMLDetail:
resp = self._session.get(f"{self.BASE_URL}/plans/get/{plan_id}")
resp.raise_for_status()
data: dict[str, object] = resp.json()["data"]
plan_data = data["plan"] # type: ignore[index]
detail_data = data["plan_detail"] # type: ignore[index]
summary = _parse_xml_summary(plan_data) # type: ignore[arg-type]
plan_xml = parse_magicplan_xml(detail_data["magicplan_format_xml"]) # type: ignore[index]
return MagicPlanXMLDetail(summary=summary, plan_xml=plan_xml)
def get_plan(self, _plan_id: str) -> MagicPlanPlan:
raise NotImplementedError

File diff suppressed because one or more lines are too long

View file

@ -1,18 +1,40 @@
import re
from dataclasses import dataclass
from typing import Optional
from typing import Any, Optional
def _camel_to_snake(name: str) -> str:
return re.sub(r"(?<!^)(?=[A-Z])", "_", name).lower()
def parse_displayable_fields(fields: list[dict[str, Any]]) -> dict[str, str]:
result: dict[str, str] = {}
for f in fields:
if f.get("type_as_string") == "sectionTitle":
continue
v: dict[str, Any] = f.get("value") or {}
if not v.get("has_value"):
continue
result[_camel_to_snake(str(f["id"]))] = str(v["value"])
return result
# ---------------------------------------------------------------------------
# XML dataclasses (sourced from MagicPlan Exchange XML format)
# ---------------------------------------------------------------------------
@dataclass
class MagicPlanRoomPoint:
class MagicPlanXMLRoomPoint:
snapped_x: float
snapped_y: float
height: float
uid: str
values: dict[str, str] # e.g. loadBearingWall, addElevationToReport
values: dict[str, str]
@dataclass
class MagicPlanWallPoint:
class MagicPlanXMLWallPoint:
"""Point in <exploded><wall> — absolute coords, no uid or values."""
x: float
@ -21,21 +43,21 @@ class MagicPlanWallPoint:
@dataclass
class MagicPlanDoor:
class MagicPlanXMLDoor:
"""Door in <floorRoom> context — wall-relative position."""
wall_point_index: int
type: str
u: float
width: float # m
depth: float # m
height: float # m
width: float
depth: float
height: float
orientation: int
snapped_type: str
snapped_position: float
snapped_width: float # m
snapped_depth: float # m
snapped_height: float # m
snapped_width: float
snapped_depth: float
snapped_height: float
snapped_orientation: int
inset_x: float
inset_y: float
@ -45,21 +67,21 @@ class MagicPlanDoor:
@dataclass
class MagicPlanWindow:
class MagicPlanXMLWindow:
"""Window in <floorRoom> context — wall-relative position."""
wall_point_index: int
type: str
u: float
width: float # m
depth: float # m
height: float # m
width: float
depth: float
height: float
orientation: int
snapped_type: str
snapped_position: float
snapped_width: float # m
snapped_depth: float # m
snapped_height: float # m
snapped_width: float
snapped_depth: float
snapped_height: float
snapped_orientation: int
inset_x: float
inset_y: float
@ -68,7 +90,7 @@ class MagicPlanWindow:
@dataclass
class MagicPlanExplodedOpening:
class MagicPlanXMLExplodedOpening:
"""Door or window in <exploded> context — absolute coords, no snapped* fields."""
type: str
@ -76,9 +98,9 @@ class MagicPlanExplodedOpening:
y1: float
x2: float
y2: float
width: float # m
depth: float # m
height: float # m
width: float
depth: float
height: float
inset_x: float
inset_y: float
orientation: int
@ -86,19 +108,19 @@ class MagicPlanExplodedOpening:
@dataclass
class MagicPlanFurniture:
class MagicPlanXMLFurniture:
type: str
x: float
y: float
snapped_x: float
snapped_y: float
angle: float
width: float # m
depth: float # m
height: float # m
snapped_width: float # m
snapped_depth: float # m
snapped_height: float # m
width: float
depth: float
height: float
snapped_width: float
snapped_depth: float
snapped_height: float
size_lock_0: str
size_lock_1: str
size_lock_2: str
@ -106,7 +128,7 @@ class MagicPlanFurniture:
@dataclass
class MagicPlanMainDimension:
class MagicPlanXMLMainDimension:
from_point: int
to_point: int
dir_x: float
@ -117,30 +139,30 @@ class MagicPlanMainDimension:
@dataclass
class MagicPlanExplodedWall:
wall_type: str # "exterior" | "interior"
points: list[MagicPlanWallPoint]
class MagicPlanXMLExplodedWall:
wall_type: str
points: list[MagicPlanXMLWallPoint]
@dataclass
class MagicPlanExploded:
walls: list[MagicPlanExplodedWall]
doors: list[MagicPlanExplodedOpening]
windows: list[MagicPlanExplodedOpening]
furniture: list[MagicPlanFurniture]
class MagicPlanXMLExploded:
walls: list[MagicPlanXMLExplodedWall]
doors: list[MagicPlanXMLExplodedOpening]
windows: list[MagicPlanXMLExplodedOpening]
furniture: list[MagicPlanXMLFurniture]
@dataclass
class MagicPlanSymbolInstance:
class MagicPlanXMLSymbolInstance:
id: str
uid: str
parent_uid: str
symbol: str
values: dict[str, str] # ceilingHeight, width, height, depth, distanceUnit, etc.
values: dict[str, str]
@dataclass
class MagicPlanRoom:
class MagicPlanXMLRoom:
uid: str
type: str
x: float
@ -149,35 +171,35 @@ class MagicPlanRoom:
was_modified: bool
linked_room_0: str
linked_room_1: str
area: float # m²
perimeter: float # m
values: dict[str, str] # ceilingHeight, label, etc.
points: list[MagicPlanRoomPoint]
doors: list[MagicPlanDoor]
windows: list[MagicPlanWindow]
furniture: list[MagicPlanFurniture]
main_dimensions: list[MagicPlanMainDimension]
area: float
perimeter: float
values: dict[str, str]
points: list[MagicPlanXMLRoomPoint]
doors: list[MagicPlanXMLDoor]
windows: list[MagicPlanXMLWindow]
furniture: list[MagicPlanXMLFurniture]
main_dimensions: list[MagicPlanXMLMainDimension]
@dataclass
class MagicPlanFloor:
class MagicPlanXMLFloor:
uid: str
name: str
floor_type: str # "0"=ground, "1"=upper, "2"=basement
floor_type: str
rotation: float
compass_angle: float
area_without_walls: float # m²
area_with_interior_walls_only: float # m²
area_with_walls: float # m²
symbol_instance: MagicPlanSymbolInstance
rooms: list[MagicPlanRoom]
furniture: list[MagicPlanFurniture] # floor-level furniture (not inside any room)
exploded: MagicPlanExploded
area_without_walls: float
area_with_interior_walls_only: float
area_with_walls: float
symbol_instance: MagicPlanXMLSymbolInstance
rooms: list[MagicPlanXMLRoom]
furniture: list[MagicPlanXMLFurniture]
exploded: MagicPlanXMLExploded
@dataclass
class MagicPlanSummary:
"""Plan metadata returned by the list-plans API endpoint."""
class MagicPlanXMLSummary:
"""Plan metadata returned by the list-plans API endpoint (old string-address format)."""
id: str
project_id: str
@ -195,25 +217,201 @@ class MagicPlanSummary:
@dataclass
class MagicPlanDetail:
class MagicPlanXMLDetail:
"""Full plan response: summary metadata + parsed XML plan."""
summary: MagicPlanSummary
plan_xml: "MagicPlanPlan"
summary: MagicPlanXMLSummary
plan_xml: "MagicPlanXMLPlan"
@dataclass
class MagicPlanPlan:
class MagicPlanXMLPlan:
id: str
uid: str
name: str
type: str
interior_wall_width: float # m
exterior_wall_width: float # m
interior_wall_width: float
exterior_wall_width: float
schematic: bool
has_land_survey_address: bool
last_patch_identifier: str
last_roll_identifier: str
values: dict[str, str] # date, statistics.*, distanceUnit, etc.
values: dict[str, str]
floors: list[MagicPlanXMLFloor]
interior_room_floors: list[MagicPlanXMLFloor]
# ---------------------------------------------------------------------------
# JSON dataclasses (sourced from GET Plan API JSON response)
# ---------------------------------------------------------------------------
@dataclass
class MagicPlanWall:
uid: str
symbol_id: str
length: float
fields: dict[str, str]
custom_fields: dict[str, str]
@dataclass
class MagicPlanWallItem:
"""Door or window — distinguished by symbol_id."""
uid: str
symbol_id: str
symbol_name: str
width: float
depth: float
height: float
pos_x: float
pos_y: float
pos_z: float
rotation_z: float
fields: dict[str, str]
custom_fields: dict[str, str]
@dataclass
class MagicPlanFurniture:
uid: str
symbol_id: str
symbol_name: str
width: float
depth: float
height: float
pos_x: float
pos_y: float
pos_z: float
rotation_z: float
fields: dict[str, str]
custom_fields: dict[str, str]
@dataclass
class MagicPlanRoom:
uid: str
name: str
area: float
perimeter: float
height: float
width: float
volume: float
dimensions: str
door_count: int
window_count: int
walls_surface: float
walls_surface_without_openings: float
doors_surface: float
windows_surface: float
wall_items: list[MagicPlanWallItem]
furnitures: list[MagicPlanFurniture]
walls: list[MagicPlanWall]
fields: dict[str, str]
custom_fields: dict[str, str]
@dataclass
class MagicPlanFloor:
uid: str
name: str
floor_type: str
area: float
perimeter: float
area_without_walls: float
area_with_interior_walls_only: float
area_with_walls: float
wall_count: int
wall_count_with_interior_walls: int
door_count: int
window_count: int
room_count: int
furniture_count: int
doors_surface: float
walls_surface: float
walls_surface_without_openings: float
windows_surface: float
volume: float
rooms: list[MagicPlanRoom]
furnitures: list[MagicPlanFurniture]
symbol_instances: list[MagicPlanFurniture]
fields: dict[str, str]
custom_fields: dict[str, str]
@dataclass
class MagicPlanPlan:
"""GET /plans/{id} — merged data.plan + data.plan_detail.plan."""
id: str
project_id: str
uid: str
name: str
area: float
perimeter: float
area_without_walls: float
area_with_interior_walls_only: float
area_with_walls: float
wall_count: int
door_count: int
window_count: int
room_count: int
furniture_count: int
floor_count: int
doors_surface: float
walls_surface: float
walls_surface_without_openings: float
windows_surface: float
volume: float
living_area: float
below_grade_living_area: float
above_grade_living_area: float
address_street: Optional[str]
address_postal_code: Optional[str]
address_city: Optional[str]
address_country: Optional[str]
address_longitude: Optional[float]
address_latitude: Optional[float]
creation_date: str
update_date: str
workgroup_id: str
team_id: str
created_by_id: str
created_by_firstname: Optional[str]
created_by_lastname: Optional[str]
created_by_email: str
thumbnail_url: str
public_url: str
cloud_url: str
url_3d: str
floors: list[MagicPlanFloor]
interior_room_floors: list[MagicPlanFloor] # from <interiorRoomPoints>
fields: dict[str, str]
custom_fields: dict[str, str]
@dataclass
class MagicPlanSummary:
"""GET /plans list — lightweight, flat address and creator fields."""
id: str
project_id: str
name: str
address_street: Optional[str]
address_postal_code: Optional[str]
address_city: Optional[str]
address_country: Optional[str]
address_longitude: Optional[float]
address_latitude: Optional[float]
creation_date: str
update_date: str
workgroup_id: str
team_id: str
created_by_id: str
created_by_firstname: Optional[str]
created_by_lastname: Optional[str]
created_by_email: str
thumbnail_url: str
public_url: str
cloud_url: str
url_3d: str

View file

@ -7,7 +7,7 @@ import pytest
import requests
from backend.magic_plan.magic_plan_client import MagicPlanClient
from backend.magic_plan.models import MagicPlanDetail, MagicPlanPlan, MagicPlanSummary
from backend.magic_plan.models import MagicPlanXMLDetail, MagicPlanXMLPlan, MagicPlanXMLSummary
FIXTURES = Path(__file__).parent.parent
API_KEY = "test-api-key"
@ -107,12 +107,12 @@ class TestGetPlanXml:
def test_returns_magic_plan_detail(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
mock_session.get.return_value.json.return_value = _get_plan_response(_load_xml())
result = client.get_plan_xml(PLAN_ID)
assert isinstance(result, MagicPlanDetail)
assert isinstance(result, MagicPlanXMLDetail)
def test_detail_contains_summary(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
mock_session.get.return_value.json.return_value = _get_plan_response(_load_xml())
result = client.get_plan_xml(PLAN_ID)
assert isinstance(result.summary, MagicPlanSummary)
assert isinstance(result.summary, MagicPlanXMLSummary)
def test_detail_summary_id(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
mock_session.get.return_value.json.return_value = _get_plan_response(_load_xml())
@ -127,7 +127,7 @@ class TestGetPlanXml:
def test_detail_contains_parsed_plan(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
mock_session.get.return_value.json.return_value = _get_plan_response(_load_xml())
result = client.get_plan_xml(PLAN_ID)
assert isinstance(result.plan_xml, MagicPlanPlan)
assert isinstance(result.plan_xml, MagicPlanXMLPlan)
def test_plan_xml_id_matches_xml(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
mock_session.get.return_value.json.return_value = _get_plan_response(_load_xml())
@ -167,7 +167,7 @@ class TestGetPlans:
mock_session.get.return_value.json.return_value = _get_plans_response([PLAN_ID, "other-id"])
result = client.get_plans(PROJECT_ID)
assert len(result) == 2
assert all(isinstance(s, MagicPlanSummary) for s in result)
assert all(isinstance(s, MagicPlanXMLSummary) for s in result)
def test_summary_id(self, client: MagicPlanClient, mock_session: MagicMock) -> None:
mock_session.get.return_value.json.return_value = _get_plans_response([PLAN_ID])

View file

@ -0,0 +1,50 @@
import pytest
from backend.magic_plan.models import parse_displayable_fields
SECTION_TITLE_FIELD = {
"id": "measures.section.title",
"type_as_string": "sectionTitle",
"value": {"has_value": False, "value": ""},
}
CEILING_HEIGHT_FIELD = {
"id": "ceilingHeight",
"type_as_string": "distance",
"value": {"has_value": True, "value": "2.45"},
}
NO_VALUE_FIELD = {
"id": "label",
"type_as_string": "text",
"value": {"has_value": False, "value": ""},
}
class TestParseDisplayableFields:
def test_excludes_section_titles(self) -> None:
result = parse_displayable_fields([SECTION_TITLE_FIELD])
assert result == {}
def test_excludes_fields_without_value(self) -> None:
result = parse_displayable_fields([NO_VALUE_FIELD])
assert result == {}
def test_includes_field_with_value(self) -> None:
result = parse_displayable_fields([CEILING_HEIGHT_FIELD])
assert "ceiling_height" in result
assert result["ceiling_height"] == "2.45"
def test_snake_cases_camel_key(self) -> None:
result = parse_displayable_fields([CEILING_HEIGHT_FIELD])
assert "ceiling_height" in result
assert "ceilingHeight" not in result
def test_mixed_fields(self) -> None:
result = parse_displayable_fields([SECTION_TITLE_FIELD, CEILING_HEIGHT_FIELD, NO_VALUE_FIELD])
assert result == {"ceiling_height": "2.45"}
def test_empty_list(self) -> None:
assert parse_displayable_fields([]) == {}

View file

@ -5,7 +5,7 @@ from pathlib import Path
import pytest
from backend.magic_plan.models import MagicPlanPlan
from backend.magic_plan.models import MagicPlanXMLPlan
from backend.magic_plan.xml_parser import parse_magicplan_xml
FIXTURES = Path(__file__).parent.parent
@ -21,17 +21,17 @@ def _load(filename: str) -> str:
@pytest.fixture(scope="module")
def plan1() -> MagicPlanPlan:
def plan1() -> MagicPlanXMLPlan:
return parse_magicplan_xml(_load("xml_example.xml"))
@pytest.fixture(scope="module")
def plan2() -> MagicPlanPlan:
def plan2() -> MagicPlanXMLPlan:
return parse_magicplan_xml(_load("xml_example_2.xml"))
@pytest.fixture(scope="module")
def plan3() -> MagicPlanPlan:
def plan3() -> MagicPlanXMLPlan:
return parse_magicplan_xml(_load("xml_example_3.xml"))
@ -41,55 +41,55 @@ def plan3() -> MagicPlanPlan:
class TestPlanAttributes:
def test_id(self, plan1: MagicPlanPlan) -> None:
def test_id(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.id == "b66bb427-33fe-4865-8d57-bad7d9d3f2e5"
def test_uid(self, plan1: MagicPlanPlan) -> None:
def test_uid(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.uid == "69e5fafc.0890b3ff"
def test_name(self, plan1: MagicPlanPlan) -> None:
def test_name(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.name == "275 Carr Hill Rd NE9 5ND"
def test_type(self, plan1: MagicPlanPlan) -> None:
def test_type(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.type == "0"
def test_interior_wall_width(self, plan1: MagicPlanPlan) -> None:
def test_interior_wall_width(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.interior_wall_width, 0.12)
def test_exterior_wall_width(self, plan1: MagicPlanPlan) -> None:
def test_exterior_wall_width(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.exterior_wall_width, 0.25)
def test_schematic_false(self, plan1: MagicPlanPlan) -> None:
def test_schematic_false(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.schematic is False
def test_has_land_survey_address_false(self, plan1: MagicPlanPlan) -> None:
def test_has_land_survey_address_false(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.has_land_survey_address is False
def test_last_patch_identifier(self, plan1: MagicPlanPlan) -> None:
def test_last_patch_identifier(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.last_patch_identifier == "0"
def test_last_roll_identifier(self, plan1: MagicPlanPlan) -> None:
def test_last_roll_identifier(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.last_roll_identifier == "0"
class TestPlanValues:
def test_date(self, plan1: MagicPlanPlan) -> None:
def test_date(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.values["date"] == "2026-04-20"
def test_statistics_area_of_height(self, plan1: MagicPlanPlan) -> None:
def test_statistics_area_of_height(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.values["statistics.areaOfHeight"] == "2.134"
def test_statistics_basement_account(self, plan1: MagicPlanPlan) -> None:
def test_statistics_basement_account(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.values["statistics.basement.account"] == "100"
def test_statistics_exterior_walls(self, plan1: MagicPlanPlan) -> None:
def test_statistics_exterior_walls(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.values["statistics.exteriorWalls"] == "0"
def test_statistics_interior_walls(self, plan1: MagicPlanPlan) -> None:
def test_statistics_interior_walls(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.values["statistics.interiorWalls"] == "0"
def test_plan2_date(self, plan2: MagicPlanPlan) -> None:
def test_plan2_date(self, plan2: MagicPlanXMLPlan) -> None:
assert plan2.values["date"] == "2026-04-16"
@ -99,43 +99,43 @@ class TestPlanValues:
class TestFloors:
def test_floor_count_plan1(self, plan1: MagicPlanPlan) -> None:
def test_floor_count_plan1(self, plan1: MagicPlanXMLPlan) -> None:
assert len(plan1.floors) == 2
def test_floor_count_plan2(self, plan2: MagicPlanPlan) -> None:
def test_floor_count_plan2(self, plan2: MagicPlanXMLPlan) -> None:
assert len(plan2.floors) == 1
def test_floor_count_plan3(self, plan3: MagicPlanPlan) -> None:
def test_floor_count_plan3(self, plan3: MagicPlanXMLPlan) -> None:
assert len(plan3.floors) == 1
def test_ground_floor_name(self, plan1: MagicPlanPlan) -> None:
def test_ground_floor_name(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].name == "Ground Floor"
def test_first_floor_name(self, plan1: MagicPlanPlan) -> None:
def test_first_floor_name(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[1].name == "1st Floor"
def test_ground_floor_type(self, plan1: MagicPlanPlan) -> None:
def test_ground_floor_type(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].floor_type == "0"
def test_upper_floor_type(self, plan1: MagicPlanPlan) -> None:
def test_upper_floor_type(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[1].floor_type == "1"
def test_floor_uid(self, plan1: MagicPlanPlan) -> None:
def test_floor_uid(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].uid == "69e5fb20.4feef7ff"
def test_ground_floor_area_without_walls(self, plan1: MagicPlanPlan) -> None:
def test_ground_floor_area_without_walls(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].area_without_walls, 40.20736)
def test_ground_floor_area_with_walls(self, plan1: MagicPlanPlan) -> None:
def test_ground_floor_area_with_walls(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].area_with_walls, 48.40593)
def test_ground_floor_area_with_interior_walls_only(self, plan1: MagicPlanPlan) -> None:
def test_ground_floor_area_with_interior_walls_only(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].area_with_interior_walls_only, 40.67878)
def test_floor_rotation(self, plan1: MagicPlanPlan) -> None:
def test_floor_rotation(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rotation, 0.0, abs_tol=1e-9)
def test_floor_compass_angle(self, plan1: MagicPlanPlan) -> None:
def test_floor_compass_angle(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].compass_angle, -1.0)
@ -145,19 +145,19 @@ class TestFloors:
class TestSymbolInstance:
def test_symbol_instance_id(self, plan1: MagicPlanPlan) -> None:
def test_symbol_instance_id(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].symbol_instance.id == "floor"
def test_symbol_instance_uid(self, plan1: MagicPlanPlan) -> None:
def test_symbol_instance_uid(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].symbol_instance.uid == "69e5fb20.4feef7ff"
def test_symbol_instance_symbol(self, plan1: MagicPlanPlan) -> None:
def test_symbol_instance_symbol(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].symbol_instance.symbol == "floor"
def test_symbol_instance_parent_uid(self, plan1: MagicPlanPlan) -> None:
def test_symbol_instance_parent_uid(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].symbol_instance.parent_uid == ""
def test_symbol_instance_ceiling_height_value(self, plan1: MagicPlanPlan) -> None:
def test_symbol_instance_ceiling_height_value(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].symbol_instance.values["ceilingHeight"] == "2.323164"
@ -167,59 +167,59 @@ class TestSymbolInstance:
class TestRooms:
def test_ground_floor_room_count(self, plan1: MagicPlanPlan) -> None:
def test_ground_floor_room_count(self, plan1: MagicPlanXMLPlan) -> None:
assert len(plan1.floors[0].rooms) == 2
def test_first_floor_room_count(self, plan1: MagicPlanPlan) -> None:
def test_first_floor_room_count(self, plan1: MagicPlanXMLPlan) -> None:
assert len(plan1.floors[1].rooms) == 4
def test_plan3_room_count(self, plan3: MagicPlanPlan) -> None:
def test_plan3_room_count(self, plan3: MagicPlanXMLPlan) -> None:
assert len(plan3.floors[0].rooms) == 9
def test_room_type(self, plan1: MagicPlanPlan) -> None:
def test_room_type(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].type == "Kitchen"
def test_room_uid(self, plan1: MagicPlanPlan) -> None:
def test_room_uid(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].uid == "69e5fbc8.71027bff"
def test_room_area(self, plan1: MagicPlanPlan) -> None:
def test_room_area(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rooms[0].area, 10.78332)
def test_room_perimeter(self, plan1: MagicPlanPlan) -> None:
def test_room_perimeter(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rooms[0].perimeter, 13.32812)
def test_room_x(self, plan1: MagicPlanPlan) -> None:
def test_room_x(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rooms[0].x, 3.80616)
def test_room_y(self, plan1: MagicPlanPlan) -> None:
def test_room_y(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rooms[0].y, 0.23162)
def test_room_rotation(self, plan1: MagicPlanPlan) -> None:
def test_room_rotation(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rooms[0].rotation, 0.0, abs_tol=1e-9)
def test_room_was_modified_false(self, plan1: MagicPlanPlan) -> None:
def test_room_was_modified_false(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].was_modified is False
def test_room_was_modified_true(self, plan1: MagicPlanPlan) -> None:
def test_room_was_modified_true(self, plan1: MagicPlanXMLPlan) -> None:
closet = plan1.floors[1].rooms[1]
assert closet.type == "Closet"
assert closet.was_modified is True
def test_room_linked_room_0(self, plan1: MagicPlanPlan) -> None:
def test_room_linked_room_0(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].linked_room_0 == "-1"
def test_room_linked_room_1(self, plan1: MagicPlanPlan) -> None:
def test_room_linked_room_1(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].linked_room_1 == "-1"
def test_room_ceiling_height_value(self, plan1: MagicPlanPlan) -> None:
def test_room_ceiling_height_value(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].values["ceilingHeight"] == "2.323164"
def test_room_label_value(self, plan2: MagicPlanPlan) -> None:
def test_room_label_value(self, plan2: MagicPlanXMLPlan) -> None:
dining = plan2.floors[0].rooms[1]
assert dining.type == "Dining Room"
assert dining.values["label"] == "Room"
def test_room_no_label_when_absent(self, plan1: MagicPlanPlan) -> None:
def test_room_no_label_when_absent(self, plan1: MagicPlanXMLPlan) -> None:
assert "label" not in plan1.floors[0].rooms[0].values
@ -229,25 +229,25 @@ class TestRooms:
class TestRoomPoints:
def test_kitchen_point_count(self, plan1: MagicPlanPlan) -> None:
def test_kitchen_point_count(self, plan1: MagicPlanXMLPlan) -> None:
assert len(plan1.floors[0].rooms[0].points) == 4
def test_living_room_point_count(self, plan1: MagicPlanPlan) -> None:
def test_living_room_point_count(self, plan1: MagicPlanXMLPlan) -> None:
assert len(plan1.floors[0].rooms[1].points) == 8
def test_point_snapped_x(self, plan1: MagicPlanPlan) -> None:
def test_point_snapped_x(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rooms[0].points[0].snapped_x, -1.44357)
def test_point_snapped_y(self, plan1: MagicPlanPlan) -> None:
def test_point_snapped_y(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rooms[0].points[0].snapped_y, -2.00846)
def test_point_height(self, plan1: MagicPlanPlan) -> None:
def test_point_height(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rooms[0].points[0].height, 2.323164)
def test_point_uid(self, plan1: MagicPlanPlan) -> None:
def test_point_uid(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].points[0].uid == "69e5fbc8.710363ff"
def test_point_values_empty_when_absent(self, plan1: MagicPlanPlan) -> None:
def test_point_values_empty_when_absent(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].points[0].values == {}
@ -257,40 +257,40 @@ class TestRoomPoints:
class TestDoors:
def test_kitchen_door_count(self, plan1: MagicPlanPlan) -> None:
def test_kitchen_door_count(self, plan1: MagicPlanXMLPlan) -> None:
assert len(plan1.floors[0].rooms[0].doors) == 5
def test_corridor_door_count(self, plan3: MagicPlanPlan) -> None:
def test_corridor_door_count(self, plan3: MagicPlanXMLPlan) -> None:
assert len(plan3.floors[0].rooms[0].doors) == 9
def test_door_wall_point_index(self, plan1: MagicPlanPlan) -> None:
def test_door_wall_point_index(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].doors[0].wall_point_index == 3
def test_door_type(self, plan1: MagicPlanPlan) -> None:
def test_door_type(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].doors[0].type == "4"
def test_door_u(self, plan1: MagicPlanPlan) -> None:
def test_door_u(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rooms[0].doors[0].u, 0.47585)
def test_door_snapped_width(self, plan1: MagicPlanPlan) -> None:
def test_door_snapped_width(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rooms[0].doors[0].snapped_width, 1.82394)
def test_door_snapped_height(self, plan1: MagicPlanPlan) -> None:
def test_door_snapped_height(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rooms[0].doors[0].snapped_height, 2.08411)
def test_door_snapped_orientation(self, plan1: MagicPlanPlan) -> None:
def test_door_snapped_orientation(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].doors[0].snapped_orientation == 3
def test_door_twin_wall_item_uid_present(self, plan1: MagicPlanPlan) -> None:
def test_door_twin_wall_item_uid_present(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].doors[0].twin_wall_item_uid == "69e5fbc8.74614fff"
def test_door_twin_wall_item_uid_absent(self, plan2: MagicPlanPlan) -> None:
def test_door_twin_wall_item_uid_absent(self, plan2: MagicPlanXMLPlan) -> None:
assert plan2.floors[0].rooms[0].doors[0].twin_wall_item_uid is None
def test_door_symbol_instance(self, plan1: MagicPlanPlan) -> None:
def test_door_symbol_instance(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].doors[0].symbol_instance == "W-0-0"
def test_door_inset_x(self, plan1: MagicPlanPlan) -> None:
def test_door_inset_x(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rooms[0].doors[0].inset_x, 0.0, abs_tol=1e-9)
@ -300,30 +300,30 @@ class TestDoors:
class TestWindows:
def test_kitchen_window_count(self, plan1: MagicPlanPlan) -> None:
def test_kitchen_window_count(self, plan1: MagicPlanXMLPlan) -> None:
assert len(plan1.floors[0].rooms[0].windows) == 1
def test_corridor_window_count_zero(self, plan3: MagicPlanPlan) -> None:
def test_corridor_window_count_zero(self, plan3: MagicPlanXMLPlan) -> None:
corridor = plan3.floors[0].rooms[0]
assert corridor.type == "Corridor"
assert len(corridor.windows) == 0
def test_window_wall_point_index(self, plan1: MagicPlanPlan) -> None:
def test_window_wall_point_index(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].windows[0].wall_point_index == 1
def test_window_type(self, plan1: MagicPlanPlan) -> None:
def test_window_type(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].windows[0].type == "1"
def test_window_u(self, plan1: MagicPlanPlan) -> None:
def test_window_u(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rooms[0].windows[0].u, 0.68463)
def test_window_snapped_width(self, plan1: MagicPlanPlan) -> None:
def test_window_snapped_width(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rooms[0].windows[0].snapped_width, 1.71972)
def test_window_snapped_height(self, plan1: MagicPlanPlan) -> None:
def test_window_snapped_height(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].rooms[0].windows[0].snapped_height, 0.94940)
def test_window_symbol_instance(self, plan1: MagicPlanPlan) -> None:
def test_window_symbol_instance(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].rooms[0].windows[0].symbol_instance == "W-0-2"
@ -333,18 +333,18 @@ class TestWindows:
class TestRoomFurniture:
def test_kitchen_furniture_count(self, plan1: MagicPlanPlan) -> None:
def test_kitchen_furniture_count(self, plan1: MagicPlanXMLPlan) -> None:
assert len(plan1.floors[0].rooms[0].furniture) == 5
def test_bathroom_furniture_count_zero(self, plan1: MagicPlanPlan) -> None:
def test_bathroom_furniture_count_zero(self, plan1: MagicPlanXMLPlan) -> None:
bathroom = plan1.floors[1].rooms[0]
assert bathroom.type == "Bathroom"
assert len(bathroom.furniture) == 0
def test_furniture_type(self, plan1: MagicPlanPlan) -> None:
def test_furniture_type(self, plan1: MagicPlanXMLPlan) -> None:
assert isinstance(plan1.floors[0].rooms[0].furniture[0].type, str)
def test_furniture_symbol_instance(self, plan1: MagicPlanPlan) -> None:
def test_furniture_symbol_instance(self, plan1: MagicPlanXMLPlan) -> None:
assert isinstance(plan1.floors[0].rooms[0].furniture[0].symbol_instance, str)
@ -354,16 +354,16 @@ class TestRoomFurniture:
class TestFloorLevelFurniture:
def test_floor_furniture_count(self, plan3: MagicPlanPlan) -> None:
def test_floor_furniture_count(self, plan3: MagicPlanXMLPlan) -> None:
assert len(plan3.floors[0].furniture) == 1
def test_floor_furniture_x(self, plan3: MagicPlanPlan) -> None:
def test_floor_furniture_x(self, plan3: MagicPlanXMLPlan) -> None:
assert math.isclose(plan3.floors[0].furniture[0].x, -2.09376)
def test_floor_furniture_y(self, plan3: MagicPlanPlan) -> None:
def test_floor_furniture_y(self, plan3: MagicPlanXMLPlan) -> None:
assert math.isclose(plan3.floors[0].furniture[0].y, 3.13664)
def test_floor_furniture_absent_plan1(self, plan1: MagicPlanPlan) -> None:
def test_floor_furniture_absent_plan1(self, plan1: MagicPlanXMLPlan) -> None:
assert len(plan1.floors[0].furniture) == 0
@ -373,13 +373,13 @@ class TestFloorLevelFurniture:
class TestMainDimensions:
def test_main_dimension_count(self, plan1: MagicPlanPlan) -> None:
def test_main_dimension_count(self, plan1: MagicPlanXMLPlan) -> None:
assert len(plan1.floors[0].rooms[0].main_dimensions) == 2
def test_main_dimension_from_point(self, plan1: MagicPlanPlan) -> None:
def test_main_dimension_from_point(self, plan1: MagicPlanXMLPlan) -> None:
assert isinstance(plan1.floors[0].rooms[0].main_dimensions[0].from_point, int)
def test_main_dimension_is_set(self, plan1: MagicPlanPlan) -> None:
def test_main_dimension_is_set(self, plan1: MagicPlanXMLPlan) -> None:
assert isinstance(plan1.floors[0].rooms[0].main_dimensions[0].is_set, bool)
@ -389,59 +389,59 @@ class TestMainDimensions:
class TestExploded:
def test_exploded_wall_count_ground_floor(self, plan1: MagicPlanPlan) -> None:
def test_exploded_wall_count_ground_floor(self, plan1: MagicPlanXMLPlan) -> None:
assert len(plan1.floors[0].exploded.walls) == 11
def test_exploded_door_count_ground_floor(self, plan1: MagicPlanPlan) -> None:
def test_exploded_door_count_ground_floor(self, plan1: MagicPlanXMLPlan) -> None:
assert len(plan1.floors[0].exploded.doors) == 6
def test_exploded_window_count_ground_floor(self, plan1: MagicPlanPlan) -> None:
def test_exploded_window_count_ground_floor(self, plan1: MagicPlanXMLPlan) -> None:
assert len(plan1.floors[0].exploded.windows) == 2
def test_exploded_wall_type(self, plan1: MagicPlanPlan) -> None:
def test_exploded_wall_type(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].exploded.walls[0].wall_type == "exterior"
def test_exploded_wall_points(self, plan1: MagicPlanPlan) -> None:
def test_exploded_wall_points(self, plan1: MagicPlanXMLPlan) -> None:
pts = plan1.floors[0].exploded.walls[0].points
assert len(pts) == 2
assert math.isclose(pts[0].x, 2.363)
assert math.isclose(pts[0].y, -1.778)
assert math.isclose(pts[0].height, 2.323164)
def test_exploded_door_x1(self, plan1: MagicPlanPlan) -> None:
def test_exploded_door_x1(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].exploded.doors[0].x1, 5.24973)
def test_exploded_door_y1(self, plan1: MagicPlanPlan) -> None:
def test_exploded_door_y1(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].exploded.doors[0].y1, -1.333153)
def test_exploded_door_width(self, plan1: MagicPlanPlan) -> None:
def test_exploded_door_width(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].exploded.doors[0].width, 0.773)
def test_exploded_door_height(self, plan1: MagicPlanPlan) -> None:
def test_exploded_door_height(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].exploded.doors[0].height, 2.02225)
def test_exploded_door_symbol_instance(self, plan1: MagicPlanPlan) -> None:
def test_exploded_door_symbol_instance(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].exploded.doors[0].symbol_instance == "W-0-1"
def test_exploded_window_x1(self, plan1: MagicPlanPlan) -> None:
def test_exploded_window_x1(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].exploded.windows[0].x1, 5.24973)
def test_exploded_window_width(self, plan1: MagicPlanPlan) -> None:
def test_exploded_window_width(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].exploded.windows[0].width, 1.71972)
def test_exploded_window_height(self, plan1: MagicPlanPlan) -> None:
def test_exploded_window_height(self, plan1: MagicPlanXMLPlan) -> None:
assert math.isclose(plan1.floors[0].exploded.windows[0].height, 0.949396)
def test_exploded_window_symbol_instance(self, plan1: MagicPlanPlan) -> None:
def test_exploded_window_symbol_instance(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.floors[0].exploded.windows[0].symbol_instance == "W-0-2"
def test_plan3_exploded_wall_count(self, plan3: MagicPlanPlan) -> None:
def test_plan3_exploded_wall_count(self, plan3: MagicPlanXMLPlan) -> None:
assert len(plan3.floors[0].exploded.walls) == 33
def test_plan3_exploded_door_count(self, plan3: MagicPlanPlan) -> None:
def test_plan3_exploded_door_count(self, plan3: MagicPlanXMLPlan) -> None:
assert len(plan3.floors[0].exploded.doors) == 12
def test_plan3_exploded_window_count(self, plan3: MagicPlanPlan) -> None:
def test_plan3_exploded_window_count(self, plan3: MagicPlanXMLPlan) -> None:
assert len(plan3.floors[0].exploded.windows) == 5
@ -451,11 +451,11 @@ class TestExploded:
class TestInteriorRoomPoints:
def test_interior_room_floor_count_plan1(self, plan1: MagicPlanPlan) -> None:
def test_interior_room_floor_count_plan1(self, plan1: MagicPlanXMLPlan) -> None:
assert len(plan1.interior_room_floors) == 2
def test_interior_room_floor_count_plan2(self, plan2: MagicPlanPlan) -> None:
def test_interior_room_floor_count_plan2(self, plan2: MagicPlanXMLPlan) -> None:
assert len(plan2.interior_room_floors) == 1
def test_interior_room_floor_uid_matches(self, plan1: MagicPlanPlan) -> None:
def test_interior_room_floor_uid_matches(self, plan1: MagicPlanXMLPlan) -> None:
assert plan1.interior_room_floors[0].uid == plan1.floors[0].uid

View file

@ -1,19 +1,19 @@
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,
MagicPlanXMLDoor,
MagicPlanXMLExploded,
MagicPlanXMLExplodedOpening,
MagicPlanXMLExplodedWall,
MagicPlanXMLFloor,
MagicPlanXMLFurniture,
MagicPlanXMLMainDimension,
MagicPlanXMLPlan,
MagicPlanXMLRoom,
MagicPlanXMLRoomPoint,
MagicPlanXMLSymbolInstance,
MagicPlanXMLWallPoint,
MagicPlanXMLWindow,
)
@ -21,8 +21,8 @@ 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(
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")),
@ -31,16 +31,16 @@ def _parse_room_point(el: ET.Element) -> MagicPlanRoomPoint:
)
def _parse_wall_point(el: ET.Element) -> MagicPlanWallPoint:
return MagicPlanWallPoint(
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) -> MagicPlanDoor:
return MagicPlanDoor(
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")),
@ -62,8 +62,8 @@ def _parse_door(el: ET.Element) -> MagicPlanDoor:
)
def _parse_window(el: ET.Element) -> MagicPlanWindow:
return MagicPlanWindow(
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")),
@ -84,8 +84,8 @@ def _parse_window(el: ET.Element) -> MagicPlanWindow:
)
def _parse_exploded_opening(el: ET.Element) -> MagicPlanExplodedOpening:
return MagicPlanExplodedOpening(
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")),
@ -101,8 +101,8 @@ def _parse_exploded_opening(el: ET.Element) -> MagicPlanExplodedOpening:
)
def _parse_furniture(el: ET.Element) -> MagicPlanFurniture:
return MagicPlanFurniture(
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")),
@ -122,8 +122,8 @@ def _parse_furniture(el: ET.Element) -> MagicPlanFurniture:
)
def _parse_main_dimension(el: ET.Element) -> MagicPlanMainDimension:
return MagicPlanMainDimension(
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")),
@ -134,16 +134,16 @@ def _parse_main_dimension(el: ET.Element) -> MagicPlanMainDimension:
)
def _parse_exploded_wall(el: ET.Element) -> MagicPlanExplodedWall:
def _parse_exploded_wall(el: ET.Element) -> MagicPlanXMLExplodedWall:
type_el = el.find("type")
return MagicPlanExplodedWall(
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) -> MagicPlanExploded:
return MagicPlanExploded(
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")],
@ -151,8 +151,8 @@ def _parse_exploded(el: ET.Element) -> MagicPlanExploded:
)
def _parse_symbol_instance(el: ET.Element) -> MagicPlanSymbolInstance:
return MagicPlanSymbolInstance(
def _parse_symbol_instance(el: ET.Element) -> MagicPlanXMLSymbolInstance:
return MagicPlanXMLSymbolInstance(
id=el.get("id", ""),
uid=el.get("uid", ""),
parent_uid=el.get("parentUid", ""),
@ -161,10 +161,10 @@ def _parse_symbol_instance(el: ET.Element) -> MagicPlanSymbolInstance:
)
def _parse_floor(el: ET.Element) -> MagicPlanFloor:
def _parse_floor(el: ET.Element) -> MagicPlanXMLFloor:
si_el = el.find("symbolInstance")
exploded_el = el.find("exploded")
return MagicPlanFloor(
return MagicPlanXMLFloor(
uid=el.get("uid", ""),
name=el.findtext("name") or "",
floor_type=el.get("floorType", "0"),
@ -174,16 +174,16 @@ def _parse_floor(el: ET.Element) -> MagicPlanFloor:
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={}),
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 MagicPlanExploded(walls=[], doors=[], windows=[], furniture=[]),
else MagicPlanXMLExploded(walls=[], doors=[], windows=[], furniture=[]),
)
def _parse_room(el: ET.Element) -> MagicPlanRoom:
return MagicPlanRoom(
def _parse_room(el: ET.Element) -> MagicPlanXMLRoom:
return MagicPlanXMLRoom(
uid=el.get("uid", ""),
type=el.get("type", ""),
x=float(el.get("x", "0")),
@ -203,10 +203,10 @@ def _parse_room(el: ET.Element) -> MagicPlanRoom:
)
def parse_magicplan_xml(xml_str: str) -> MagicPlanPlan:
def parse_magicplan_xml(xml_str: str) -> MagicPlanXMLPlan:
root = ET.fromstring(xml_str)
irp_el = root.find("interiorRoomPoints")
return MagicPlanPlan(
return MagicPlanXMLPlan(
id=root.get("id", ""),
uid=root.get("uid", ""),
name=root.get("name", ""),