Files
Task_Reporter/app/modules/report_generation/schemas.py
egg 44822a561a feat: Improve file display, timezone handling, and LOT management
Changes:
- Fix datetime serialization with UTC 'Z' suffix for correct timezone display
- Add PDF upload support with extension fallback for MIME detection
- Fix LOT add/remove by creating new list for SQLAlchemy JSON change detection
- Add file message components (FileMessage, ImageLightbox, UploadPreview)
- Add multi-file upload support with progress tracking
- Link uploaded files to chat messages via message_id
- Include file attachments in AI report generation
- Update specs for file-storage, realtime-messaging, and ai-report-generation

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 12:39:15 +08:00

128 lines
4.4 KiB
Python

"""Pydantic schemas for report generation API
Request and response models for the report generation endpoints.
"""
from pydantic import BaseModel, Field, ConfigDict, field_serializer
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
model_config = ConfigDict(from_attributes=True)
@field_serializer("generated_at")
def serialize_datetime(self, dt: datetime) -> str:
"""Serialize datetime with 'Z' suffix to indicate UTC"""
return dt.isoformat() + "Z"
class ReportListItem(BaseModel):
"""Report item in list response"""
report_id: str
generated_at: datetime
generated_by: str
status: ReportStatus
report_title: Optional[str] = None
model_config = ConfigDict(from_attributes=True)
@field_serializer("generated_at")
def serialize_datetime(self, dt: datetime) -> str:
"""Serialize datetime with 'Z' suffix to indicate UTC"""
return dt.isoformat() + "Z"
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")