Add Guest model for event guest management

This commit introduces the `Guest` model to manage event guests, including their details, status, RSVP tracking, and relationships with events and users. It also supports features like invitation handling, guest statuses, and guest-to-gift associations. This forms the foundation for handling guest-related functionalities in the backend.
This commit is contained in:
2025-02-27 18:23:51 +01:00
parent 20f9d89cd8
commit 2326767c54

View File

@@ -0,0 +1,92 @@
from datetime import datetime, timezone
from enum import Enum
from sqlalchemy import Column, String, Boolean, ForeignKey, UniqueConstraint, Integer, Enum as SQLEnum, DateTime, Table
from sqlalchemy.dialects.postgresql import UUID, JSONB
from sqlalchemy.orm import relationship
from .base import Base, TimestampMixin, UUIDMixin
class GuestStatus(str, Enum):
INVITED = "invited" # Initial state when guest is added
PENDING = "pending" # Invitation sent, waiting for response
CONFIRMED = "confirmed" # RSVP confirmed attendance
DECLINED = "declined" # RSVP declined attendance
WAITLISTED = "waitlisted" # On waitlist if event is at capacity
CANCELLED = "cancelled" # Guest cancelled after initial confirmation
class Guest(Base, UUIDMixin, TimestampMixin):
__tablename__ = 'guests'
# Foreign Keys
event_id = Column(UUID(as_uuid=True), ForeignKey('events.id'), nullable=False)
invited_by = Column(UUID(as_uuid=True), ForeignKey('users.id'), nullable=False)
user_id = Column(UUID(as_uuid=True), ForeignKey('users.id')) # Optional, if guest is a registered user
# Guest Information
full_name = Column(String, nullable=False)
email = Column(String)
phone = Column(String)
invitation_code = Column(String, unique=True, nullable=False)
# Guest Status
status = Column(SQLEnum(GuestStatus), nullable=False, default=GuestStatus.INVITED)
max_additional_guests = Column(Integer, default=0)
actual_additional_guests = Column(Integer, default=0)
# Tracking
invitation_sent_at = Column(DateTime(timezone=True))
last_reminded_at = Column(DateTime(timezone=True))
response_date = Column(DateTime(timezone=True))
# Additional Information
dietary_restrictions = Column(String)
notes = Column(String)
custom_fields = Column(JSONB)
# Access Management
is_blocked = Column(Boolean, default=False)
can_bring_guests = Column(Boolean, default=False)
# Relationships
event = relationship("Event", back_populates="guests")
inviter = relationship("User", foreign_keys=[invited_by])
user = relationship("User", foreign_keys=[user_id])
rsvp = relationship("RSVP", back_populates="guest", uselist=False)
gifts = relationship("GiftItem", secondary="guest_gifts", back_populates="reserved_by")
__table_args__ = (
UniqueConstraint('event_id', 'email', name='uq_event_guest_email'),
)
def __repr__(self):
return f"<Guest {self.full_name} ({self.status.value})>"
@property
def total_guests(self):
"""Total number of guests including additional guests"""
return 1 + (self.actual_additional_guests or 0)
@property
def has_responded(self):
"""Check if guest has responded to invitation"""
return self.status in (GuestStatus.CONFIRMED, GuestStatus.DECLINED)
def update_status(self, new_status: GuestStatus):
"""Update guest status and record response date"""
self.status = new_status
if new_status in (GuestStatus.CONFIRMED, GuestStatus.DECLINED):
self.response_date = datetime.now(timezone.utc)
# Association table for guest gifts (many-to-many relationship)
guest_gifts = Table(
'guest_gifts',
Base.metadata,
Column('guest_id', UUID(as_uuid=True), ForeignKey('guests.id'), primary_key=True),
Column('gift_id', UUID(as_uuid=True), ForeignKey('gift_items.id'), primary_key=True),
Column('reserved_at', DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)),
Column('notes', String),
)