Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions 022-wedding-venues/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./wedding_platform.db"

engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
106 changes: 106 additions & 0 deletions 022-wedding-venues/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List, Optional
from datetime import datetime, timedelta
from database import engine, get_db, Base
import models, schemas, services

Base.metadata.create_all(bind=engine)

app = FastAPI()

@app.post("/signup/couple", response_model=schemas.UserResponse)
def signup_couple(user: schemas.CoupleSignUp, db: Session = Depends(get_db)):
return services.create_user(db, user)

@app.post("/signup/manager", response_model=schemas.UserResponse)
def signup_manager(user: schemas.ManagerSignUp, db: Session = Depends(get_db)):
return services.create_user(db, user)

@app.post("/venues", response_model=schemas.VenueResponse)
def create_venue(venue: schemas.VenueCreate, manager_id: int, db: Session = Depends(get_db)):
# In a real app, we would verify the manager_id via authentication
return services.create_venue(db, manager_id, venue)

@app.post("/search", response_model=List[schemas.VenueResponse])
def search(params: schemas.SearchParams, db: Session = Depends(get_db)):
venues = services.search_venues(db, params)
return venues

@app.get("/venues/{venue_id}")
def get_venue(venue_id: int, guest_count: Optional[int] = None, db: Session = Depends(get_db)):
details = services.get_venue_details(db, venue_id, guest_count)
if not details:
raise HTTPException(status_code=404, detail="Venue not found")
return details

@app.post("/venues/{venue_id}/availability")
def update_availability(venue_id: int, update: schemas.AvailabilityUpdate, db: Session = Depends(get_db)):
avail = db.query(models.Availability).filter(
models.Availability.venue_id == venue_id,
models.Availability.date == update.date.date()
).first()
if not avail:
avail = models.Availability(venue_id=venue_id, date=update.date.date())
db.add(avail)
avail.status = update.status
avail.note = update.note
db.commit()
return {"status": "updated"}

@app.post("/venues/{venue_id}/tours/slots")
def create_tour_slot(venue_id: int, slot_data: schemas.TourSlotCreate, db: Session = Depends(get_db)):
try:
# Convert date + time to datetime
start_time = datetime.combine(slot_data.date.date(),
datetime.strptime(slot_data.time, "%H:%M").time())
# We need to adapt services.create_tour_slot slightly or just pass the datetime
# I'll create a small helper here since I can't easily edit services.py again without another turn
# But wait, I can just redefine the call.

# I need to modify services.py to accept the combined datetime.
# Let me check my services.py code. It expects slot_data.start_time.
# I will wrap it.

# Since I can't edit services.py easily without a turn, I will use a modified call.
# Let's assume slot_data is a Pydantic model, I'll create a dummy object for the service.

class SlotDataHelper:
def __init__(self, date, start_time, duration_minutes, capacity):
self.date = date
self.start_time = start_time
self.duration_minutes = duration_minutes
self.capacity = capacity

helper = SlotDataHelper(slot_data.date, start_time, slot_data.duration_minutes, slot_data.capacity)
return services.create_tour_slot(db, venue_id, helper)
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))

@app.post("/tours/book", response_model=schemas.TourBookingResponse)
def book_tour(booking: schemas.TourBookingRequest, couple_id: int, db: Session = Depends(get_db)):
try:
return services.book_tour(db, couple_id, booking)
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))

@app.post("/tours/bookings/{booking_id}/handle")
def handle_tour(booking_id: int, approve: bool, db: Session = Depends(get_db)):
try:
return services.handle_tour_request(db, booking_id, approve)
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))

@app.post("/weddings/request", response_model=schemas.WeddingBookingResponse)
def request_wedding(request: schemas.WeddingBookingRequest, couple_id: int, db: Session = Depends(get_db)):
try:
return services.request_wedding(db, couple_id, request)
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))

@app.post("/weddings/bookings/{booking_id}/handle")
def handle_wedding(booking_id: int, approve: bool, reason: Optional[str] = None, db: Session = Depends(get_db)):
try:
return services.handle_wedding_request(db, booking_id, approve, reason)
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
109 changes: 109 additions & 0 deletions 022-wedding-venues/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
from sqlalchemy import Column, Integer, String, Float, DateTime, ForeignKey, Enum, Boolean, Text
from sqlalchemy.orm import relationship
from database import Base
import enum

class UserRole(enum.Enum):
COUPLE = "couple"
MANAGER = "manager"

class VenueType(enum.Enum):
INDOOR = "Indoor"
OUTDOOR = "Outdoor"
BOTH = "Both"

class VenueStatus(enum.Enum):
ACTIVE = "Active"
INACTIVE = "Inactive"

class TourType(enum.Enum):
IN_PERSON = "In-Person"
VIRTUAL = "Virtual"

class TourStatus(enum.Enum):
PENDING = "Pending"
APPROVED = "Approved"
DENIED = "Denied"

class BookingStatus(enum.Enum):
PENDING = "Pending"
CONFIRMED = "Confirmed"
DECLINED = "Declined"

class AvailabilityStatus(enum.Enum):
AVAILABLE = "Available"
BLOCKED = "Blocked"
BOOKED = "Booked"

class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True, nullable=False)
password = Column(String, nullable=False)
role = Column(Enum(UserRole), nullable=False)

# Couple specific
partner_name1 = Column(String, nullable=True)
partner_name2 = Column(String, nullable=True)
postcode = Column(String, nullable=True)
wedding_date = Column(DateTime, nullable=True)
venue_type_pref = Column(Enum(VenueType), nullable=True)

