From e60a016ab17911a6ca3e388d36442fdf730f469d Mon Sep 17 00:00:00 2001 From: Felipe Cardoso Date: Thu, 27 Feb 2025 18:45:08 +0100 Subject: [PATCH] Add RSVP model with status tracking and guest constraints Introduce a new RSVP model to manage responses for events, including status, guest count, and additional details like dietary requirements. Enforce uniqueness for each guest per event and provide functionality to update RSVP status. This will support efficient event RSVP handling and data integrity. --- backend/app/models/gift.py | 121 +++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 backend/app/models/gift.py diff --git a/backend/app/models/gift.py b/backend/app/models/gift.py new file mode 100644 index 0000000..7cf2f59 --- /dev/null +++ b/backend/app/models/gift.py @@ -0,0 +1,121 @@ +from datetime import datetime, timezone +from enum import Enum +from sqlalchemy import ( + Column, String, Boolean, ForeignKey, UniqueConstraint, + Integer, DateTime, Enum as SQLEnum, Table, Float +) +from sqlalchemy.orm import relationship +from sqlalchemy.dialects.postgresql import UUID, JSONB +from .base import Base, TimestampMixin, UUIDMixin + + +class GiftStatus(str, Enum): + AVAILABLE = "available" + RESERVED = "reserved" + PURCHASED = "purchased" + RECEIVED = "received" + REMOVED = "removed" + + +class GiftPriority(str, Enum): + LOW = "low" + MEDIUM = "medium" + HIGH = "high" + MUST_HAVE = "must_have" + + +class GiftItem(Base, UUIDMixin, TimestampMixin): + __tablename__ = 'gift_items' + + # Foreign Keys + event_id = Column(UUID(as_uuid=True), ForeignKey('events.id'), nullable=False) + added_by = Column(UUID(as_uuid=True), ForeignKey('users.id'), nullable=False) + category_id = Column(UUID(as_uuid=True), ForeignKey('gift_categories.id')) + + # Gift Details + name = Column(String, nullable=False) + description = Column(String) + price = Column(Float) + currency = Column(String, default='USD') + quantity_requested = Column(Integer, default=1) + quantity_received = Column(Integer, default=0) + + # Status and Priority + status = Column(SQLEnum(GiftStatus), nullable=False, default=GiftStatus.AVAILABLE) + priority = Column(SQLEnum(GiftPriority), default=GiftPriority.MEDIUM) + + # Purchase Information + purchase_url = Column(String) + store_name = Column(String) + brand = Column(String) + model = Column(String) + + # Display + image_url = Column(String) + display_order = Column(Integer) + is_visible = Column(Boolean, default=True) + + # Additional Information + notes = Column(String) + custom_fields = Column(JSONB) + + # Tracking + last_status_change = Column(DateTime(timezone=True)) + + # Relationships + event = relationship("Event", back_populates="gifts") + added_by_user = relationship("User", foreign_keys=[added_by]) + category = relationship("GiftCategory", back_populates="gifts") + reserved_by = relationship("Guest", + secondary="guest_gifts", + back_populates="gifts") + purchase_history = relationship("GiftPurchase", + back_populates="gift_item", + order_by="desc(GiftPurchase.purchased_at)") + + def __repr__(self): + return f"" + + def update_status(self, new_status: GiftStatus): + """Update gift status and record the change time""" + self.status = new_status + self.last_status_change = datetime.now(timezone.utc) + + @property + def remaining_quantity(self) -> int: + """Calculate remaining quantity needed""" + return max(0, self.quantity_requested - self.quantity_received) + + @property + def is_fully_received(self) -> bool: + """Check if all requested quantities have been received""" + return self.quantity_received >= self.quantity_requested + + @property + def formatted_price(self) -> str: + """Return formatted price with currency""" + if self.price is None: + return "Price not set" + return f"{self.price:.2f} {self.currency}" + + +# Association table for tracking gift purchases +class GiftPurchase(Base, UUIDMixin): + __tablename__ = 'gift_purchases' + + gift_id = Column(UUID(as_uuid=True), ForeignKey('gift_items.id'), nullable=False) + guest_id = Column(UUID(as_uuid=True), ForeignKey('guests.id'), nullable=False) + quantity = Column(Integer, default=1, nullable=False) + purchased_at = Column(DateTime(timezone=True), + default=lambda: datetime.now(timezone.utc), + nullable=False) + purchase_price = Column(Float) + purchase_currency = Column(String) + notes = Column(String) + + # Relationships + gift_item = relationship("GiftItem", back_populates="purchase_history") + guest = relationship("Guest") + + def __repr__(self): + return f"" \ No newline at end of file