144 lines
5.7 KiB
Python
144 lines
5.7 KiB
Python
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"<Room(id={self.id}, room={repr(self.name)}, area_id={self.area_id}, function_id={self.function_id})>"
|
||
|
||
@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}")
|
||
|