# Manager specific
manager_name = Column(String, nullable=True)
phone = Column(String, nullable=True)
business_name = Column(String, nullable=True)

class Venue(Base):
__tablename__ = "venues"
id = Column(Integer, primary_key=True, index=True)
manager_id = Column(Integer, ForeignKey("users.id"))
name = Column(String, nullable=False)
address = Column(String, nullable=False)
postcode = Column(String, nullable=False)
description = Column(Text, nullable=False) # min 200 chars check in schema
contact_info = Column(String, nullable=False)
min_guests = Column(Integer, nullable=False)
max_guests = Column(Integer, nullable=False)
base_fee = Column(Float, nullable=False)
per_person_fee = Column(Float, nullable=False)
venue_type = Column(Enum(VenueType), nullable=False)
status = Column(Enum(VenueStatus), default=VenueStatus.ACTIVE)
main_image_url = Column(String, nullable=True)

class Availability(Base):
__tablename__ = "availabilities"
id = Column(Integer, primary_key=True, index=True)
venue_id = Column(Integer, ForeignKey("venues.id"))
date = Column(DateTime, nullable=False)
status = Column(Enum(AvailabilityStatus), default=AvailabilityStatus.AVAILABLE)
note = Column(String, nullable=True)

class TourSlot(Base):
__tablename__ = "tour_slots"
id = Column(Integer, primary_key=True, index=True)
venue_id = Column(Integer, ForeignKey("venues.id"))
start_time = Column(DateTime, nullable=False)
duration_minutes = Column(Integer, nullable=False)
capacity = Column(Integer, nullable=False) # current available capacity

class TourBooking(Base):
__tablename__ = "tour_bookings"
id = Column(Integer, primary_key=True, index=True)
slot_id = Column(Integer, ForeignKey("tour_slots.id"))
couple_id = Column(Integer, ForeignKey("users.id"))
tour_type = Column(Enum(TourType), nullable=False)
attendee_count = Column(Integer, nullable=False)
notes = Column(String, nullable=True)
status = Column(Enum(TourStatus), default=TourStatus.PENDING)

class WeddingBooking(Base):
__tablename__ = "wedding_bookings"
id = Column(Integer, primary_key=True, index=True)
venue_id = Column(Integer, ForeignKey("venues.id"))
couple_id = Column(Integer, ForeignKey("users.id"))
date = Column(DateTime, nullable=False)
guest_count = Column(Integer, nullable=False)
note = Column(String, nullable=True)
status = Column(Enum(BookingStatus), default=BookingStatus.PENDING)
decline_reason = Column(String, nullable=True)
131 changes: 131 additions & 0 deletions 022-wedding-venues/schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
from pydantic import BaseModel, EmailStr, Field, validator
from typing import Optional, List
from datetime import datetime, date
from models import UserRole, VenueType, VenueStatus, TourType, TourStatus, BookingStatus, AvailabilityStatus

class UserBase(BaseModel):
email: EmailStr
password: str
role: UserRole

class CoupleSignUp(UserBase):
partner_name1: str
partner_name2: str
postcode: str
wedding_date: datetime
venue_type_pref: VenueType

class ManagerSignUp(UserBase):
manager_name: str
phone: str
business_name: str

class UserResponse(BaseModel):
id: int
email: EmailStr
role: UserRole

class Config:
orm_mode = True

class VenueCreate(BaseModel):
name: str
address: str
postcode: str
description: str = Field(..., min_length=200)
contact_info: str
min_guests: int
max_guests: int
base_fee: float
per_person_fee: float
venue_type: VenueType
status: VenueStatus = VenueStatus.ACTIVE
main_image_url: Optional[str] = None

class VenueResponse(BaseModel):
id: int
name: str
address: str
postcode: str
description: str
contact_info: str
min_guests: int
max_guests: int
base_fee: float
per_person_fee: float
venue_type: VenueType
status: VenueStatus
main_image_url: Optional[str]
estimated_price: Optional[float] = None

class Config:
orm_mode = True

class SearchParams(BaseModel):
postcode: str
date: datetime
guest_count: int
min_price: Optional[float] = None
max_price: Optional[float] = None
venue_type: Optional[VenueType] = None
sort_by: str = "distance" # "distance" or "price"

class AvailabilityUpdate(BaseModel):
date: datetime
status: AvailabilityStatus
note: Optional[str] = None

class AvailabilityResponse(BaseModel):
date: datetime
status: AvailabilityStatus
note: Optional[str] = None

class TourSlotCreate(BaseModel):
date: datetime
time: str # HH:MM
duration_minutes: int
capacity: int

class TourSlotResponse(BaseModel):
id: int
start_time: datetime
duration_minutes: int
capacity: int

class Config:
orm_mode = True

class TourBookingRequest(BaseModel):
slot_id: int
tour_type: TourType
attendee_count: int
notes: Optional[str] = None

class TourBookingResponse(BaseModel):
id: int
slot_id: int
status: TourStatus
tour_type: TourType
attendee_count: int
notes: Optional[str]

class Config:
orm_mode = True

class WeddingBookingRequest(BaseModel):
venue_id: int
date: datetime
guest_count: int
note: Optional[str] = None

class WeddingBookingResponse(BaseModel):
id: int
venue_id: int
date: datetime
guest_count: int
status: BookingStatus
note: Optional[str]
decline_reason: Optional[str]

class Config:
orm_mode = True
Loading