import uuid from datetime import date, datetime from typing import Literal from pydantic import BaseModel, ConfigDict, field_validator from api.schemas.workstream_dependency import WorkstreamDepStub from api.workplan_status import normalize_workstream_status WorkstreamStatus = Literal[ "proposed", "ready", "active", "blocked", "backlog", "finished", "archived", ] ExecutionState = Literal["manual", "queued", "scheduled", "launching", "paused", "completed", "cancelled"] LaunchMode = Literal["manual", "queued", "scheduled", "immediate"] ConcurrencyMode = Literal["sequential", "parallel"] class WorkstreamStatusMixin(BaseModel): @field_validator("status", mode="before", check_fields=False) @classmethod def _normalise_status(cls, value): return normalize_workstream_status(value) class WorkstreamCreate(WorkstreamStatusMixin): topic_id: uuid.UUID slug: str title: str description: str | None = None status: WorkstreamStatus = "active" owner: str | None = None due_date: date | None = None planning_priority: str | None = None planning_order: int | None = None execution_state: ExecutionState = "manual" launch_mode: LaunchMode = "manual" concurrency_mode: ConcurrencyMode = "sequential" queue_rank: int | None = None execution_group: str | None = None scheduled_for: datetime | None = None repo_id: uuid.UUID | None = None # GEMS primary: the owning repository repo_goal_id: uuid.UUID | None = None class WorkstreamUpdate(WorkstreamStatusMixin): title: str | None = None description: str | None = None status: WorkstreamStatus | None = None owner: str | None = None due_date: date | None = None planning_priority: str | None = None planning_order: int | None = None execution_state: ExecutionState | None = None launch_mode: LaunchMode | None = None concurrency_mode: ConcurrencyMode | None = None queue_rank: int | None = None execution_group: str | None = None scheduled_for: datetime | None = None repo_id: uuid.UUID | None = None repo_goal_id: uuid.UUID | None = None class WorkstreamRead(WorkstreamStatusMixin): model_config = ConfigDict(from_attributes=True) id: uuid.UUID topic_id: uuid.UUID repo_id: uuid.UUID | None = None repo_goal_id: uuid.UUID | None = None slug: str title: str description: str | None = None status: WorkstreamStatus owner: str | None = None due_date: date | None = None planning_priority: str | None = None planning_order: int | None = None execution_state: ExecutionState = "manual" launch_mode: LaunchMode = "manual" concurrency_mode: ConcurrencyMode = "sequential" queue_rank: int | None = None execution_group: str | None = None scheduled_for: datetime | None = None created_at: datetime updated_at: datetime class WorkstreamWithTaskCounts(WorkstreamRead): tasks_total: int = 0 tasks_wait: int = 0 tasks_todo: int = 0 tasks_progress: int = 0 tasks_done: int = 0 tasks_cancel: int = 0 class WorkstreamWithDeps(WorkstreamWithTaskCounts): """WorkstreamWithTaskCounts enriched with dependency graph edges.""" depends_on: list[WorkstreamDepStub] = [] blocks: list[WorkstreamDepStub] = [] blocked_reasons: list[dict] = []