from typing import Optional, TYPE_CHECKING, List if TYPE_CHECKING: from .areas import Area from .room_functions import RoomFunction from .inventory import Inventory from .users import User from sqlalchemy import ForeignKey, Identity, Integer, Unicode from sqlalchemy.orm import Mapped, mapped_column, relationship from . import db class Room(db.Model): __tablename__ = 'Rooms' id: Mapped[int] = mapped_column("ID", Integer, Identity(start=1, increment=1), primary_key=True) name: Mapped[Optional[str]] = mapped_column("Room", Unicode(255), nullable=True) area_id: Mapped[Optional[int]] = mapped_column("Area", Integer, ForeignKey("Areas.ID")) function_id: Mapped[Optional[int]] = mapped_column("Function", Integer, ForeignKey("Room Functions.ID")) area: Mapped[Optional['Area']] = relationship('Area', back_populates='rooms') room_function: Mapped[Optional['RoomFunction']] = relationship('RoomFunction', back_populates='rooms') inventory: Mapped[List['Inventory']] = relationship('Inventory', back_populates='location') users: Mapped[List['User']] = relationship('User', back_populates='location') def __init__(self, name: Optional[str] = None, area_id: Optional[int] = None, function_id: Optional[int] = None): self.name = name self.area_id = area_id self.function_id = function_id def __repr__(self): return f"" @property def full_name(self): name = self.name or "" func = self.room_function.description if self.room_function else "" return f"{name} - {func}".strip(" -") def serialize(self): return { 'id': self.id, 'name': self.name, 'area_id': self.area_id, 'function_id': self.function_id } @classmethod def sync_from_state(cls, submitted_rooms: list[dict], section_map: dict[str, int], function_map: dict[str, int]) -> None: """ Syncs the Rooms table with the submitted room list. Resolves foreign keys using section_map and function_map. Supports add, update, and delete. """ def resolve_fk(key, fk_map, label): # Print the fucking map so we can see what we're working with print(f"Resolving {label} ID: {key} using map: {fk_map}") if key is None: return None key = str(key) if key.startswith("temp") or not key.isdigit(): if key in fk_map: return fk_map[key] raise ValueError(f"Unable to resolve {label} ID: {key}") return int(key) # It's already a real ID submitted_clean = [] seen_ids = set() for room in submitted_rooms: if not isinstance(room, dict): continue name = str(room.get("name", "")).strip() if not name: continue rid = room.get("id") section_id = room.get("section_id") function_id = room.get("function_id") submitted_clean.append({ "id": rid, "name": name, "section_id": section_id, "function_id": function_id }) if rid and not str(rid).startswith("room-"): try: seen_ids.add(int(rid)) except ValueError: pass # Not valid? Not seen. existing_query = db.session.query(cls) existing_by_id = {room.id: room for room in existing_query.all()} existing_ids = set(existing_by_id.keys()) print(f"Existing room IDs: {existing_ids}") print(f"Submitted room IDs: {seen_ids}") for entry in submitted_clean: rid = entry.get("id") name = entry["name"] resolved_section_id = resolve_fk(entry.get("section_id"), section_map, "section") resolved_function_id = resolve_fk(entry.get("function_id"), function_map, "function") if not rid or str(rid).startswith("room-"): new_room = cls(name=name, area_id=resolved_section_id, function_id=resolved_function_id) db.session.add(new_room) print(f"➕ Adding room: {new_room}") else: try: rid_int = int(rid) except ValueError: print(f"⚠️ Invalid room ID format: {rid}") continue room = existing_by_id.get(rid_int) if not room: print(f"⚠️ No matching room in DB for ID: {rid_int}") continue updated = False if room.name != name: print(f"✏️ Updating room name {room.id}: '{room.name}' → '{name}'") room.name = name updated = True if room.area_id != resolved_section_id: print(f"✏️ Updating room area {room.id}: {room.area_id} → {resolved_section_id}") room.area_id = resolved_section_id updated = True if room.function_id != resolved_function_id: print(f"✏️ Updating room function {room.id}: {room.function_id} → {resolved_function_id}") room.function_id = resolved_function_id updated = True if not updated: print(f"✅ No changes to room {room.id}") for existing_id in existing_ids - seen_ids: room = existing_by_id[existing_id] db.session.delete(room) print(f"🗑️ Removing room: {room.name}")