"""Pydantic-схемы API M1."""
from datetime import date, datetime
from typing import Any
from pydantic import BaseModel, ConfigDict, Field
# --- Task ---
DEADLINE_PERIODS = ("day", "week", "month", "year")
RECUR_KINDS = ("interval", "weekdays", "monthly")
class TagOut(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
name: str
class ProjectOut(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
name: str
relevance_status: str
priority: int | None
note: str
class TaskCreate(BaseModel):
title: str = Field(min_length=1, max_length=500)
description: str = ""
parent_task_id: int | None = None # подзадача (M3)
class TaskUpdate(BaseModel):
"""Частичное обновление метаданных задачи (ручная детализация)."""
title: str | None = Field(None, max_length=500)
description: str | None = None
project_id: int | None = None
parent_task_id: int | None = None
tag_ids: list[int] | None = None
priority: int | None = None
status: str | None = None
estimated_minutes: int | None = Field(None, ge=1, le=24 * 60)
actual_minutes: int | None = Field(None, ge=1, le=24 * 60)
budget_money: int | None = Field(None, ge=0)
cost_estimate_money: int | None = Field(None, ge=0)
# Дедлайны: строгий — конкретная дата; нестрогий — период («в течение…»)
deadline_date: date | None = None
deadline_period: str | None = None
# Регулярность (ТЗ 3.5): тип задачи + правило повторения
task_type: str | None = None
recur_kind: str | None = Field(None, pattern="^(interval|weekdays|monthly)$")
recur_interval_days: int | None = Field(None, ge=1, le=365)
recur_weekdays: str | None = Field(None, pattern=r"^[1-7](,[1-7])*$") # ISO, пн=1
recur_day_of_month: int | None = Field(None, ge=1, le=31)
class TaskOut(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
title: str
description: str
task_type: str
status: str
detail_state: str
parent_task_id: int | None
project: ProjectOut | None
priority: int | None
tags: list[TagOut]
ai_proposal: dict[str, Any] | None
estimated_minutes: int | None
actual_minutes: int | None
budget_money: int | None
cost_estimate_money: int | None
deadline_date: date | None
deadline_period: str | None
recur_kind: str | None
recur_interval_days: int | None
recur_weekdays: str | None
recur_day_of_month: int | None
created_at: datetime
approved_at: datetime | None
done_at: datetime | None
class ApproveIn(BaseModel):
"""Утверждение детализации; apply_proposal=True — принять предложение LLM."""
apply_proposal: bool = False
class SuggestIn(BaseModel):
"""Режим «3 варианта»: доступное время в минутах."""
available_minutes: int = Field(ge=5, le=24 * 60)
# --- Project / Tag ---
class AttachmentOut(BaseModel):
"""Вложение задачи (изображение)."""
model_config = ConfigDict(from_attributes=True)
id: int
task_id: int
filename: str
original_name: str
mime: str
size: int
created_at: datetime
class ProjectCreate(BaseModel):
name: str = Field(min_length=1, max_length=200)
priority: int | None = None
note: str = ""
class ProjectUpdate(BaseModel):
name: str | None = Field(None, max_length=200)
relevance_status: str | None = None
priority: int | None = None
note: str | None = None
class TagCreate(BaseModel):
name: str = Field(min_length=1, max_length=100)
CURRENCIES = ("UAH", "USD", "EUR", "GBP", "PLN")
# Языки UI: пустое language = нет переопределения (следуем SSO locale)
LANGUAGES = ("ru", "en", "uk")
class AppSettingsOut(BaseModel):
currency: str = "UAH"
language: str = ""
class AppSettingsUpdate(BaseModel):
# Частичный PUT: None = поле не передано, не затираем
currency: str | None = Field(None, pattern="^(UAH|USD|EUR|GBP|PLN)$")
language: str | None = Field(None, pattern="^(|ru|en|uk)$")