230 lines
6.7 KiB
Python
230 lines
6.7 KiB
Python
from datetime import datetime
|
|
from pydantic import BaseModel, Field
|
|
|
|
from .models import CodeLanguage, NodeType, Priority, Status, TestStatus
|
|
|
|
|
|
# ─── Requirement ─────────────────────────────────────────────────────────
|
|
class RequirementCreate(BaseModel):
|
|
id: str = Field(..., max_length=64)
|
|
title: str = Field(..., max_length=256)
|
|
description: str | None = None
|
|
priority: Priority = Priority.medium
|
|
phase: int | None = None
|
|
status: Status = Status.draft
|
|
node_type: NodeType = NodeType.requirement
|
|
parent_id: str | None = None
|
|
sort_order: int = 0
|
|
project_id: int | None = None
|
|
gitea_issue_number: int | None = None
|
|
gitea_repo: str | None = None
|
|
acceptance_criteria: str | None = None
|
|
test_spec: str | None = None
|
|
test_code: str | None = None
|
|
impl_code: str | None = None
|
|
code_language: CodeLanguage | None = None
|
|
target_test_file: str | None = None
|
|
target_impl_file: str | None = None
|
|
|
|
|
|
class RequirementUpdate(BaseModel):
|
|
title: str | None = None
|
|
description: str | None = None
|
|
priority: Priority | None = None
|
|
phase: int | None = None
|
|
status: Status | None = None
|
|
node_type: NodeType | None = None
|
|
parent_id: str | None = None
|
|
sort_order: int | None = None
|
|
gitea_issue_number: int | None = None
|
|
gitea_repo: str | None = None
|
|
acceptance_criteria: str | None = None
|
|
test_spec: str | None = None
|
|
test_code: str | None = None
|
|
impl_code: str | None = None
|
|
code_language: CodeLanguage | None = None
|
|
target_test_file: str | None = None
|
|
target_impl_file: str | None = None
|
|
measurable: bool | None = None
|
|
consistent: bool | None = None
|
|
single_purpose: bool | None = None
|
|
|
|
|
|
class QualityReport(BaseModel):
|
|
traceable: bool
|
|
measurable: bool
|
|
consistent: bool
|
|
single_purpose: bool
|
|
errors: list[str]
|
|
warnings: list[str]
|
|
valid: bool
|
|
|
|
|
|
class RequirementResponse(BaseModel):
|
|
id: str
|
|
title: str
|
|
description: str | None
|
|
priority: Priority
|
|
phase: int | None
|
|
status: Status
|
|
node_type: NodeType
|
|
parent_id: str | None
|
|
sort_order: int
|
|
project_id: int | None
|
|
gitea_issue_number: int | None
|
|
gitea_repo: str | None
|
|
traceable: bool
|
|
measurable: bool
|
|
consistent: bool
|
|
single_purpose: bool
|
|
acceptance_criteria: str | None
|
|
test_spec: str | None
|
|
test_code: str | None
|
|
impl_code: str | None
|
|
code_language: CodeLanguage | None
|
|
target_test_file: str | None
|
|
target_impl_file: str | None
|
|
test_status: TestStatus
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
quality: QualityReport | None = None
|
|
children: list["RequirementResponse"] = []
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class RequirementTreeNode(BaseModel):
|
|
id: str
|
|
title: str
|
|
priority: Priority
|
|
phase: int | None
|
|
status: Status
|
|
node_type: NodeType
|
|
parent_id: str | None
|
|
child_count: int = 0
|
|
traceable: bool
|
|
measurable: bool
|
|
consistent: bool
|
|
single_purpose: bool
|
|
gitea_issue_number: int | None = None
|
|
children: list["RequirementTreeNode"] = []
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
# ─── Requirement History ────────────────────────────────────────────────
|
|
class RequirementHistorySummary(BaseModel):
|
|
id: int
|
|
requirement_id: str
|
|
version: int
|
|
title: str
|
|
status: str
|
|
created_at: datetime
|
|
change_summary: str | None
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class RequirementHistoryDetail(RequirementHistorySummary):
|
|
description: str | None
|
|
priority: str
|
|
phase: int | None
|
|
node_type: str
|
|
acceptance_criteria: str | None
|
|
test_spec: str | None
|
|
test_code: str | None
|
|
impl_code: str | None
|
|
code_language: str | None
|
|
target_test_file: str | None
|
|
target_impl_file: str | None
|
|
measurable: bool
|
|
consistent: bool
|
|
single_purpose: bool
|
|
gitea_issue_number: int | None
|
|
gitea_repo: str | None
|
|
|
|
|
|
# ─── Graph Layouts ──────────────────────────────────────────────────────
|
|
class GraphLayoutCreate(BaseModel):
|
|
name: str = Field(..., max_length=128)
|
|
description: str | None = None
|
|
project_id: int | None = None
|
|
node_positions: dict[str, dict[str, float]]
|
|
viewport: dict[str, object]
|
|
is_default: bool = False
|
|
|
|
|
|
class GraphLayoutUpdate(BaseModel):
|
|
name: str | None = None
|
|
description: str | None = None
|
|
node_positions: dict[str, dict[str, float]] | None = None
|
|
viewport: dict[str, object] | None = None
|
|
is_default: bool | None = None
|
|
|
|
|
|
class GraphLayoutResponse(BaseModel):
|
|
id: int
|
|
name: str
|
|
description: str | None
|
|
project_id: int | None
|
|
is_default: bool
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class GraphLayoutDetailResponse(GraphLayoutResponse):
|
|
node_positions: dict[str, dict[str, float]]
|
|
viewport: dict[str, object]
|
|
|
|
|
|
# ─── Cross-Pillar Links ─────────────────────────────────────────────────
|
|
class LinkCreate(BaseModel):
|
|
source_id: str
|
|
target_id: str
|
|
relationship_type: str = "shared_parent"
|
|
|
|
|
|
class LinkResponse(BaseModel):
|
|
source_id: str
|
|
target_id: str
|
|
relationship_type: str
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
# ─── Project ────────────────────────────────────────────────────────────
|
|
class ProjectCreate(BaseModel):
|
|
name: str = Field(..., max_length=256)
|
|
description: str | None = None
|
|
repo_path: str | None = None
|
|
repo_url: str | None = None
|
|
requirements_yaml_path: str | None = None
|
|
|
|
|
|
class ProjectResponse(BaseModel):
|
|
id: int
|
|
name: str
|
|
description: str | None
|
|
repo_path: str | None
|
|
repo_url: str | None
|
|
requirements_yaml_path: str | None
|
|
created_at: datetime
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
# ─── Stats ───────────────────────────────────────────────────────────────
|
|
class StatsResponse(BaseModel):
|
|
total_nodes: int
|
|
pillars: int
|
|
requirements: int
|
|
sub_requirements: int
|
|
leaves: int
|
|
by_status: dict[str, int]
|
|
by_priority: dict[str, int]
|
|
quality_passing: int
|
|
quality_failing: int
|
|
coverage_percent: float
|