
This commit includes: - Project structure and FastAPI setup - SQLAlchemy models for users, vehicles, schedules, and tickets - Alembic migrations - User authentication and management - Vehicle and schedule management - Ticket purchase and cancellation with time restrictions - Comprehensive API documentation
97 lines
3.2 KiB
Python
97 lines
3.2 KiB
Python
import uuid
|
|
from datetime import datetime, timedelta
|
|
from typing import Optional
|
|
|
|
from fastapi import HTTPException, status
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.models.ticket import Ticket, TicketStatus
|
|
from app.models.vehicle import Schedule, VehicleType
|
|
|
|
|
|
def generate_ticket_number() -> str:
|
|
"""Generate a unique ticket number."""
|
|
return str(uuid.uuid4())
|
|
|
|
|
|
def validate_purchase_time(schedule: Schedule) -> None:
|
|
"""
|
|
Validate that a ticket can be purchased based on departure time.
|
|
|
|
Args:
|
|
schedule: The schedule for which the ticket is being purchased
|
|
|
|
Raises:
|
|
HTTPException: If ticket cannot be purchased due to time restrictions
|
|
"""
|
|
now = datetime.utcnow()
|
|
# Check if it's less than 10 minutes to departure
|
|
if schedule.departure_time - now < timedelta(minutes=10):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Cannot purchase ticket less than 10 minutes before departure",
|
|
)
|
|
|
|
|
|
def validate_cancellation_time(ticket: Ticket) -> None:
|
|
"""
|
|
Validate that a ticket can be cancelled based on departure time.
|
|
|
|
Args:
|
|
ticket: The ticket to be cancelled
|
|
|
|
Raises:
|
|
HTTPException: If ticket cannot be cancelled due to time restrictions
|
|
"""
|
|
now = datetime.utcnow()
|
|
# Check if it's less than 3 minutes to departure
|
|
if ticket.schedule.departure_time - now < timedelta(minutes=3):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Cannot cancel ticket less than 3 minutes before departure",
|
|
)
|
|
|
|
# Check if ticket is already used, cancelled, or expired
|
|
if ticket.status != TicketStatus.ACTIVE:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Cannot cancel ticket with status {ticket.status}",
|
|
)
|
|
|
|
|
|
def assign_seat_number(db: Session, schedule: Schedule, vehicle_type: VehicleType) -> Optional[str]:
|
|
"""
|
|
Assign a seat number for a ticket if vehicle is a train.
|
|
For cars and buses, seat number is None.
|
|
|
|
Args:
|
|
db: Database session
|
|
schedule: The schedule for which the ticket is being purchased
|
|
vehicle_type: Type of vehicle
|
|
|
|
Returns:
|
|
Seat number string for trains, None for other vehicle types
|
|
"""
|
|
if vehicle_type != VehicleType.TRAIN:
|
|
return None
|
|
|
|
# For trains, find the next available seat number
|
|
# Get all occupied seats for this schedule
|
|
occupied_seats = db.query(Ticket.seat_number).filter(
|
|
Ticket.schedule_id == schedule.id,
|
|
Ticket.status == TicketStatus.ACTIVE,
|
|
Ticket.seat_number.isnot(None)
|
|
).all()
|
|
|
|
occupied_seat_numbers = [int(seat[0]) for seat in occupied_seats if seat[0] and seat[0].isdigit()]
|
|
|
|
# Find the first available seat
|
|
for seat_num in range(1, schedule.vehicle.capacity + 1):
|
|
if seat_num not in occupied_seat_numbers:
|
|
return str(seat_num)
|
|
|
|
# This should not happen if available_seats is managed correctly
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="No available seats",
|
|
) |