""" Event-Centric Pydantic Models for MongoDB Author: AI Generated Created: 2025-11-24 Purpose: Define schemas for event-specific analysis results """ from pydantic import BaseModel, Field from typing import List, Dict, Optional, Any from datetime import datetime from bson import ObjectId class PyObjectId(ObjectId): """Custom ObjectId type for Pydantic v2""" @classmethod def __get_pydantic_core_schema__(cls, source_type, handler): from pydantic_core import core_schema return core_schema.union_schema([ core_schema.is_instance_schema(ObjectId), core_schema.chain_schema([ core_schema.str_schema(), core_schema.no_info_plain_validator_function(cls.validate), ]) ], serialization=core_schema.plain_serializer_function_ser_schema( lambda x: str(x) )) @classmethod def validate(cls, v): if isinstance(v, ObjectId): return v if isinstance(v, str): if not ObjectId.is_valid(v): raise ValueError(f"Invalid ObjectId: {v}") return ObjectId(v) raise ValueError(f"Expected ObjectId or string, got {type(v)}") class MarketingContent(BaseModel): """Marketing email content generated by AI""" email_subject: str email_body: str status: str = "Draft" # Draft, Approved, Sent generated_at: datetime = Field(default_factory=datetime.utcnow) approved_at: Optional[datetime] = None approved_by: Optional[str] = None class EventAudienceSegment(BaseModel): """ Audience segment specific to an event. Stores clustering results and marketing content for Event Owner review. """ id: Optional[PyObjectId] = Field(default=None, alias="_id") event_code: str = Field(..., description="Event identifier") segment_name: str = Field(..., description="Human-readable segment name in Vietnamese") segment_type: str = Field(..., description="Segment category (e.g., VIP, Potential, Dormant)") user_count: int = Field(..., description="Number of users in this segment") user_ids: List[PyObjectId] = Field(default_factory=list, description="List of user ObjectIds in this segment") criteria: Dict[str, Any] = Field( default_factory=dict, description="Average statistics for this segment (e.g., avg_spend, avg_tickets, avg_recency)" ) marketing_content: Optional[MarketingContent] = Field( default=None, description="AI-generated marketing email (Draft, pending approval)" ) created_at: datetime = Field(default_factory=datetime.utcnow) last_updated: datetime = Field(default_factory=datetime.utcnow) class Config: populate_by_name = True arbitrary_types_allowed = True json_encoders = {ObjectId: str} class AIInsights(BaseModel): """AI-generated insights from sentiment analysis""" summary: str = Field(..., description="Overall sentiment summary in Vietnamese") top_issues: List[str] = Field(default_factory=list, description="Top 5 recurring issues") improvement_suggestions: List[str] = Field(default_factory=list, description="Actionable suggestions") predicted_nps: Optional[float] = Field(None, description="Predicted Net Promoter Score (0-100)") class EventSentimentSummary(BaseModel): """ Aggregated sentiment analysis summary for an event. Provides Event Owner with quick insights about attendee feedback. """ id: Optional[PyObjectId] = Field(default=None, alias="_id") event_code: str = Field(..., description="Event identifier") total_comments: int = Field(default=0, description="Total number of comments analyzed") sentiment_distribution: Dict[str, int] = Field( default_factory=dict, description="Count of Positive, Negative, Neutral comments" ) avg_confidence: float = Field(default=0.0, description="Average confidence score of sentiment predictions") top_keywords: List[str] = Field( default_factory=list, description="Most frequently mentioned keywords/phrases" ) ai_insights: Optional[AIInsights] = Field( default=None, description="AI-generated insights and recommendations" ) last_updated: datetime = Field(default_factory=datetime.utcnow) class Config: populate_by_name = True arbitrary_types_allowed = True json_encoders = {ObjectId: str}