invy/backend/models.py

106 lines
4.1 KiB
Python

from sqlalchemy import Boolean, Column, Integer, String, DateTime, ForeignKey, Text, Enum as SQLEnum
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from database import Base
import uuid
import enum
class User(Base):
__tablename__ = "users"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
email = Column(String, unique=True, nullable=False, index=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationships
event_memberships = relationship("EventMember", back_populates="user", cascade="all, delete-orphan")
guests_added = relationship("Guest", back_populates="added_by_user", foreign_keys="Guest.added_by_user_id")
class Event(Base):
__tablename__ = "events"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
name = Column(String, nullable=False)
date = Column(DateTime(timezone=True), nullable=True)
location = Column(String, nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Relationships
members = relationship("EventMember", back_populates="event", cascade="all, delete-orphan")
guests = relationship("Guest", back_populates="event", cascade="all, delete-orphan")
class RoleEnum(str, enum.Enum):
admin = "admin"
editor = "editor"
viewer = "viewer"
class EventMember(Base):
__tablename__ = "event_members"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
event_id = Column(UUID(as_uuid=True), ForeignKey("events.id", ondelete="CASCADE"), nullable=False, index=True)
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
role = Column(SQLEnum(RoleEnum), default=RoleEnum.admin, nullable=False)
display_name = Column(String, nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationships
event = relationship("Event", back_populates="members")
user = relationship("User", back_populates="event_memberships")
__table_args__ = (
__import__('sqlalchemy').UniqueConstraint('event_id', 'user_id', name='uq_event_user'),
)
class GuestStatus(str, enum.Enum):
invited = "invited"
confirmed = "confirmed"
declined = "declined"
class Guest(Base):
__tablename__ = "guests_v2"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
event_id = Column(UUID(as_uuid=True), ForeignKey("events.id", ondelete="CASCADE"), nullable=False, index=True)
added_by_user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False, index=True)
# Guest Information
first_name = Column(String, nullable=False)
last_name = Column(String, nullable=False)
email = Column(String, nullable=True)
phone = Column(String, nullable=True) # Legacy field - use phone_number instead
phone_number = Column(String, nullable=True)
# RSVP & Preferences
rsvp_status = Column(SQLEnum(GuestStatus), default=GuestStatus.invited, nullable=False)
meal_preference = Column(String, nullable=True)
# Plus One
has_plus_one = Column(Boolean, default=False)
plus_one_name = Column(String, nullable=True)
# Event Details
table_number = Column(String, nullable=True)
side = Column(String, nullable=True) # e.g. "groom side", "bride side"
# Source Information
owner_email = Column(String, nullable=True) # Email of person who added this guest
source = Column(String, default="manual", nullable=False) # 'google', 'manual', 'self-service'
# Notes & Metadata
notes = Column(Text, nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Relationships
event = relationship("Event", back_populates="guests")
added_by_user = relationship("User", back_populates="guests_added", foreign_keys=[added_by_user_id])