update to python fastpi

This commit is contained in:
Iliyan Angelov
2025-11-16 15:59:05 +02:00
parent 93d4c1df80
commit 98ccd5b6ff
4464 changed files with 773233 additions and 13740 deletions

View File

@@ -0,0 +1,288 @@
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from sqlalchemy import func, and_
from typing import Optional
from datetime import datetime, timedelta
from ..config.database import get_db
from ..middleware.auth import get_current_user, authorize_roles
from ..models.user import User
from ..models.booking import Booking, BookingStatus
from ..models.payment import Payment, PaymentStatus
from ..models.room import Room
router = APIRouter(prefix="/reports", tags=["reports"])
@router.get("")
async def get_reports(
from_date: Optional[str] = Query(None, alias="from"),
to_date: Optional[str] = Query(None, alias="to"),
type: Optional[str] = Query(None),
current_user: User = Depends(authorize_roles("admin", "staff")),
db: Session = Depends(get_db)
):
"""Get comprehensive reports (Admin/Staff only)"""
try:
# Parse dates if provided
start_date = None
end_date = None
if from_date:
try:
start_date = datetime.strptime(from_date, "%Y-%m-%d")
except ValueError:
start_date = datetime.fromisoformat(from_date.replace('Z', '+00:00'))
if to_date:
try:
end_date = datetime.strptime(to_date, "%Y-%m-%d")
# Set to end of day
end_date = end_date.replace(hour=23, minute=59, second=59)
except ValueError:
end_date = datetime.fromisoformat(to_date.replace('Z', '+00:00'))
# Base queries
booking_query = db.query(Booking)
payment_query = db.query(Payment).filter(Payment.payment_status == PaymentStatus.completed)
# Apply date filters
if start_date:
booking_query = booking_query.filter(Booking.created_at >= start_date)
payment_query = payment_query.filter(Payment.payment_date >= start_date)
if end_date:
booking_query = booking_query.filter(Booking.created_at <= end_date)
payment_query = payment_query.filter(Payment.payment_date <= end_date)
# Total bookings
total_bookings = booking_query.count()
# Total revenue
total_revenue = payment_query.with_entities(func.sum(Payment.amount)).scalar() or 0.0
# Total customers (unique users with bookings)
total_customers = db.query(func.count(func.distinct(Booking.user_id))).scalar() or 0
if start_date or end_date:
customer_query = db.query(func.count(func.distinct(Booking.user_id)))
if start_date:
customer_query = customer_query.filter(Booking.created_at >= start_date)
if end_date:
customer_query = customer_query.filter(Booking.created_at <= end_date)
total_customers = customer_query.scalar() or 0
# Available rooms
available_rooms = db.query(Room).filter(Room.status == "available").count()
# Occupied rooms (rooms with active bookings)
occupied_rooms = db.query(func.count(func.distinct(Booking.room_id))).filter(
Booking.status.in_([BookingStatus.confirmed, BookingStatus.checked_in])
).scalar() or 0
# Revenue by date (daily breakdown)
revenue_by_date = []
if start_date and end_date:
daily_revenue_query = db.query(
func.date(Payment.payment_date).label('date'),
func.sum(Payment.amount).label('revenue'),
func.count(func.distinct(Payment.booking_id)).label('bookings')
).filter(Payment.payment_status == PaymentStatus.completed)
if start_date:
daily_revenue_query = daily_revenue_query.filter(Payment.payment_date >= start_date)
if end_date:
daily_revenue_query = daily_revenue_query.filter(Payment.payment_date <= end_date)
daily_revenue_query = daily_revenue_query.group_by(
func.date(Payment.payment_date)
).order_by(func.date(Payment.payment_date))
daily_data = daily_revenue_query.all()
revenue_by_date = [
{
"date": str(date),
"revenue": float(revenue or 0),
"bookings": int(bookings or 0)
}
for date, revenue, bookings in daily_data
]
# Bookings by status
bookings_by_status = {}
for status in BookingStatus:
count = booking_query.filter(Booking.status == status).count()
status_name = status.value if hasattr(status, 'value') else str(status)
bookings_by_status[status_name] = count
# Top rooms (by revenue)
top_rooms_query = db.query(
Room.id,
Room.room_number,
func.count(Booking.id).label('bookings'),
func.sum(Payment.amount).label('revenue')
).join(Booking, Room.id == Booking.room_id).join(
Payment, Booking.id == Payment.booking_id
).filter(Payment.payment_status == PaymentStatus.completed)
if start_date:
top_rooms_query = top_rooms_query.filter(Booking.created_at >= start_date)
if end_date:
top_rooms_query = top_rooms_query.filter(Booking.created_at <= end_date)
top_rooms_data = top_rooms_query.group_by(Room.id, Room.room_number).order_by(
func.sum(Payment.amount).desc()
).limit(10).all()
top_rooms = [
{
"room_id": room_id,
"room_number": room_number,
"bookings": int(bookings or 0),
"revenue": float(revenue or 0)
}
for room_id, room_number, bookings, revenue in top_rooms_data
]
return {
"status": "success",
"success": True,
"data": {
"total_bookings": total_bookings,
"total_revenue": float(total_revenue),
"total_customers": int(total_customers),
"available_rooms": available_rooms,
"occupied_rooms": occupied_rooms,
"revenue_by_date": revenue_by_date if revenue_by_date else None,
"bookings_by_status": bookings_by_status,
"top_rooms": top_rooms if top_rooms else None,
}
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/dashboard")
async def get_dashboard_stats(
current_user: User = Depends(authorize_roles("admin", "staff")),
db: Session = Depends(get_db)
):
"""Get dashboard statistics (Admin/Staff only)"""
try:
# Total bookings
total_bookings = db.query(Booking).count()
# Active bookings
active_bookings = db.query(Booking).filter(
Booking.status.in_([BookingStatus.pending, BookingStatus.confirmed, BookingStatus.checked_in])
).count()
# Total revenue (from completed payments)
total_revenue = db.query(func.sum(Payment.amount)).filter(
Payment.payment_status == PaymentStatus.completed
).scalar() or 0.0
# Today's revenue
today_start = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
today_revenue = db.query(func.sum(Payment.amount)).filter(
and_(
Payment.payment_status == PaymentStatus.completed,
Payment.payment_date >= today_start
)
).scalar() or 0.0
# Total rooms
total_rooms = db.query(Room).count()
# Available rooms
available_rooms = db.query(Room).filter(Room.status == "available").count()
# Recent bookings (last 7 days)
week_ago = datetime.utcnow() - timedelta(days=7)
recent_bookings = db.query(Booking).filter(
Booking.created_at >= week_ago
).count()
# Pending payments
pending_payments = db.query(Payment).filter(
Payment.payment_status == PaymentStatus.pending
).count()
return {
"status": "success",
"data": {
"total_bookings": total_bookings,
"active_bookings": active_bookings,
"total_revenue": float(total_revenue),
"today_revenue": float(today_revenue),
"total_rooms": total_rooms,
"available_rooms": available_rooms,
"recent_bookings": recent_bookings,
"pending_payments": pending_payments,
}
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/revenue")
async def get_revenue_report(
start_date: Optional[str] = Query(None),
end_date: Optional[str] = Query(None),
current_user: User = Depends(authorize_roles("admin", "staff")),
db: Session = Depends(get_db)
):
"""Get revenue report (Admin/Staff only)"""
try:
query = db.query(Payment).filter(
Payment.payment_status == PaymentStatus.completed
)
if start_date:
start = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
query = query.filter(Payment.payment_date >= start)
if end_date:
end = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
query = query.filter(Payment.payment_date <= end)
# Total revenue
total_revenue = db.query(func.sum(Payment.amount)).filter(
Payment.payment_status == PaymentStatus.completed
).scalar() or 0.0
# Revenue by payment method
revenue_by_method = db.query(
Payment.payment_method,
func.sum(Payment.amount).label('total')
).filter(
Payment.payment_status == PaymentStatus.completed
).group_by(Payment.payment_method).all()
method_breakdown = {}
for method, total in revenue_by_method:
method_name = method.value if hasattr(method, 'value') else str(method)
method_breakdown[method_name] = float(total or 0)
# Revenue by date (daily breakdown)
daily_revenue = db.query(
func.date(Payment.payment_date).label('date'),
func.sum(Payment.amount).label('total')
).filter(
Payment.payment_status == PaymentStatus.completed
).group_by(func.date(Payment.payment_date)).order_by(func.date(Payment.payment_date).desc()).limit(30).all()
daily_breakdown = [
{
"date": date.isoformat() if isinstance(date, datetime) else str(date),
"revenue": float(total or 0)
}
for date, total in daily_revenue
]
return {
"status": "success",
"data": {
"total_revenue": float(total_revenue),
"revenue_by_method": method_breakdown,
"daily_breakdown": daily_breakdown,
}
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))