Files
eventspace/backend/app/models/gift.py
Felipe Cardoso 46fabc38a4
All checks were successful
Build and Push Docker Images / changes (push) Successful in 4s
Build and Push Docker Images / build-backend (push) Successful in 48s
Build and Push Docker Images / build-frontend (push) Has been skipped
Add EventMedia and GiftCategory models
Introduce EventMedia for managing media files linked to events, including file metadata, purpose, and relationships. Add GiftCategory to categorize gifts, featuring unique constraints, display options, and utility methods for handling related gifts.
2025-02-27 19:08:48 +01:00

180 lines
6.0 KiB
Python

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"<GiftItem {self.name} ({self.status.value})>"
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"<GiftPurchase gift_id={self.gift_id} guest_id={self.guest_id}>"
class GiftCategory(Base, UUIDMixin, TimestampMixin):
__tablename__ = 'gift_categories'
# Foreign Keys
event_id = Column(UUID(as_uuid=True), ForeignKey('events.id'), nullable=False)
created_by = Column(UUID(as_uuid=True), ForeignKey('users.id'), nullable=False)
# Category Details
name = Column(String, nullable=False)
description = Column(String)
# Display
icon = Column(String) # Icon identifier or URL
color = Column(String) # Color code (hex/rgb)
display_order = Column(Integer, default=0)
is_visible = Column(Boolean, default=True)
# Additional Settings
custom_fields = Column(JSONB)
# Relationships
event = relationship("Event", back_populates="gift_categories")
gifts = relationship("GiftItem",
back_populates="category",
order_by="GiftItem.display_order")
created_by_user = relationship("User", foreign_keys=[created_by])
# Ensure unique category names within an event
__table_args__ = (
UniqueConstraint('event_id', 'name', name='uq_event_category_name'),
)
def __repr__(self):
return f"<GiftCategory {self.name}>"
@property
def total_gifts(self) -> int:
"""Get total number of gifts in category"""
return len(self.gifts)
@property
def available_gifts(self) -> int:
"""Get number of available gifts in category"""
return sum(1 for gift in self.gifts
if gift.status == 'available' and gift.is_visible)
def reorder_gifts(self, gift_ids: list[UUID]) -> None:
"""
Reorder gifts within the category
Args:
gift_ids: List of gift IDs in desired order
"""
gift_order = {gift_id: idx for idx, gift_id in enumerate(gift_ids)}
for gift in self.gifts:
if gift.id in gift_order:
gift.display_order = gift_order[gift.id]