- Event-based triggers (Phase 1): - Trigger/TriggerLog models with field_change type - TriggerService for condition evaluation and action execution - Trigger CRUD API endpoints - Task integration (status, assignee, priority changes) - Frontend: TriggerList, TriggerForm components - Weekly reports (Phase 2): - ScheduledReport/ReportHistory models - ReportService for stats generation - APScheduler for Friday 16:00 job - Report preview/generate/history API - Frontend: WeeklyReportPreview, ReportHistory components - Tests: 23 new tests (14 triggers + 9 reports) - OpenSpec: add-automation change archived 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
83 lines
2.3 KiB
Python
83 lines
2.3 KiB
Python
from datetime import datetime
|
|
from typing import Optional, List, Dict, Any
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class TriggerCondition(BaseModel):
|
|
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")
|
|
|
|
|
|
class TriggerAction(BaseModel):
|
|
type: str = Field(default="notify", description="Action type: notify")
|
|
target: str = Field(default="assignee", description="Target: assignee, creator, project_owner, user:<id>")
|
|
template: Optional[str] = Field(None, description="Message template with variables")
|
|
|
|
|
|
class TriggerCreate(BaseModel):
|
|
name: str = Field(..., min_length=1, max_length=200)
|
|
description: Optional[str] = Field(None, max_length=2000)
|
|
trigger_type: str = Field(default="field_change")
|
|
conditions: TriggerCondition
|
|
actions: List[TriggerAction]
|
|
is_active: bool = Field(default=True)
|
|
|
|
|
|
class TriggerUpdate(BaseModel):
|
|
name: Optional[str] = Field(None, min_length=1, max_length=200)
|
|
description: Optional[str] = Field(None, max_length=2000)
|
|
conditions: Optional[TriggerCondition] = None
|
|
actions: Optional[List[TriggerAction]] = None
|
|
is_active: Optional[bool] = None
|
|
|
|
|
|
class TriggerUserInfo(BaseModel):
|
|
id: str
|
|
name: str
|
|
email: str
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class TriggerResponse(BaseModel):
|
|
id: str
|
|
project_id: str
|
|
name: str
|
|
description: Optional[str]
|
|
trigger_type: str
|
|
conditions: Dict[str, Any]
|
|
actions: List[Dict[str, Any]]
|
|
is_active: bool
|
|
created_by: Optional[str]
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
creator: Optional[TriggerUserInfo] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class TriggerListResponse(BaseModel):
|
|
triggers: List[TriggerResponse]
|
|
total: int
|
|
|
|
|
|
class TriggerLogResponse(BaseModel):
|
|
id: str
|
|
trigger_id: str
|
|
task_id: Optional[str]
|
|
executed_at: datetime
|
|
status: str
|
|
details: Optional[Dict[str, Any]]
|
|
error_message: Optional[str]
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class TriggerLogListResponse(BaseModel):
|
|
logs: List[TriggerLogResponse]
|
|
total: int
|