sabrebooking / sabrebackend / sabre / sabreapi / utils.py
utils.py
Raw
from .CONSTANTS import UPPERCASE, LOWERCASE, DIGITS

from .serializers import BookingSerializer, CateringSerializer
from .models import Room, AvailableMeal, Catering
from django.core.mail import EmailMessage
import os
from email.message import EmailMessage
import ssl
import smtplib
from datetime import datetime


GMAIL_ADMIN_USER = os.getenv("GMAIL_ADMIN_USER")
ADMIN_PASSWORD = os.getenv("GMAIL_ADMIN_PASSWORD")

# Utility function



def get_booking_details(data):
    return {
        "user": None,
        "is_approved": False,
        "is_recurring": False,
        "is_cancelled": False,
        "event_duration": data["eventDuration"],
        "number_of_attendees": data["numberOfDelegates"],
        "room": data["room"],
        "event_type": data["eventType"],
        "event_date": data["eventDate"],
        "event_start": data["eventStartTime"],
        "event_end": data["eventEndTime"],
        "company_name": data["companyName"]
    }

# To be removed
def get_catering_details(data):
    # TODO:
    return {
        "user": None,
        "chosen_meal": data["chosenMeal"],
        "catering_package": data["cateringPackage"],
        "meal_time": data["teaMealBreakTime"],
        "special_dietary_requirements": data["specialDietaryRequirements"],
        "bar_requirements": data["barRequirements"],
        "served": data["served"],
    }

def send_confirmation_email(user):
    """Sends a confirmation email to the user"""
    subject = "[Sabre] Booking succesfully submitted"
    body= f"""Hi, {user.first_name}
        
Thank you for submitting your booking with Sabre. This email serves to inform you that we have received the request and we will be sending a confirmation soon!
Please remember to pay as soon as possible for us to confirm your booking.

Best,
Sabre Admin Team.
    """

    em = EmailMessage()
    em['From'] = GMAIL_ADMIN_USER
    em['To'] = user.email
    em['Subject'] = subject
    em.set_content(body)
    context = ssl.create_default_context()

    with smtplib.SMTP_SSL('smtp.gmail.com', 465, context=context) as smtp:
        try:
            smtp.login(GMAIL_ADMIN_USER, ADMIN_PASSWORD)
            smtp.sendmail(GMAIL_ADMIN_USER, user.email, em.as_string())
        except Exception as e:
            return {"error": str(e)}
    return {"success": True}

def send_approval_email(user):
    """Sends a booking approval email to the user"""
    subject = "[Sabre] Booking Approved by admin."
    body = f"""Hi, {user.first_name}
        
This email is to let you know that your booking has been approved by the admin. Hooray! Please check out your calendar to accept the invitation and you should be all set.

Best,
Sabre Admin Team.
    """

    em = EmailMessage()
    em['From'] = GMAIL_ADMIN_USER
    em['To'] = user.email
    em['Subject'] = subject
    em.set_content(body)
    context = ssl.create_default_context()

    with smtplib.SMTP_SSL('smtp.gmail.com', 465, context=context) as smtp:
        try:
            smtp.login(GMAIL_ADMIN_USER, ADMIN_PASSWORD)
            smtp.sendmail(GMAIL_ADMIN_USER, user.email, em.as_string())
        except Exception as e:
            return {"error": str(e)}
    return {"success": True}





def get_feedback_details(data):
    """
    Extracts feedback details from the input json.

    Parameters:
    - data (dict): Dictionary with user's rating, recommendation score, and additional feedback details.

    Returns:
    - dict: Feedback details.
    """
    return {
        "rating": data["rating"],
        "recommendation": data["recommendation"],
        "comments": data["comments"]
        # "user": user
    }





