Source code for pycroft.model.facilities
# Copyright (c) 2015 The Pycroft Authors. See the AUTHORS file.
# This file is part of the Pycroft project and licensed under the terms of
# the Apache License, Version 2.0. See the LICENSE file for details.
"""
pycroft.model.facilities
~~~~~~~~~~~~~~~~~~~~~~~~
"""
from __future__ import annotations
import operator
import typing as t
from sqlalchemy import ForeignKey, UniqueConstraint
from sqlalchemy.orm import relationship, Mapped, mapped_column
from pycroft.model import ddl
from pycroft.model.address import Address, address_remove_orphans
from pycroft.model.base import IntegerIdModel
from .finance import Account
if t.TYPE_CHECKING:
# backrefs:
from .user import User, RoomHistoryEntry
from .host import Host
from .port import PatchPort
from .logging import RoomLogEntry
from .swdd import Tenancy
[docs]
class Site(IntegerIdModel):
name: Mapped[str]
# backrefs
buildings: Mapped[list[Building]] = relationship(back_populates="site")
# /backrefs
[docs]
class Building(IntegerIdModel):
site_id: Mapped[int] = mapped_column(ForeignKey(Site.id), index=True)
site: Mapped[Site] = relationship(back_populates="buildings")
number: Mapped[str]
short_name: Mapped[str] = mapped_column(unique=True)
street: Mapped[str]
wifi_available: Mapped[bool] = mapped_column(default=False)
fee_account_id: Mapped[int] = mapped_column(ForeignKey(Account.id))
fee_account: Mapped[Account] = relationship(back_populates="building")
swdd_haus_id: Mapped[int | None]
__table_args__ = (UniqueConstraint("street", "number", name="building_address"),)
# backrefs
rooms: Mapped[list[Room]] = relationship(
back_populates="building", order_by="(Room.level, Room.number)"
)
# /backrefs
@property
def street_and_number(self):
return f"{self.street} {self.number}"
[docs]
class Room(IntegerIdModel):
number: Mapped[str]
level: Mapped[int]
inhabitable: Mapped[bool]
# many to one from Room to Building
building_id: Mapped[int] = mapped_column(
ForeignKey(Building.id, onupdate="CASCADE"),
index=True,
)
building: Mapped[Building] = relationship(back_populates="rooms")
address_id: Mapped[int] = mapped_column(ForeignKey(Address.id), index=True)
address: Mapped[Address] = relationship(back_populates="rooms")
swdd_vo_suchname: Mapped[str | None]
connected_patch_ports: Mapped[list[PatchPort]] = relationship(
primaryjoin='and_(PatchPort.room_id == Room.id, PatchPort.switch_port_id.is_not(None))',
viewonly=True,
)
users_sharing_address: Mapped[list[User]] = relationship(
primaryjoin='and_(User.room_id == Room.id, User.address_id == Room.address_id)',
viewonly=True,
)
# backrefs
users: Mapped[list[User]] = relationship(back_populates="room", viewonly=True)
hosts: Mapped[list[Host]] = relationship(back_populates="room")
room_history_entries: Mapped[list[RoomHistoryEntry]] = relationship(
back_populates="room",
order_by="RoomHistoryEntry.id",
viewonly=True
)
log_entries: Mapped[list[RoomLogEntry]] = relationship(
back_populates="room", viewonly=True, cascade="all, delete-orphan"
)
patch_ports: Mapped[list[PatchPort]] = relationship(
foreign_keys="PatchPort.room_id",
back_populates="room",
cascade="all, delete-orphan",
)
tenancies: Mapped[list[Tenancy]] = relationship(
back_populates="room",
viewonly=True,
)
# /backrefs
def __str__(self):
return self.short_name
def __unicode__(self):
return self.short_name
@property
def short_name(self):
return f"{self.building.short_name} {self.level_and_number}"
@property
def level_and_number(self):
return f"{self.level}-{self.number}"
@property
def is_switch_room(self):
from pycroft.model.host import Host
from pycroft.model.host import Switch
return Host.q.join(Switch, Host.id == Switch.host_id).filter(Host.room_id==self.id).first() is not None
@property
def latest_log_entry(self) -> RoomLogEntry | None:
if not (le := self.log_entries):
return None
return max(le, key=operator.attrgetter("created_at"))
__table_args__ = (UniqueConstraint('swdd_vo_suchname'),)
manager = ddl.DDLManager()
manager.add_trigger(Room.__table__, ddl.Trigger(
'room_address_cleanup_trigger',
Room.__table__,
('UPDATE', 'DELETE'),
f'{address_remove_orphans.name}()',
))
manager.register()