TEST-BE-PR-001 fix calendar schema import recursion
This commit is contained in:
@@ -9,10 +9,10 @@ BE-CAL-API-004: TimeSlot cancel schemas.
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import date, time, datetime
|
from datetime import date as dt_date, time as dt_time, datetime as dt_datetime
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pydantic import BaseModel, Field, model_validator, field_validator
|
from pydantic import BaseModel, Field, model_validator, field_validator
|
||||||
from typing import Any, Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -102,17 +102,17 @@ class SlotStatusEnum(str, Enum):
|
|||||||
|
|
||||||
class TimeSlotCreate(BaseModel):
|
class TimeSlotCreate(BaseModel):
|
||||||
"""Request body for creating a single calendar slot."""
|
"""Request body for creating a single calendar slot."""
|
||||||
date: Optional[date] = Field(None, description="Target date (defaults to today)")
|
date: Optional[dt_date] = Field(None, description="Target date (defaults to today)")
|
||||||
slot_type: SlotTypeEnum = Field(..., description="work | on_call | entertainment | system")
|
slot_type: SlotTypeEnum = Field(..., description="work | on_call | entertainment | system")
|
||||||
scheduled_at: time = Field(..., description="Planned start time HH:MM (00:00-23:00)")
|
scheduled_at: dt_time = Field(..., description="Planned start time HH:MM (00:00-23:00)")
|
||||||
estimated_duration: int = Field(..., ge=1, le=50, description="Duration in minutes (1-50)")
|
estimated_duration: int = Field(..., ge=1, le=50, description="Duration in minutes (1-50)")
|
||||||
event_type: Optional[EventTypeEnum] = Field(None, description="job | entertainment | system_event")
|
event_type: Optional[EventTypeEnum] = Field(None, description="job | entertainment | system_event")
|
||||||
event_data: Optional[dict[str, Any]] = Field(None, description="Event details JSON")
|
event_data: Optional[dict] = Field(None, description="Event details JSON")
|
||||||
priority: int = Field(0, ge=0, le=99, description="Priority 0-99")
|
priority: int = Field(0, ge=0, le=99, description="Priority 0-99")
|
||||||
|
|
||||||
@field_validator("scheduled_at")
|
@field_validator("scheduled_at")
|
||||||
@classmethod
|
@classmethod
|
||||||
def _validate_scheduled_at(cls, v: time) -> time:
|
def _validate_scheduled_at(cls, v: dt_time) -> dt_time:
|
||||||
if v.hour > 23:
|
if v.hour > 23:
|
||||||
raise ValueError("scheduled_at hour must be between 00 and 23")
|
raise ValueError("scheduled_at hour must be between 00 and 23")
|
||||||
return v
|
return v
|
||||||
@@ -132,7 +132,7 @@ class TimeSlotResponse(BaseModel):
|
|||||||
"""Response for a single TimeSlot."""
|
"""Response for a single TimeSlot."""
|
||||||
id: int
|
id: int
|
||||||
user_id: int
|
user_id: int
|
||||||
date: date
|
date: dt_date
|
||||||
slot_type: str
|
slot_type: str
|
||||||
estimated_duration: int
|
estimated_duration: int
|
||||||
scheduled_at: str # HH:MM:SS ISO format
|
scheduled_at: str # HH:MM:SS ISO format
|
||||||
@@ -140,12 +140,12 @@ class TimeSlotResponse(BaseModel):
|
|||||||
attended: bool
|
attended: bool
|
||||||
actual_duration: Optional[int] = None
|
actual_duration: Optional[int] = None
|
||||||
event_type: Optional[str] = None
|
event_type: Optional[str] = None
|
||||||
event_data: Optional[dict[str, Any]] = None
|
event_data: Optional[dict] = None
|
||||||
priority: int
|
priority: int
|
||||||
status: str
|
status: str
|
||||||
plan_id: Optional[int] = None
|
plan_id: Optional[int] = None
|
||||||
created_at: Optional[datetime] = None
|
created_at: Optional[dt_datetime] = None
|
||||||
updated_at: Optional[datetime] = None
|
updated_at: Optional[dt_datetime] = None
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
@@ -169,15 +169,15 @@ class TimeSlotEdit(BaseModel):
|
|||||||
``virtual_id`` (for plan-generated virtual slots) in the URL path.
|
``virtual_id`` (for plan-generated virtual slots) in the URL path.
|
||||||
"""
|
"""
|
||||||
slot_type: Optional[SlotTypeEnum] = Field(None, description="New slot type")
|
slot_type: Optional[SlotTypeEnum] = Field(None, description="New slot type")
|
||||||
scheduled_at: Optional[time] = Field(None, description="New start time HH:MM")
|
scheduled_at: Optional[dt_time] = Field(None, description="New start time HH:MM")
|
||||||
estimated_duration: Optional[int] = Field(None, ge=1, le=50, description="New duration in minutes (1-50)")
|
estimated_duration: Optional[int] = Field(None, ge=1, le=50, description="New duration in minutes (1-50)")
|
||||||
event_type: Optional[EventTypeEnum] = Field(None, description="New event type")
|
event_type: Optional[EventTypeEnum] = Field(None, description="New event type")
|
||||||
event_data: Optional[dict[str, Any]] = Field(None, description="New event details JSON")
|
event_data: Optional[dict] = Field(None, description="New event details JSON")
|
||||||
priority: Optional[int] = Field(None, ge=0, le=99, description="New priority 0-99")
|
priority: Optional[int] = Field(None, ge=0, le=99, description="New priority 0-99")
|
||||||
|
|
||||||
@field_validator("scheduled_at")
|
@field_validator("scheduled_at")
|
||||||
@classmethod
|
@classmethod
|
||||||
def _validate_scheduled_at(cls, v: Optional[time]) -> Optional[time]:
|
def _validate_scheduled_at(cls, v: Optional[dt_time]) -> Optional[dt_time]:
|
||||||
if v is not None and v.hour > 23:
|
if v is not None and v.hour > 23:
|
||||||
raise ValueError("scheduled_at hour must be between 00 and 23")
|
raise ValueError("scheduled_at hour must be between 00 and 23")
|
||||||
return v
|
return v
|
||||||
@@ -214,7 +214,7 @@ class CalendarSlotItem(BaseModel):
|
|||||||
id: Optional[int] = Field(None, description="Real slot DB id (None for virtual)")
|
id: Optional[int] = Field(None, description="Real slot DB id (None for virtual)")
|
||||||
virtual_id: Optional[str] = Field(None, description="Virtual slot id (None for real)")
|
virtual_id: Optional[str] = Field(None, description="Virtual slot id (None for real)")
|
||||||
user_id: int
|
user_id: int
|
||||||
date: date
|
date: dt_date
|
||||||
slot_type: str
|
slot_type: str
|
||||||
estimated_duration: int
|
estimated_duration: int
|
||||||
scheduled_at: str # HH:MM:SS ISO format
|
scheduled_at: str # HH:MM:SS ISO format
|
||||||
@@ -222,12 +222,12 @@ class CalendarSlotItem(BaseModel):
|
|||||||
attended: bool
|
attended: bool
|
||||||
actual_duration: Optional[int] = None
|
actual_duration: Optional[int] = None
|
||||||
event_type: Optional[str] = None
|
event_type: Optional[str] = None
|
||||||
event_data: Optional[dict[str, Any]] = None
|
event_data: Optional[dict] = None
|
||||||
priority: int
|
priority: int
|
||||||
status: str
|
status: str
|
||||||
plan_id: Optional[int] = None
|
plan_id: Optional[int] = None
|
||||||
created_at: Optional[datetime] = None
|
created_at: Optional[dt_datetime] = None
|
||||||
updated_at: Optional[datetime] = None
|
updated_at: Optional[dt_datetime] = None
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
@@ -235,7 +235,7 @@ class CalendarSlotItem(BaseModel):
|
|||||||
|
|
||||||
class CalendarDayResponse(BaseModel):
|
class CalendarDayResponse(BaseModel):
|
||||||
"""Response for a single-day calendar query."""
|
"""Response for a single-day calendar query."""
|
||||||
date: date
|
date: dt_date
|
||||||
user_id: int
|
user_id: int
|
||||||
slots: list[CalendarSlotItem] = Field(
|
slots: list[CalendarSlotItem] = Field(
|
||||||
default_factory=list,
|
default_factory=list,
|
||||||
@@ -290,16 +290,16 @@ class SchedulePlanCreate(BaseModel):
|
|||||||
"""Request body for creating a recurring schedule plan."""
|
"""Request body for creating a recurring schedule plan."""
|
||||||
slot_type: SlotTypeEnum = Field(..., description="work | on_call | entertainment | system")
|
slot_type: SlotTypeEnum = Field(..., description="work | on_call | entertainment | system")
|
||||||
estimated_duration: int = Field(..., ge=1, le=50, description="Duration in minutes (1-50)")
|
estimated_duration: int = Field(..., ge=1, le=50, description="Duration in minutes (1-50)")
|
||||||
at_time: time = Field(..., description="Daily scheduled time (HH:MM)")
|
at_time: dt_time = Field(..., description="Daily scheduled time (HH:MM)")
|
||||||
on_day: Optional[DayOfWeekEnum] = Field(None, description="Day of week (sun-sat)")
|
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_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)")
|
on_month: Optional[MonthOfYearEnum] = Field(None, description="Month (jan-dec)")
|
||||||
event_type: Optional[EventTypeEnum] = Field(None, description="job | entertainment | system_event")
|
event_type: Optional[EventTypeEnum] = Field(None, description="job | entertainment | system_event")
|
||||||
event_data: Optional[dict[str, Any]] = Field(None, description="Event details JSON")
|
event_data: Optional[dict] = Field(None, description="Event details JSON")
|
||||||
|
|
||||||
@field_validator("at_time")
|
@field_validator("at_time")
|
||||||
@classmethod
|
@classmethod
|
||||||
def _validate_at_time(cls, v: time) -> time:
|
def _validate_at_time(cls, v: dt_time) -> dt_time:
|
||||||
if v.hour > 23:
|
if v.hour > 23:
|
||||||
raise ValueError("at_time hour must be between 00 and 23")
|
raise ValueError("at_time hour must be between 00 and 23")
|
||||||
return v
|
return v
|
||||||
@@ -325,10 +325,10 @@ class SchedulePlanResponse(BaseModel):
|
|||||||
on_week: Optional[int] = None
|
on_week: Optional[int] = None
|
||||||
on_month: Optional[str] = None
|
on_month: Optional[str] = None
|
||||||
event_type: Optional[str] = None
|
event_type: Optional[str] = None
|
||||||
event_data: Optional[dict[str, Any]] = None
|
event_data: Optional[dict] = None
|
||||||
is_active: bool
|
is_active: bool
|
||||||
created_at: Optional[datetime] = None
|
created_at: Optional[dt_datetime] = None
|
||||||
updated_at: Optional[datetime] = None
|
updated_at: Optional[dt_datetime] = None
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
@@ -352,19 +352,19 @@ class SchedulePlanEdit(BaseModel):
|
|||||||
"""
|
"""
|
||||||
slot_type: Optional[SlotTypeEnum] = Field(None, description="New slot type")
|
slot_type: Optional[SlotTypeEnum] = Field(None, description="New slot type")
|
||||||
estimated_duration: Optional[int] = Field(None, ge=1, le=50, description="New duration in minutes (1-50)")
|
estimated_duration: Optional[int] = Field(None, ge=1, le=50, description="New duration in minutes (1-50)")
|
||||||
at_time: Optional[time] = Field(None, description="New daily time (HH:MM)")
|
at_time: Optional[dt_time] = Field(None, description="New daily time (HH:MM)")
|
||||||
on_day: Optional[DayOfWeekEnum] = Field(None, description="New day of week (sun-sat), use 'clear' param to remove")
|
on_day: Optional[DayOfWeekEnum] = Field(None, description="New day of week (sun-sat), use 'clear' param to remove")
|
||||||
on_week: Optional[int] = Field(None, ge=1, le=4, description="New week of month (1-4), use 'clear' param to remove")
|
on_week: Optional[int] = Field(None, ge=1, le=4, description="New week of month (1-4), use 'clear' param to remove")
|
||||||
on_month: Optional[MonthOfYearEnum] = Field(None, description="New month (jan-dec), use 'clear' param to remove")
|
on_month: Optional[MonthOfYearEnum] = Field(None, description="New month (jan-dec), use 'clear' param to remove")
|
||||||
event_type: Optional[EventTypeEnum] = Field(None, description="New event type")
|
event_type: Optional[EventTypeEnum] = Field(None, description="New event type")
|
||||||
event_data: Optional[dict[str, Any]] = Field(None, description="New event details JSON")
|
event_data: Optional[dict] = Field(None, description="New event details JSON")
|
||||||
clear_on_day: bool = Field(False, description="Clear on_day (set to NULL)")
|
clear_on_day: bool = Field(False, description="Clear on_day (set to NULL)")
|
||||||
clear_on_week: bool = Field(False, description="Clear on_week (set to NULL)")
|
clear_on_week: bool = Field(False, description="Clear on_week (set to NULL)")
|
||||||
clear_on_month: bool = Field(False, description="Clear on_month (set to NULL)")
|
clear_on_month: bool = Field(False, description="Clear on_month (set to NULL)")
|
||||||
|
|
||||||
@field_validator("at_time")
|
@field_validator("at_time")
|
||||||
@classmethod
|
@classmethod
|
||||||
def _validate_at_time(cls, v: Optional[time]) -> Optional[time]:
|
def _validate_at_time(cls, v: Optional[dt_time]) -> Optional[dt_time]:
|
||||||
if v is not None and v.hour > 23:
|
if v is not None and v.hour > 23:
|
||||||
raise ValueError("at_time hour must be between 00 and 23")
|
raise ValueError("at_time hour must be between 00 and 23")
|
||||||
return v
|
return v
|
||||||
@@ -403,7 +403,7 @@ class DateListResponse(BaseModel):
|
|||||||
Returns only dates that have at least one materialized (real) future
|
Returns only dates that have at least one materialized (real) future
|
||||||
slot. Pure plan-generated (virtual) dates are excluded.
|
slot. Pure plan-generated (virtual) dates are excluded.
|
||||||
"""
|
"""
|
||||||
dates: list[date] = Field(
|
dates: list[dt_date] = Field(
|
||||||
default_factory=list,
|
default_factory=list,
|
||||||
description="Sorted list of future dates with materialized slots",
|
description="Sorted list of future dates with materialized slots",
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user