invy/backend/schemas.py

243 lines
6.5 KiB
Python

from pydantic import BaseModel, Field
from typing import Optional, List, Dict
from datetime import datetime
from uuid import UUID
# ============================================
# User Schemas
# ============================================
class UserBase(BaseModel):
email: str
class UserCreate(UserBase):
pass
class User(UserBase):
id: UUID
created_at: datetime
class Config:
from_attributes = True
# ============================================
# Event Schemas
# ============================================
class EventBase(BaseModel):
name: str
date: Optional[datetime] = None
location: Optional[str] = None
partner1_name: Optional[str] = None
partner2_name: Optional[str] = None
venue: Optional[str] = None
event_time: Optional[str] = None
guest_link: Optional[str] = None
class EventCreate(EventBase):
pass
class EventUpdate(BaseModel):
name: Optional[str] = None
date: Optional[datetime] = None
location: Optional[str] = None
partner1_name: Optional[str] = None
partner2_name: Optional[str] = None
venue: Optional[str] = None
event_time: Optional[str] = None
guest_link: Optional[str] = None
class Event(EventBase):
id: UUID
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
class EventWithMembers(Event):
members: List["EventMember"] = []
# ============================================
# Event Member Schemas
# ============================================
class EventMemberBase(BaseModel):
role: str = "admin" # admin, editor, viewer
display_name: Optional[str] = None
class EventMemberCreate(BaseModel):
user_email: str = Field(..., description="Email address of the user to invite")
role: str = "admin"
display_name: Optional[str] = None
class EventMember(EventMemberBase):
id: UUID
event_id: UUID
user_id: UUID
created_at: datetime
user: Optional[User] = None
class Config:
from_attributes = True
# ============================================
# Guest Schemas
# ============================================
class GuestBase(BaseModel):
first_name: str
last_name: str
email: Optional[str] = None
phone_number: Optional[str] = None
rsvp_status: str = "invited" # invited, confirmed, declined
meal_preference: Optional[str] = None
has_plus_one: bool = False
plus_one_name: Optional[str] = None
table_number: Optional[str] = None
side: Optional[str] = None # e.g., "groom side", "bride side"
notes: Optional[str] = None
class GuestCreate(GuestBase):
pass
class GuestUpdate(BaseModel):
first_name: Optional[str] = None
last_name: Optional[str] = None
email: Optional[str] = None
phone_number: Optional[str] = None
rsvp_status: Optional[str] = None
meal_preference: Optional[str] = None
has_plus_one: Optional[bool] = None
plus_one_name: Optional[str] = None
table_number: Optional[str] = None
side: Optional[str] = None
notes: Optional[str] = None
class Guest(GuestBase):
id: UUID
event_id: UUID
added_by_user_id: UUID
owner_email: Optional[str] = None
source: str = "manual"
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
# ============================================
# Bulk Import Schemas
# ============================================
class GuestImportItem(BaseModel):
first_name: str
last_name: str
email: Optional[str] = None
phone_number: Optional[str] = None
side: Optional[str] = None
notes: Optional[str] = None
class GuestBulkImport(BaseModel):
guests: List[GuestImportItem]
# ============================================
# Filter/Search Schemas
# ============================================
class GuestFilter(BaseModel):
search: Optional[str] = None
side: Optional[str] = None
status: Optional[str] = None
added_by: Optional[str] = None # "me" for current user
# ============================================
# WhatsApp Schemas
# ============================================
class WhatsAppMessage(BaseModel):
message: str
phone: Optional[str] = None # Optional: override guest's phone
class WhatsAppStatus(BaseModel):
message_id: str
status: str
timestamp: datetime
class WhatsAppWeddingInviteRequest(BaseModel):
"""Request to send wedding invitation template to guest(s)"""
guest_ids: Optional[List[str]] = None # For bulk sending
phone_override: Optional[str] = None # Optional: override phone number
template_key: Optional[str] = "wedding_invitation" # Registry key for template selection
# Optional form data overrides (frontend form values take priority over DB)
partner1_name: Optional[str] = None # First partner / groom name
partner2_name: Optional[str] = None # Second partner / bride name
venue: Optional[str] = None # Hall / venue name
event_date: Optional[str] = None # YYYY-MM-DD or DD/MM
event_time: Optional[str] = None # HH:mm
guest_link: Optional[str] = None # RSVP link
extra_params: Optional[Dict[str, str]] = None # Custom/extra param values keyed by param name
class Config:
from_attributes = True
class WhatsAppSendResult(BaseModel):
"""Result of sending WhatsApp message to a guest"""
guest_id: str
guest_name: Optional[str] = None
phone: str
status: str # "sent", "failed"
message_id: Optional[str] = None
error: Optional[str] = None
class Config:
from_attributes = True
class WhatsAppBulkResult(BaseModel):
"""Result of bulk WhatsApp sending"""
total: int
succeeded: int
failed: int
results: List[WhatsAppSendResult]
class Config:
from_attributes = True
# ============================================
# Google Contacts Import Schema
# ============================================
class GoogleContactsImport(BaseModel):
access_token: str
owner: Optional[str] = "Google Import"
# ============================================
# Public Guest Self-Service Schema
# ============================================
class GuestPublicUpdate(BaseModel):
"""Schema for public guest self-service updates (phone-based lookup)"""
first_name: Optional[str] = None
last_name: Optional[str] = None
rsvp_status: Optional[str] = None
meal_preference: Optional[str] = None
has_plus_one: Optional[bool] = None
plus_one_name: Optional[str] = None