Compare commits
1 Commits
1ed7a85e11
...
3dcd07bdf3
| Author | SHA1 | Date | |
|---|---|---|---|
| 3dcd07bdf3 |
143
app/models/calendar.py
Normal file
143
app/models/calendar.py
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
"""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())
|
||||||
Reference in New Issue
Block a user