feat: implement automation module
- 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>
This commit is contained in:
50
backend/app/schemas/report.py
Normal file
50
backend/app/schemas/report.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional, List, Dict, Any
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ProjectSummary(BaseModel):
|
||||
project_id: str
|
||||
project_title: str
|
||||
completed_count: int
|
||||
in_progress_count: int
|
||||
overdue_count: int
|
||||
total_tasks: int
|
||||
|
||||
|
||||
class ReportSummary(BaseModel):
|
||||
completed_count: int
|
||||
in_progress_count: int
|
||||
overdue_count: int
|
||||
total_tasks: int
|
||||
|
||||
|
||||
class WeeklyReportContent(BaseModel):
|
||||
week_start: str
|
||||
week_end: str
|
||||
generated_at: str
|
||||
projects: List[Dict[str, Any]]
|
||||
summary: ReportSummary
|
||||
|
||||
|
||||
class ReportHistoryItem(BaseModel):
|
||||
id: str
|
||||
report_id: str
|
||||
generated_at: datetime
|
||||
content: Dict[str, Any]
|
||||
status: str
|
||||
error_message: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class ReportHistoryListResponse(BaseModel):
|
||||
reports: List[ReportHistoryItem]
|
||||
total: int
|
||||
|
||||
|
||||
class GenerateReportResponse(BaseModel):
|
||||
message: str
|
||||
report_id: str
|
||||
summary: ReportSummary
|
||||
82
backend/app/schemas/trigger.py
Normal file
82
backend/app/schemas/trigger.py
Normal file
@@ -0,0 +1,82 @@
|
||||
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
|
||||
Reference in New Issue
Block a user