from datetime import datetime, timezone import uuid from sqlalchemy.sql import text from fastapi import HTTPException from app.models.appointment import AppointmentCreateRequest def build_filter_query(filters: dict): """ Builds a dynamic SQL filter query based on the provided filter parameters. Args: filters (dict): A dictionary containing filter conditions. Returns: str: The WHERE clause to append to a query. """ conditions = [] for key, value in filters.items(): if value: conditions.append(f"{key} = :{key}") return " AND ".join(conditions) def validate_query_result(result, error_message="Item not found"): """ Validates the query result and raises an HTTPException if no result is found. Args: result (dict): The result of the query. error_message (str): The error message to return if the result is None. Raises: HTTPException: If the result is None. """ if not result: raise HTTPException(status_code=404, detail=error_message) def validate_datetime_format(datetime_str: str): """ Validates if the given string is in proper datetime format (ISO 8601). Args: datetime_str (str): The datetime string to validate. Raises: HTTPException: If the datetime string is invalid. """ try: datetime.fromisoformat(datetime_str) except ValueError: raise HTTPException(status_code=400, detail="Invalid datetime format. Use ISO 8601 format.") def serialize_appointment(appointment): """ Serializes an appointment database row into a dictionary. Args: appointment (RowProxy): The SQLAlchemy RowProxy object. Returns: dict: A serialized dictionary representation of the appointment. """ if appointment is None: return None return { "appointment_id": str(appointment["appointment_id"]), "customer_id": str(appointment["customer_id"]), "merchant_id": str(appointment["merchant_id"]), "merchant_name": appointment["merchant_name"], "city": appointment["city"], "location_id": appointment["location_id"], "merchant_address": { "street": appointment["address_street"], "area": appointment["address_area"], "postcode": appointment["address_in_tcode"], "state": appointment["address_state"] }, "appointment_date": ( appointment["appointment_date"].isoformat() if appointment["appointment_date"] else None ), "appointment_time": ( appointment["appointment_time"].isoformat() if hasattr(appointment["appointment_time"], "isoformat") else str(appointment["appointment_time"]) ), "status": appointment["status"], "notes": appointment["notes"], "total_amount": float(appointment["total_amount"]) if appointment["total_amount"] else 0.0, "discount_amount": float(appointment["discount_amount"]) if appointment["discount_amount"] else 0.0, "cleared_amount": float(appointment["cleared_amount"]) if appointment["cleared_amount"] else 0.0, "payment_mode": appointment["payment_mode"], "payment_status": appointment["payment_status"], "created_at": ( appointment["created_at"].isoformat() if appointment["created_at"] else None ), "updated_at": ( appointment["updated_at"].isoformat() if appointment["updated_at"] else None ), } def validate_existing_appointment(appointment): """ Validates the existing appointment's status for operations. Args: appointment (dict): The appointment data. Raises: HTTPException: If the appointment is not found or cannot be modified. """ if not appointment: raise HTTPException(status_code=404, detail="Appointment not found.") if appointment["status"] == "canceled": raise HTTPException(status_code=400, detail="Cannot modify a canceled appointment.") def calculate_appointment_duration(services): """ Calculates the total duration of the appointment based on its services. Args: services (list): A list of services in the appointment. Returns: int: Total duration in minutes. """ return sum(service.get("duration", 0) for service in services) def to_in_appointments_db(appointment: AppointmentCreateRequest): return { "appointment_id": appointment.appointment_id, "customer_id": appointment.customer_id, # You will update later when customer is added "merchant_id": appointment.merchant_id, "merchant_name": appointment.merchant_name, "city": appointment.city, "location_id": appointment.location_id, # Extracted from merchant_address "address_street": appointment.merchant_address.street, "address_area": appointment.merchant_address.area, "address_in_tcode": appointment.merchant_address.postcode, "address_state": appointment.merchant_address.state, "geo_location": ( f"POINT({appointment.merchant_address.location.coordinates[0]} " f"{appointment.merchant_address.location.coordinates[1]})" if appointment.merchant_address.location else None ), "appointment_date": appointment.appointment_date, "appointment_time": appointment.appointment_time, "status": appointment.status, "total_amount": appointment.total_amount, "discount_amount": appointment.discount, "cleared_amount": appointment.cleared_amount, "payment_status": appointment.payment_status, "payment_mode": appointment.payment_mode, "notes": appointment.notes, } def to_in_appointments_services_db(appointment_services, appointment_id): services_data = [] for service in appointment_services: services_data.append({ "appointment_service_id":uuid.uuid4(), "appointment_id": appointment_id, "service_id": service.service_id, "service_name": service.name, "duration_minutes": service.duration, "unit_price": service.price, "quantity": service.quantity, "line_total": service.price * service.quantity, "associate_id": service.associate_id, "associate_name": service.associate_name, }) return services_data