def save_booking_details(data, user_id):
    """
    Validates and saves booking details associated with a user to the database.

    Args:
        data (dict): Raw booking data.
        user_id (int): ID of the user associated with the booking.

    Returns:
        dict: A dictionary indicating operation success and either the booking object or errors.
    """
    # Handle booking data
    booking_data = get_booking_details(data)
    # catering_data = get_catering_details(data)
    # catering_serializer = CateringSerializer(data=catering_data)

    # if catering_serializer.is_valid():
    #     catering_instance = catering_serializer.save()
    # else:
    #     return {"success": False, "errors": catering_serializer.errors}
    room = Room.objects.get(name=booking_data["room"])
    # associate a booking with the user
    booking_data["user"] = user_id
    booking_data["room"] = room.id
    # booking_data["catering"] = catering_instance.id                             PAY ATTENTION TO LINE 90.
    print(GMAIL_ADMIN_USER)
    print(ADMIN_PASSWORD)
    booking_serializer = BookingSerializer(data=booking_data)
    # save the booking is the serialized request is valid
   
    if booking_serializer.is_valid():
        saved_booking = booking_serializer.save()
        booking_id = saved_booking.id
        return {"success": True, "booking": saved_booking, "booking_id": booking_id}
    # return an error if serialized request is not valid
    return {"success": False, "errors": booking_serializer.errors}

def save_catering_details(data, user_id):
    """
    Validates and saves catering details associated with a user to the database.

    Args:
        data (dict): Raw catering data.
        user_id (int): ID of the user associated with the catering.

    Returns:
        dict: A dictionary indicating operation success and either the catering object or errors.
    """

    # catering_data = get_catering_details(data)

    # get a list of meal ids
    meal_IDs = []
    for meal in data:
        meal_IDs.append(meal[id])
    
    meals = AvailableMeal.objects.filter(id__in=meal_IDs)

    # create a catering object
    catering = Catering.objects.create(user=user_id)

    # save meals to catering meals field
    catering.meals.set(meals)

    #Get the id's of foreign fields.
    # chosen_meal = AvailableMeal.objects.filter(id=catering_data["chosen_meal"])
    catering["user"] = user_id 
    catering["special_dietary_requirements"] = data["special_dietary_requirements"]
    catering["bar_requirements"] = data["bar_requirements"]

    catering_serializer = CateringSerializer(catering) 

    if catering_serializer.is_valid():
        return {"success": True, "catering": catering_serializer.save()}
    else:
        return {"success": False, "errors": catering_serializer.errors}
    




def get_available_rooms(date, event_start, event_end, number_of_attendees):
    """
    Returns a list of available rooms based on given date, start and end times, and the number of attendees.

    Parameters:
    - date (str): The date for the event in the format 'YYYY-MM-DD'.
    - event_start (str): The start time for the event in the format 'HH:MM'.
    - event_end (str): The end time for the event in the format 'HH:MM'.
    - number_of_attendees (int/str): The number of attendees for the event.

    Returns:
    - list: A list of room names that are available for the given criteria.

    Note:
    This function assumes the existence of a Room model where each room has a capacity and associated bookings.
    """
    # Convert the date string to a datetime.date object
    date = datetime.strptime(date, '%Y-%m-%d').date()
    # Convert the start and end time strings to datetime.time objects
    start = datetime.strptime(event_start, '%H:%M').time()
    end = datetime.strptime(event_end, '%H:%M').time()

    # Fetch all rooms ordered by their capacity
    rooms = set(Room.objects.all().order_by('capacity'))

    for room in list(rooms): # Loop through the list of rooms.

        # number of attendees exceed capacity, skip room.
        if room.capacity < int(number_of_attendees):
            rooms.discard(room)
            continue

        # confirm no bookings conflict with the proposed time.
        found_conflict = False
        for booking in room.bookings.all():
            found_conflict = False
            if booking.event_date != date:
                continue

            if not (start >= booking.event_end or end <= booking.event_start):
                found_conflict = True
                break
        
        # check if any of the bookings caused a conflict.
        if found_conflict:
            rooms.discard(room)
            continue
        return [[room.id, room.name, room.capacity] for room in rooms]
    

                


    # for booking in room.bookings.all(): # Go over all the bookings associated with this room.
    #     if booking.event_date != date: # If the booking date is not the same as our target date, ignore that booking.
    #         continue
    #     # Remove the room from the set if there is a conflict.
    #     if not (start >= booking.event_end or end <= booking.event_start): 
    #         rooms.discard(room)