Spaces:
Running
Running
| 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__) | |
| 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") | |
| 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") | |
| 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") | |
| 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") | |
| 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)} | |
| ) |