"""Calendar models — TimeSlot and related enums. TimeSlot represents a single scheduled slot on a user's calendar. Slots can be created manually or materialized from a SchedulePlan. See: NEXT_WAVE_DEV_DIRECTION.md §1.1 """ from sqlalchemy import ( Column, Integer, String, Text, DateTime, Date, Time, ForeignKey, Enum, Boolean, JSON, ) from sqlalchemy.sql import func from app.core.config import Base import enum # --------------------------------------------------------------------------- # Enums # --------------------------------------------------------------------------- class SlotType(str, enum.Enum): """What kind of slot this is.""" WORK = "work" ON_CALL = "on_call" ENTERTAINMENT = "entertainment" SYSTEM = "system" class SlotStatus(str, enum.Enum): """Lifecycle status of a slot.""" NOT_STARTED = "not_started" ONGOING = "ongoing" DEFERRED = "deferred" SKIPPED = "skipped" PAUSED = "paused" FINISHED = "finished" ABORTED = "aborted" class EventType(str, enum.Enum): """High-level event category stored alongside the slot.""" JOB = "job" ENTERTAINMENT = "entertainment" SYSTEM_EVENT = "system_event" # --------------------------------------------------------------------------- # TimeSlot model # --------------------------------------------------------------------------- class TimeSlot(Base): __tablename__ = "time_slots" id = Column(Integer, primary_key=True, index=True) user_id = Column( Integer, ForeignKey("users.id"), nullable=False, index=True, comment="Owner of this slot", ) date = Column( Date, nullable=False, index=True, comment="Calendar date for this slot", ) slot_type = Column( Enum(SlotType, values_callable=lambda x: [e.value for e in x]), nullable=False, comment="work | on_call | entertainment | system", ) estimated_duration = Column( Integer, nullable=False, comment="Estimated duration in minutes (1-50)", ) scheduled_at = Column( Time, nullable=False, comment="Planned start time (00:00-23:00)", ) started_at = Column( Time, nullable=True, comment="Actual start time (filled when slot begins)", ) attended = Column( Boolean, default=False, nullable=False, comment="Whether the slot has been attended", ) actual_duration = Column( Integer, nullable=True, comment="Actual duration in minutes (0-65535), no upper design limit", ) event_type = Column( Enum(EventType, values_callable=lambda x: [e.value for e in x]), nullable=True, comment="job | entertainment | system_event", ) event_data = Column( JSON, nullable=True, comment="Event details JSON — structure depends on event_type", ) priority = Column( Integer, nullable=False, default=0, comment="Priority 0-99, higher = more important", ) status = Column( Enum(SlotStatus, values_callable=lambda x: [e.value for e in x]), nullable=False, default=SlotStatus.NOT_STARTED, comment="Lifecycle status of this slot", ) plan_id = Column( Integer, ForeignKey("schedule_plans.id"), nullable=True, comment="Source plan if materialized from a SchedulePlan; set NULL on edit/cancel", ) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), onupdate=func.now())