bookmyservice-ams / app /routers /appointment.py
sivarajbookmyservice's picture
removed duplicate end point
19f03f8
from fastapi import APIRouter, HTTPException, Depends, Query
from app.services.appointment import (
create_new_appointment,
reschedule_appointment,
cancel_appointment_service,
get_appointments_by_customer_id
)
from app.models.appointment import AppointmentCreateRequest, AppointmentListResponse
from app.services.order import OrderController
from app.auth import get_current_user
import logging
from typing import Optional
from app.repositories.cache import get_or_set_cache, get_or_set_appointment_cache
from app.core.config import settings
# Initialize router and logger
router = APIRouter()
logger = logging.getLogger(__name__)
@router.post("/appointment")
async def create_appointment(appointment: AppointmentCreateRequest, current_user: dict = Depends(get_current_user)):
"""
API endpoint to create a new appointment and generate a Razorpay order.
Args:
appointment (Appointment): The details of the appointment to create.
razorpay_service (RazorpayService): Dependency injection for Razorpay service.
Returns:
dict: Confirmation message with payment details.
"""
try:
# Extract customer_id from current_user token
customer_id = current_user.get("sub")
merchant_id = current_user.get("merchant_id")
if not customer_id:
raise HTTPException(status_code=401, detail="Invalid token: missing customer ID")
# Set the customer_id in the appointment object
appointment.customer_id = customer_id
appointment.merchant_id = merchant_id if appointment.merchant_id is None else appointment.merchant_id
logger.info(f"Creating a new appointment for customer: {customer_id}")
return await create_new_appointment(appointment)
except HTTPException as e:
logger.error(f"Failed to create appointment: {e.detail}")
raise e
except Exception as e:
logger.error(f"Unexpected error while creating appointment: {e}")
raise HTTPException(status_code=500, detail="Failed to create appointment")
@router.put("/reschedule/{appointment_id}")
async def reschedule(appointment_id: str, new_date: str, new_time: str, current_user: dict = Depends(get_current_user)):
"""
API endpoint to reschedule an existing appointment.
Args:
appointment_id (str): The ID of the appointment to reschedule.
new_date (str): The new date for the appointment (YYYY-MM-DD).
new_time (str): The new time for the appointment (HH:MM:SS).
Returns:
dict: Confirmation message.
"""
try:
# Extract customer_id from current_user token for authorization
customer_id = current_user.get("sub")
if not customer_id:
raise HTTPException(status_code=401, detail="Invalid token: missing customer ID")
logger.info(f"Rescheduling appointment {appointment_id} for customer: {customer_id}")
return await reschedule_appointment(appointment_id, new_date, new_time, customer_id)
except HTTPException as e:
logger.error(f"Failed to reschedule appointment {appointment_id}: {e.detail}")
raise e
except ValueError as ve:
logger.error(f"Invalid date or time format: {ve}")
raise HTTPException(status_code=400, detail=f"Invalid date or time format: {ve}")
except Exception as e:
logger.error(f"Unexpected error while rescheduling appointment {appointment_id}: {e}")
raise HTTPException(status_code=500, detail="Failed to reschedule appointment")
@router.put("/cancel/{appointment_id}")
async def cancel_appointment(appointment_id: str, cancle_reason: str, current_user: dict = Depends(get_current_user)):
"""
API endpoint to cancel an appointment.
Args:
appointment_id (str): The ID of the appointment to cancel.
cancle_reason (str): The reason for cancellation.
Returns:
dict: Confirmation message. need validate the user role
"""
try:
# Extract customer_id from current_user token for authorization
customer_id = current_user.get("sub")
if not customer_id:
raise HTTPException(status_code=401, detail="Invalid token: missing customer ID")
logger.info(f"Cancelling appointment {appointment_id} for customer: {customer_id}")
return await cancel_appointment_service(
appointment_id=appointment_id,
cancel_reason=cancle_reason,
customer_id=customer_id
)
except HTTPException as e:
logger.error(f"Failed to cancel appointment {appointment_id}: {e.detail}")
raise e
except Exception as e:
logger.error(f"Unexpected error while cancelling appointment {appointment_id}: {e}")
raise HTTPException(status_code=500, detail="Failed to cancel appointment")
@router.post("/order")
async def create_order(amount: float, currency: str = "INR", order_controller: OrderController = Depends(), current_user: dict = Depends(get_current_user)):
"""
Creates a Razorpay order before booking an appointment, with caching in Redis.
Args:
amount (float): Total amount for payment.
currency (str): Currency for the payment (default: INR).
Returns:
dict: Razorpay order response.
"""
try:
# Extract customer_id from current_user token
customer_id = current_user.get("sub")
if not customer_id:
raise HTTPException(status_code=401, detail="Invalid token: missing customer ID")
return await order_controller.create_order(customer_id, amount, currency)
except HTTPException as e:
raise e
except Exception as e:
logger.error(f"❌ Failed to create Razorpay order: {e}")
raise HTTPException(status_code=500, detail="Failed to create Razorpay order")
@router.get(
"",
response_model=dict,
summary="Get customer appointments",
description="Retrieve paginated list of appointments for the authenticated customer (no trailing slash)"
)
async def list_customer_appointments(
limit: int = Query(10, ge=1, le=50),
offset: int = Query(0, ge=0),
status: Optional[str] = Query(None, description="Either 'active' or 'past'"),
current_user: dict = Depends(get_current_user)
):
# Extract customer_id from current_user token
logger.info(f"Request received for list_customer_appointments_no_slash with limit={limit}, offset={offset}, status={status}")
customer_id = current_user.get("sub")
logger.info(f"Fetching appointments for customer: {customer_id} with limit={limit}, offset={offset}, status={status}")
if not customer_id:
raise HTTPException(status_code=401, detail="Invalid token: missing customer ID")
# ✅ Validate status input
valid_statuses = {"active", "past"}
if status and status.lower() not in valid_statuses:
raise HTTPException(status_code=400, detail="Invalid status. Use 'active' or 'past'.")
cache_key = settings.get_cache_key("appointments", customer_id, status, limit, offset)
async def fetch_from_db():
return await get_appointments_by_customer_id(
customer_id=customer_id,
limit=limit,
offset=offset,
status=status.lower() if status else None
)
try:
response = await get_or_set_appointment_cache(
cache_key,
fetch_from_db,
status=status.lower() if status else None
)
return {"data": response}
except Exception as e:
logger.error(f"Error in list_customer_appointments_no_slash: {e}")
raise HTTPException(
status_code=500,
detail={"message": "Failed to retrieve appointments", "error": str(e)}
)