""" Pydantic Models for Structured Report Output Defines the data structures for vendor reports matching the exact requirements. """ from pydantic import BaseModel, Field from typing import List, Optional from datetime import datetime class PunchlistItem(BaseModel): """Represents a single punchlist item with all details.""" punchlist_name: str = Field(description="Name/title of the punchlist item") description: Optional[str] = Field(default=None, description="Full description of the issue") priority: Optional[str] = Field(default=None, description="Priority level (e.g., '(1) Very High', '(2) High')") date_identified: Optional[str] = Field(default=None, description="Date when issue was identified") date_completed: Optional[str] = Field(default=None, description="Date when issue was completed (if closed)") status: str = Field(description="Current status: 'Complete', 'Monitor', 'Incomplete', etc.") status_updates: Optional[str] = Field(default=None, description="Status update history/text") issue_image: Optional[str] = Field(default=None, description="Issue image filename or link if available") age_days: Optional[int] = Field(default=None, description="Number of days since date_identified (was calculated)") class VendorUpdates24h(BaseModel): """24-hour updates grouped by type.""" added: List[PunchlistItem] = Field( default_factory=list, description="Specific items added in the last 24 hours" ) closed: List[PunchlistItem] = Field( default_factory=list, description="Specific items closed in the last 24 hours" ) changed_to_monitor: List[PunchlistItem] = Field( default_factory=list, description="Specific items changed to monitor status in the last 24 hours" ) class VendorMetrics(BaseModel): """Statistics and metrics for a single vendor - matches exact requirements.""" vendor_name: str = Field(description="Name of the vendor (normalized)") # Counts per vendor total_items: int = Field(description="Total number of punchlist items for this vendor") closed_count: int = Field(description="Number of closed items (status contains 'Complete' or 'complete')") open_count: int = Field(description="Number of open items (status contains 'Incomplete')") monitor_count: int = Field(description="Number of items in monitor status (status contains 'Monitor' or 'montor')") # Updates in the last 24 hours per vendor updates_24h: VendorUpdates24h = Field( default_factory=VendorUpdates24h, description="Updates in the last 24 hours: specific items added, closed, and changed to monitor" ) # Oldest 3 unaddressed items per vendor oldest_unaddressed: List[PunchlistItem] = Field( default_factory=list, description="Oldest 3 unaddressed items (not closed) for this vendor, sorted by date_identified" ) # All priority items by vendor very_high_priority_items: List[PunchlistItem] = Field( default_factory=list, description="All 'very high' priority items for this vendor (priority contains '(1) Very High' or 'Very High')" ) high_priority_items: List[PunchlistItem] = Field( default_factory=list, description="All 'high' priority items for this vendor (priority contains '(2) High' or 'High' but not 'Very High')" ) class FullReport(BaseModel): """Complete report containing all vendor metrics.""" report_generated_at: str = Field( default_factory=lambda: datetime.now().isoformat(), description="Timestamp when report was generated (ISO format)" ) vendors: List[VendorMetrics] = Field( default_factory=list, description="List of vendor metrics - one entry per vendor with all required statistics" ) summary: Optional[dict] = Field( default=None, description="Optional overall summary statistics across all vendors (total vendors, total items, etc.)" )