- Add ActionBar component with expandable toolbar for mobile - Add @mention functionality with autocomplete dropdown - Add browser notification system (push, sound, vibration) - Add NotificationSettings modal for user preferences - Add mention badges on room list cards - Add ReportPreview with Markdown rendering and copy/download - Add message copy functionality with hover actions - Add backend mentions field to messages with Alembic migration - Add lots field to rooms, remove templates - Optimize WebSocket database session handling - Various UX polish (animations, accessibility) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
120 lines
4.0 KiB
Python
120 lines
4.0 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
|
|
|
|
|
|
class ReportMarkdownResponse(BaseModel):
|
|
"""Report content in Markdown format for in-page preview"""
|
|
report_id: str
|
|
report_title: Optional[str] = None
|
|
markdown: str = Field(..., description="Full report content in Markdown format")
|
|
|
|
|
|
# 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
|
|
|
|
|
|
# Health Check Response
|
|
class HealthCheckResponse(BaseModel):
|
|
"""DIFY service health check response"""
|
|
status: str = Field(..., description="Status: 'ok' or 'error'")
|
|
message: str = Field(..., description="Human-readable status message")
|