You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

306 lines
6.9 KiB
Python

# backend/app/schemas.py
from __future__ import annotations
from datetime import date, datetime
from typing import Optional
from pydantic import BaseModel
from .models import ClsType, InboxKind, InboxStatus, Prio, Sphere, TaskStatus
# ---------- people ----------
class PersonOut(BaseModel):
id: str
name: str
initial: str
color: str
is_me: bool
# ---------- tree ----------
class ProjectNode(BaseModel):
id: str
folder_id: str
parent_id: Optional[str] = None
name: str
tone: str
sort_order: int
pinned: bool
task_count: int = 0 # 자신+하위 프로젝트의 작업 수 합
children: list[ProjectNode] = []
class FolderOut(BaseModel):
id: str
name: str
tone: str
icon: str
sort_order: int
is_system: bool
projects: list[ProjectNode] = [] # 최상위 프로젝트(중첩 children 포함)
class FolderCreate(BaseModel):
name: str
tone: Optional[str] = "ink"
icon: Optional[str] = "folder"
class FolderPatch(BaseModel):
name: Optional[str] = None
tone: Optional[str] = None
icon: Optional[str] = None
sort_order: Optional[int] = None
class ProjectCreate(BaseModel):
folder_id: str
parent_id: Optional[str] = None
name: str
tone: Optional[str] = "ink"
class ProjectPatch(BaseModel):
name: Optional[str] = None
tone: Optional[str] = None
parent_id: Optional[str] = None
sort_order: Optional[int] = None
# ---------- tasks ----------
class CommentOut(BaseModel):
id: str
task_id: str
person_id: str
text: str
created_at: datetime
class TaskNode(BaseModel):
id: str
project_id: str
parent_id: Optional[str] = None
title: str
status: TaskStatus
assignee_id: Optional[str] = None
due: Optional[date] = None
prio: Prio
notes: str
est: str
delegated: bool
sort_order: int
created_at: datetime
updated_at: datetime
comments: list[CommentOut] = []
children: list[TaskNode] = []
class TaskCreate(BaseModel):
title: str
project_id: str
parent_id: Optional[str] = None
status: Optional[TaskStatus] = TaskStatus.todo
assignee_id: Optional[str] = None
due: Optional[date] = None
prio: Optional[Prio] = Prio.normal
notes: Optional[str] = ""
est: Optional[str] = ""
class TaskPatch(BaseModel):
title: Optional[str] = None
project_id: Optional[str] = None
parent_id: Optional[str] = None
status: Optional[TaskStatus] = None
assignee_id: Optional[str] = None
due: Optional[date] = None
prio: Optional[Prio] = None
notes: Optional[str] = None
est: Optional[str] = None
delegated: Optional[bool] = None
sort_order: Optional[int] = None
class CommentCreate(BaseModel):
person_id: str
text: str
# ---------- scaffold ----------
class ScaffoldItem(BaseModel):
title: str
est: str = ""
class ScaffoldOut(BaseModel):
kind: str # "연구 · 논문 작성" 등
icon: str # "brain"|"branch"|"file"|"list"
items: list[ScaffoldItem]
created: bool = False # True면 실제 생성됨, False면 미리보기
created_task_ids: list[str] = []
class ScaffoldRequest(BaseModel):
create: bool = False # True면 하위작업 실제 생성, False면 미리보기
use_llm: bool = False # LLM 보강 사용 여부
# ---------- risks ----------
class RiskOut(BaseModel):
kind: str # "지연 위험" | "업무 쏠림" | "의존성"
icon: str # "clock" | "scale" | "link"
tone: str # "coral" | "amber" | "violet"
task_id: Optional[str] = None # 관련 task id (cta 대상)
text: str # 한국어. 강조어를 ** ** 마크다운으로 감싼다
cta: Optional[str] = None # "작업 열기" | "후속 작업 보기"
# ---------- inbox ----------
class ClassificationOut(BaseModel):
id: Optional[str] = None
inbox_item_id: str
type: ClsType
sphere: Sphere
project_id: Optional[str] = None
proj_label: str
tone: str
due_text: str
when_text: str
extra: str
reason: str
confidence: float
model: str
created_at: Optional[datetime] = None
class InboxItemOut(BaseModel):
id: str
kind: InboxKind
raw: str
status: InboxStatus
created_at: datetime
materialized_task_id: Optional[str] = None
classification: Optional[ClassificationOut] = None # 최신 1건
class CaptureRequest(BaseModel):
kind: InboxKind = InboxKind.text
raw: str
class CaptureResponse(BaseModel):
item: InboxItemOut
classification: ClassificationOut
class ReclassifyRequest(BaseModel):
type: Optional[ClsType] = None # 사용자가 타입 강제 가능
class ConfirmResponse(BaseModel):
item: InboxItemOut
task: Optional[TaskNode] = None # type=task 면 생성된 작업
# ---------- llm ----------
class LLMHealth(BaseModel):
reachable: bool
provider: str # "ollama" | "heuristic"
model: str
host: Optional[str] = None
detail: str = ""
# ---------- dashboard ----------
class UserOut(BaseModel):
name: str # 예: "지우"
initial: str # 예: "지"
class WeatherOut(BaseModel):
temp: int # 현재 기온 (예: 24)
cond: str # "맑음 · 한낮 28°"
icon: str # 'cloudSun' 저장 → 표시용 'sun' 으로 매핑
class BriefingOut(BaseModel):
today: str # 히어로 날짜 라벨 (예: "6월 7일 일요일")
weather: WeatherOut
commute: str
sleep: str
note: str # HTML 허용
class EventOut(BaseModel):
id: str
time: str
title: str
tag: str
dur: str
tone: str
soon: bool
class ApprovalSummaryOut(BaseModel):
id: str
icon: str
tone: str
title: str
time: str
# risk=="high" 만 포함, 최대 3건. detail/cta/alt/undo_label 미포함.
class GoalOut(BaseModel):
id: str
title: str
pct: int
sub: str
tone: str # tone=키('blue' 등)
class InboxRecentOut(BaseModel):
id: str
kind: str
raw: str
type: str
proj_label: str
tone: str
# 평탄화 형태: 최신 classification 에서 type/proj_label/tone 을 끌어와 채운다.
class TaskSummaryItem(BaseModel):
id: str
title: str
prio: str
project: str
class TaskSummaryOut(BaseModel):
open_count: int # 미완료 작업 수
items: list[TaskSummaryItem]
class BadgesOut(BaseModel):
appr: int # 결재함 대기(=high risk 건수)
task: int # 미완료 작업 수(상단 작업 배지)
noti: int # 알림 배지 (시드 상수 6)
class DashboardOut(BaseModel):
user: UserOut
briefing: BriefingOut
saved_today: str # "47분"
today_routed: int # 7
schedule: list[EventOut]
task_summary: TaskSummaryOut
goals: list[GoalOut]
approvals_summary: list[ApprovalSummaryOut] # high-risk 만, 최대 3건
inbox_recent: list[InboxRecentOut] # 최근 3 (평탄화 형태)
badges: BadgesOut
# 재귀 모델 전방참조 해소
ProjectNode.model_rebuild()
TaskNode.model_rebuild()