from pydantic import BaseModel, field_serializer from datetime import datetime, timezone from typing import Optional, List def _as_utc_iso(dt: datetime | None) -> str | None: """Serialize a datetime to an ISO-8601 string with explicit Z suffix. SQLite stores datetimes without tzinfo; we treat all stored values as UTC.""" if dt is None: return None if dt.tzinfo is None: dt = dt.replace(tzinfo=timezone.utc) return dt.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z' class ConversationCreate(BaseModel): type: str # 'direct' | 'group' name: Optional[str] = None participant_ids: List[int] class MessageCreate(BaseModel): body: str class ParticipantOut(BaseModel): user_id: int username: str joined_at: datetime last_read_at: Optional[datetime] = None model_config = {"from_attributes": True} @field_serializer('joined_at') def ser_joined_at(self, v): return _as_utc_iso(v) @field_serializer('last_read_at') def ser_last_read_at(self, v): return _as_utc_iso(v) class MessageOut(BaseModel): id: int conversation_id: int sender_id: int sender_name: str body: str sent_at: datetime is_deleted: bool model_config = {"from_attributes": True} @field_serializer('sent_at') def ser_sent_at(self, v): return _as_utc_iso(v) class ConversationOut(BaseModel): id: int type: str name: Optional[str] = None is_system: bool created_at: datetime participants: List[ParticipantOut] last_message: Optional[MessageOut] = None unread_count: int model_config = {"from_attributes": True} @field_serializer('created_at') def ser_created_at(self, v): return _as_utc_iso(v)