Fix test failures and workload/websocket behavior

This commit is contained in:
beabigegg
2026-01-11 08:37:21 +08:00
parent 3bdc6ff1c9
commit f5f870da56
49 changed files with 3006 additions and 1132 deletions

View File

@@ -1,4 +1,4 @@
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, computed_field, model_validator
from typing import Optional
from datetime import datetime, date
from decimal import Decimal
@@ -19,9 +19,22 @@ class ProjectBase(BaseModel):
end_date: Optional[date] = None
security_level: SecurityLevel = SecurityLevel.DEPARTMENT
@model_validator(mode="before")
@classmethod
def apply_name_alias(cls, values):
if isinstance(values, dict) and not values.get("title") and values.get("name"):
values["title"] = values["name"]
return values
@computed_field
@property
def name(self) -> str:
return self.title
class ProjectCreate(ProjectBase):
department_id: Optional[str] = None
template_id: Optional[str] = None
class ProjectUpdate(BaseModel):
@@ -34,6 +47,13 @@ class ProjectUpdate(BaseModel):
status: Optional[str] = Field(None, max_length=50)
department_id: Optional[str] = None
@model_validator(mode="before")
@classmethod
def apply_name_alias(cls, values):
if isinstance(values, dict) and not values.get("title") and values.get("name"):
values["title"] = values["name"]
return values
class ProjectResponse(ProjectBase):
id: str

View File

@@ -48,3 +48,12 @@ class GenerateReportResponse(BaseModel):
message: str
report_id: str
summary: ReportSummary
class WeeklyReportSubscription(BaseModel):
is_active: bool
last_sent_at: Optional[datetime] = None
class WeeklyReportSubscriptionUpdate(BaseModel):
is_active: bool

View File

@@ -35,6 +35,15 @@ class TaskBase(BaseModel):
start_date: Optional[datetime] = None
due_date: Optional[datetime] = None
@field_validator("title")
@classmethod
def title_not_blank(cls, value: str) -> str:
if value is None:
return value
if value.strip() == "":
raise ValueError("Title cannot be blank or whitespace")
return value
class TaskCreate(TaskBase):
parent_task_id: Optional[str] = None
@@ -57,6 +66,15 @@ class TaskUpdate(BaseModel):
custom_values: Optional[List[CustomValueInput]] = None
version: Optional[int] = Field(None, ge=1, description="Version for optimistic locking")
@field_validator("title")
@classmethod
def title_not_blank(cls, value: Optional[str]) -> Optional[str]:
if value is None:
return value
if value.strip() == "":
raise ValueError("Title cannot be blank or whitespace")
return value
class TaskStatusUpdate(BaseModel):
status_id: str
@@ -131,3 +149,8 @@ class TaskDeleteResponse(BaseModel):
task: TaskResponse
blockers_resolved: int = 0
force_deleted: bool = False
@computed_field
@property
def id(self) -> str:
return self.task.id

View File

@@ -5,9 +5,18 @@ from pydantic import BaseModel, Field
class FieldChangeCondition(BaseModel):
"""Condition for field_change triggers."""
field: str = Field(..., description="Field to check: status_id, assignee_id, priority")
operator: str = Field(..., description="Operator: equals, not_equals, changed_to, changed_from")
value: str = Field(..., description="Value to compare against")
field: str = Field(..., description="Field to check: status_id, assignee_id, priority, start_date, due_date, custom_fields")
operator: str = Field(..., description="Operator: equals, not_equals, changed_to, changed_from, before, after, in")
value: Any = Field(..., description="Value to compare against")
field_id: Optional[str] = Field(None, description="Custom field ID when field is custom_fields")
class TriggerRule(BaseModel):
"""Rule for composite field_change triggers."""
field: str = Field(..., description="Field to check: status_id, assignee_id, priority, start_date, due_date, custom_fields")
operator: str = Field(..., description="Operator: equals, not_equals, changed_to, changed_from, before, after, in")
value: Any = Field(..., description="Value to compare against")
field_id: Optional[str] = Field(None, description="Custom field ID when field is custom_fields")
class ScheduleCondition(BaseModel):
@@ -19,9 +28,12 @@ class ScheduleCondition(BaseModel):
class TriggerCondition(BaseModel):
"""Union condition that supports both field_change and schedule triggers."""
# Field change conditions
field: Optional[str] = Field(None, description="Field to check: status_id, assignee_id, priority")
operator: Optional[str] = Field(None, description="Operator: equals, not_equals, changed_to, changed_from")
value: Optional[str] = Field(None, description="Value to compare against")
field: Optional[str] = Field(None, description="Field to check: status_id, assignee_id, priority, start_date, due_date, custom_fields")
operator: Optional[str] = Field(None, description="Operator: equals, not_equals, changed_to, changed_from, before, after, in")
value: Optional[Any] = Field(None, description="Value to compare against")
field_id: Optional[str] = Field(None, description="Custom field ID when field is custom_fields")
logic: Optional[str] = Field(None, description="Composite logic: and")
rules: Optional[List[TriggerRule]] = None
# Schedule conditions
cron_expression: Optional[str] = Field(None, description="Cron expression for schedule triggers")
deadline_reminder_days: Optional[int] = Field(None, ge=1, le=365, description="Days before due date to send reminder")
@@ -37,7 +49,7 @@ class TriggerAction(BaseModel):
"""
type: str = Field(..., description="Action type: notify, update_field, auto_assign")
# Notify action fields
target: Optional[str] = Field(None, description="Target: assignee, creator, project_owner, user:<id>")
target: Optional[str] = Field(None, description="Target: assignee, creator, project_owner, project_members, department:<id>, role:<name>, user:<id>")
template: Optional[str] = Field(None, description="Message template with variables")
# update_field action fields (FEAT-014)
field: Optional[str] = Field(None, description="Field to update: priority, status_id, due_date")