171 lines
4.9 KiB
Python
171 lines
4.9 KiB
Python
"""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] = []
|