Files
Task_Reporter/app/modules/report_generation/schemas.py
egg 3927441103 feat: Add AI report generation with DIFY integration
- Add Users table for display name resolution from AD authentication
- Integrate DIFY AI service for report content generation
- Create docx assembly service with image embedding from MinIO
- Add REST API endpoints for report generation and download
- Add WebSocket notifications for generation progress
- Add frontend UI with progress modal and download functionality
- Add integration tests for report generation flow

Report sections (Traditional Chinese):
- 事件摘要 (Summary)
- 時間軸 (Timeline)
- 參與人員 (Participants)
- 處理過程 (Resolution Process)
- 目前狀態 (Current Status)
- 最終處置結果 (Final Resolution)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 18:32:40 +08:00

106 lines
3.5 KiB
Python

"""Pydantic schemas for report generation API
Request and response models for the report generation endpoints.
"""
from pydantic import BaseModel, Field
from typing import Optional, List
from datetime import datetime
from enum import Enum
class ReportStatus(str, Enum):
"""Report generation status"""
PENDING = "pending"
COLLECTING_DATA = "collecting_data"
GENERATING_CONTENT = "generating_content"
ASSEMBLING_DOCUMENT = "assembling_document"
COMPLETED = "completed"
FAILED = "failed"
# Request Schemas
class ReportGenerateRequest(BaseModel):
"""Request to generate a report (optional parameters)"""
include_images: bool = Field(default=True, description="Whether to embed images in the report")
include_file_list: bool = Field(default=True, description="Whether to include file attachment list")
# Response Schemas
class ReportGenerateResponse(BaseModel):
"""Response after triggering report generation"""
report_id: str = Field(..., description="Unique report identifier")
status: ReportStatus = Field(..., description="Initial status (typically 'pending')")
message: str = Field(default="Report generation started", description="Status message")
class ReportStatusResponse(BaseModel):
"""Full report metadata response"""
report_id: str
room_id: str
generated_by: str
generated_at: datetime
status: ReportStatus
error_message: Optional[str] = None
report_title: Optional[str] = None
prompt_tokens: Optional[int] = None
completion_tokens: Optional[int] = None
class Config:
from_attributes = True
class ReportListItem(BaseModel):
"""Report item in list response"""
report_id: str
generated_at: datetime
generated_by: str
status: ReportStatus
report_title: Optional[str] = None
class Config:
from_attributes = True
class ReportListResponse(BaseModel):
"""List of reports for a room"""
reports: List[ReportListItem]
total: int
# AI Report Content Schemas (validated JSON from DIFY)
class TimelineEvent(BaseModel):
"""Single event in timeline"""
time: str = Field(..., description="Time of event (HH:MM or YYYY-MM-DD HH:MM)")
description: str = Field(..., description="Event description")
class ParticipantInfo(BaseModel):
"""Participant information"""
name: str = Field(..., description="Participant name")
role: str = Field(..., description="Role in incident (e.g., 事件發起人, 維修負責人)")
class AIReportContent(BaseModel):
"""Validated JSON schema from DIFY AI response"""
summary: dict = Field(..., description="Event summary with 'content' field")
timeline: dict = Field(..., description="Timeline with 'events' list")
participants: dict = Field(..., description="Participants with 'members' list")
resolution_process: dict = Field(..., description="Resolution process with 'content' field")
current_status: dict = Field(..., description="Current status with 'status' and 'description' fields")
final_resolution: dict = Field(..., description="Final resolution with 'has_resolution' and 'content' fields")
@classmethod
def validate_structure(cls, data: dict) -> bool:
"""Validate the basic structure of AI response"""
required_keys = ["summary", "timeline", "participants", "resolution_process", "current_status", "final_resolution"]
for key in required_keys:
if key not in data:
return False
return True
# Error Response
class ErrorResponse(BaseModel):
"""Error response"""
detail: str