Files
eventspace/backend/app/models/guest.py
Felipe Cardoso 632330b4ac Refactor models to improve consistency and relationships
Updated `Base` import to use centralized `app.core.database.Base` for consistency. Enhanced `TimestampMixin` and `UUIDMixin` with docstrings for clarity. Fixed metadata reference in `guest_gifts` table and added relationships in `email_template`.
2025-02-28 09:21:02 +01:00

93 lines
3.5 KiB
Python

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),
)