"""Pydantic schemas for the Calendar plugin.""" from __future__ import annotations from datetime import UTC, datetime from typing import Any from pydantic import BaseModel, Field, field_validator def _ensure_utc(v: datetime | None) -> datetime | None: """Convert naive datetimes to UTC to prevent local-time shifts in DB storage.""" if v is not None and isinstance(v, datetime) and v.tzinfo is None: return v.replace(tzinfo=UTC) return v class CalendarCreate(BaseModel): name: str = Field(..., min_length=1, max_length=200) color: str = Field("#3B82F6", max_length=20) type: str = Field("personal", pattern="^(personal|team|project|company)$") class CalendarUpdate(BaseModel): name: str | None = Field(None, min_length=1, max_length=200) color: str | None = Field(None, max_length=20) class CalendarResponse(BaseModel): id: str name: str color: str type: str owner_id: str ics_token: str | None = None created_at: datetime | None = None updated_at: datetime | None = None class ShareRequest(BaseModel): user_id: str | None = None group_id: str | None = None permission: str = Field(..., pattern="^(read|write)$") class ShareResponse(BaseModel): id: str calendar_id: str user_id: str | None = None group_id: str | None = None permission: str class EntryCreate(BaseModel): calendar_id: str entry_type: str = Field(..., pattern="^(appointment|task)$") subtype: str = Field("normal", pattern="^(normal|follow_up|private)$") title: str = Field(..., min_length=1, max_length=500) description: str | None = None start_at: datetime | None = None _ensure_utc_start = field_validator("start_at")(lambda cls, v: _ensure_utc(v)) end_at: datetime | None = None _ensure_utc_end = field_validator("end_at")(lambda cls, v: _ensure_utc(v)) all_day: bool = False location: str | None = Field(None, max_length=500) due_date: datetime | None = None _ensure_utc_due = field_validator("due_date")(lambda cls, v: _ensure_utc(v)) priority: str = Field("medium", pattern="^(low|medium|high)$") status: str = Field("open", pattern="^(open|in_progress|done|cancelled)$") assigned_to: str | None = None reminder: dict[str, Any] | None = None recurrence: dict[str, Any] | None = None class EntryUpdate(BaseModel): calendar_id: str | None = None subtype: str | None = Field(None, pattern="^(normal|follow_up|private)$") title: str | None = Field(None, min_length=1, max_length=500) description: str | None = None start_at: datetime | None = None _ensure_utc_start = field_validator("start_at")(lambda cls, v: _ensure_utc(v)) end_at: datetime | None = None _ensure_utc_end = field_validator("end_at")(lambda cls, v: _ensure_utc(v)) all_day: bool | None = None location: str | None = Field(None, max_length=500) due_date: datetime | None = None _ensure_utc_due = field_validator("due_date")(lambda cls, v: _ensure_utc(v)) priority: str | None = Field(None, pattern="^(low|medium|high)$") status: str | None = Field(None, pattern="^(open|in_progress|done|cancelled)$") assigned_to: str | None = None reminder: dict[str, Any] | None = None recurrence: dict[str, Any] | None = None class EntryResponse(BaseModel): id: str calendar_id: str entry_type: str subtype: str title: str description: str | None = None start_at: datetime | None = None end_at: datetime | None = None all_day: bool location: str | None = None due_date: datetime | None = None priority: str status: str assigned_to: str | None = None reminder: dict[str, Any] | None = None recurrence: dict[str, Any] | None = None created_by: str created_at: datetime | None = None updated_at: datetime | None = None links: list[dict] = [] subtasks: list[dict] = [] class LinkRequest(BaseModel): entity_type: str = Field(..., pattern="^(company|contact)$") entity_id: str class SubtaskCreate(BaseModel): title: str = Field(..., min_length=1, max_length=500) class SubtaskResponse(BaseModel): id: str entry_id: str title: str completed: bool class SubtaskUpdate(BaseModel): completed: bool | None = None title: str | None = Field(None, min_length=1, max_length=500) class BulkAction(BaseModel): entry_ids: list[str] = Field(..., min_length=1) action: str = Field(..., pattern="^(open|in_progress|done|cancelled|delete)$") class ResourceCreate(BaseModel): name: str = Field(..., min_length=1, max_length=200) type: str = Field(..., pattern="^(room|equipment)$") class ResourceResponse(BaseModel): id: str name: str type: str class BookResourceRequest(BaseModel): resource_id: str class ResourceBookingResponse(BaseModel): id: str resource_id: str entry_id: str start_at: datetime end_at: datetime class ImportResult(BaseModel): entries_created: int errors: list[str] = []