BE-CAL-API-005: implement plan-schedule / plan-list API
- Add SchedulePlanCreate, SchedulePlanResponse, SchedulePlanListResponse schemas
- Add DayOfWeekEnum, MonthOfYearEnum schema enums
- Add POST /calendar/plans endpoint (create plan with hierarchy validation)
- Add GET /calendar/plans endpoint (list plans, optional include_inactive)
- Add GET /calendar/plans/{plan_id} endpoint (get single plan)
This commit is contained in:
@@ -251,3 +251,89 @@ class TimeSlotCancelResponse(BaseModel):
|
||||
"""Response after cancelling a slot — includes the cancelled slot."""
|
||||
slot: TimeSlotResponse
|
||||
message: str = Field("Slot cancelled successfully", description="Human-readable result")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# SchedulePlan enums (mirror DB enums)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class DayOfWeekEnum(str, Enum):
|
||||
SUN = "sun"
|
||||
MON = "mon"
|
||||
TUE = "tue"
|
||||
WED = "wed"
|
||||
THU = "thu"
|
||||
FRI = "fri"
|
||||
SAT = "sat"
|
||||
|
||||
|
||||
class MonthOfYearEnum(str, Enum):
|
||||
JAN = "jan"
|
||||
FEB = "feb"
|
||||
MAR = "mar"
|
||||
APR = "apr"
|
||||
MAY = "may"
|
||||
JUN = "jun"
|
||||
JUL = "jul"
|
||||
AUG = "aug"
|
||||
SEP = "sep"
|
||||
OCT = "oct"
|
||||
NOV = "nov"
|
||||
DEC = "dec"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# SchedulePlan create / response (BE-CAL-API-005)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class SchedulePlanCreate(BaseModel):
|
||||
"""Request body for creating a recurring schedule plan."""
|
||||
slot_type: SlotTypeEnum = Field(..., description="work | on_call | entertainment | system")
|
||||
estimated_duration: int = Field(..., ge=1, le=50, description="Duration in minutes (1-50)")
|
||||
at_time: time = Field(..., description="Daily scheduled time (HH:MM)")
|
||||
on_day: Optional[DayOfWeekEnum] = Field(None, description="Day of week (sun-sat)")
|
||||
on_week: Optional[int] = Field(None, ge=1, le=4, description="Week of month (1-4)")
|
||||
on_month: Optional[MonthOfYearEnum] = Field(None, description="Month (jan-dec)")
|
||||
event_type: Optional[EventTypeEnum] = Field(None, description="job | entertainment | system_event")
|
||||
event_data: Optional[dict[str, Any]] = Field(None, description="Event details JSON")
|
||||
|
||||
@field_validator("at_time")
|
||||
@classmethod
|
||||
def _validate_at_time(cls, v: time) -> time:
|
||||
if v.hour > 23:
|
||||
raise ValueError("at_time hour must be between 00 and 23")
|
||||
return v
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _validate_hierarchy(self) -> "SchedulePlanCreate":
|
||||
"""Enforce period-parameter hierarchy: on_month → on_week → on_day."""
|
||||
if self.on_month is not None and self.on_week is None:
|
||||
raise ValueError("on_month requires on_week to be set")
|
||||
if self.on_week is not None and self.on_day is None:
|
||||
raise ValueError("on_week requires on_day to be set")
|
||||
return self
|
||||
|
||||
|
||||
class SchedulePlanResponse(BaseModel):
|
||||
"""Response for a single SchedulePlan."""
|
||||
id: int
|
||||
user_id: int
|
||||
slot_type: str
|
||||
estimated_duration: int
|
||||
at_time: str # HH:MM:SS ISO format
|
||||
on_day: Optional[str] = None
|
||||
on_week: Optional[int] = None
|
||||
on_month: Optional[str] = None
|
||||
event_type: Optional[str] = None
|
||||
event_data: Optional[dict[str, Any]] = None
|
||||
is_active: bool
|
||||
created_at: Optional[datetime] = None
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class SchedulePlanListResponse(BaseModel):
|
||||
"""Response for listing schedule plans."""
|
||||
plans: list[SchedulePlanResponse] = Field(default_factory=list)
|
||||
|
||||
Reference in New Issue
Block a user