from __future__ import annotations # TODO: Remove if python requirement is >= 3.10 from datetime import datetime from typing import Optional, Union from sqlalchemy import UniqueConstraint from flaschengeist.models import ModelSerializeMixin, UtcDateTime, Serial from flaschengeist.models.user import User from flaschengeist.database import db ######### # Types # ######### _table_prefix_ = "events_" class EventType(db.Model, ModelSerializeMixin): __tablename__ = _table_prefix_ + "event_type" id: int = db.Column(Serial, primary_key=True) name: str = db.Column(db.String(30), nullable=False, unique=True) class JobType(db.Model, ModelSerializeMixin): __tablename__ = _table_prefix_ + "job_type" id: int = db.Column(Serial, primary_key=True) name: str = db.Column(db.String(30), nullable=False, unique=True) ######## # Jobs # ######## class Service(db.Model, ModelSerializeMixin): __tablename__ = _table_prefix_ + "service" userid: str = "" is_backup: bool = db.Column(db.Boolean, default=False) value: float = db.Column(db.Numeric(precision=3, scale=2, asdecimal=False), nullable=False) _job_id = db.Column( "job_id", Serial, db.ForeignKey(f"{_table_prefix_}job.id"), nullable=False, primary_key=True, ) _user_id = db.Column("user_id", Serial, db.ForeignKey("user.id"), nullable=False, primary_key=True) user_: User = db.relationship("User") job_: Job = db.relationship("Job") @property def userid(self): return self.user_.userid class Job(db.Model, ModelSerializeMixin): __tablename__ = _table_prefix_ + "job" id: int = db.Column(Serial, primary_key=True) start: datetime = db.Column(UtcDateTime, nullable=False) end: Optional[datetime] = db.Column(UtcDateTime) type: Union[JobType, int] = db.relationship("JobType") comment: Optional[str] = db.Column(db.String(256)) locked: bool = db.Column(db.Boolean(), default=False, nullable=False) services: list[Service] = db.relationship( "Service", back_populates="job_", cascade="save-update, merge, delete, delete-orphan" ) required_services: float = db.Column(db.Numeric(precision=4, scale=2, asdecimal=False), nullable=False) event_ = db.relationship("Event", back_populates="jobs") event_id_ = db.Column("event_id", Serial, db.ForeignKey(f"{_table_prefix_}event.id"), nullable=False) type_id_ = db.Column("type_id", Serial, db.ForeignKey(f"{_table_prefix_}job_type.id"), nullable=False) invitations_ = db.relationship("Invitation", cascade="all,delete,delete-orphan", back_populates="job_") __table_args__ = (UniqueConstraint("type_id", "start", "event_id", name="_type_start_uc"),) ########## # Events # ########## class Event(db.Model, ModelSerializeMixin): """Model for an Event""" __tablename__ = _table_prefix_ + "event" id: int = db.Column(Serial, primary_key=True) start: datetime = db.Column(UtcDateTime, nullable=False) end: Optional[datetime] = db.Column(UtcDateTime) name: Optional[str] = db.Column(db.String(255)) description: Optional[str] = db.Column(db.String(512)) type: Union[EventType, int] = db.relationship("EventType") is_template: bool = db.Column(db.Boolean, default=False) jobs: list[Job] = db.relationship( "Job", back_populates="event_", cascade="all,delete,delete-orphan", order_by="[Job.start, Job.end]", ) # Protected for internal use _type_id = db.Column( "type_id", Serial, db.ForeignKey(f"{_table_prefix_}event_type.id", ondelete="CASCADE"), nullable=False, ) class Invitation(db.Model, ModelSerializeMixin): __tablename__ = _table_prefix_ + "invitation" id: int = db.Column(Serial, primary_key=True) job_id: int = db.Column(Serial, db.ForeignKey(_table_prefix_ + "job.id"), nullable=False) # Dummy properties for API export invitee_id: str = None # User who was invited to take over inviter_id: str = None # User who invited the invitee transferee_id: Optional[str] = None # In case of a transfer: The user who is transfered out of the job # Not exported properties for backend use job_: Job = db.relationship(Job, foreign_keys="Invitation.job_id") invitee_: User = db.relationship("User", foreign_keys="Invitation._invitee_id") inviter_: User = db.relationship("User", foreign_keys="Invitation._inviter_id") transferee_: User = db.relationship("User", foreign_keys="Invitation._transferee_id") # Protected properties needed for internal use _invitee_id = db.Column("invitee_id", Serial, db.ForeignKey("user.id"), nullable=False) _inviter_id = db.Column("inviter_id", Serial, db.ForeignKey("user.id"), nullable=False) _transferee_id = db.Column("transferee_id", Serial, db.ForeignKey("user.id")) @property def invitee_id(self): return self.invitee_.userid @property def inviter_id(self): return self.inviter_.userid @property def transferee_id(self): return self.transferee_.userid if self.transferee_ else None