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.
180 lines
6.0 KiB
Python
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]
|