feat(Packagers): template for interviews done, rest of code is hooked up to new template

This commit is contained in:
darkicewolf50 2024-12-14 15:14:17 -07:00
parent 27330a8a59
commit 99bfecce23
12 changed files with 88 additions and 66 deletions

Binary file not shown.

View File

@ -5,7 +5,7 @@ from openpyxl.styles import Font, Border, Side, PatternFill
from openpyxl.formatting.rule import FormulaRule from openpyxl.formatting.rule import FormulaRule
def NoSheet(): def NoSheet(file_path):
""" """
Creates the Template for more data to be added Creates the Template for more data to be added
@ -41,7 +41,7 @@ Recruitment Responses:
- Where did you hear about us?: Testing - Where did you hear about us?: Testing
- Are you available for team meetings/work days? Saturdays 10 am - 4 pm: "No" #add condiftional formatting for no to make whole line red - Are you available for team meetings/work days? Saturdays 10 am - 4 pm: "No" #add condiftional formatting for no to make whole line red
Interview TimeTable: Interview TimeTable:
- Date: 9/16/2024 - Date: 2024-09-16
- Meeting Duration: 30 min - Meeting Duration: 30 min
- Start Time Slot: 10:00:00 AM - Start Time Slot: 10:00:00 AM
- Slot: 1 - Slot: 1
@ -56,14 +56,15 @@ Data Helper And Info:
- Done - Done
- No Show - No Show
- Cancelled/Moved - Cancelled/Moved
- First time Startup: Move docker volume pointer to new dirve and start pu container - First time Startup: Move docker volume pointer to new drive and start up container
- Weird Date: Add more space and it will change from ### to a date
- How to Add Dropdown: Go into data, click data validation, select list then select the area you want to get values from in the formula spot - How to Add Dropdown: Go into data, click data validation, select list then select the area you want to get values from in the formula spot
""" """
# uses the base above "yaml file" to create the base template # uses the base above "yaml file" to create the base template
yamlsheet = yaml.safe_load(yamlraw) yamlsheet = yaml.safe_load(yamlraw)
year_donation = int(str(datetime.datetime.now().year)[2:]) # gets the last two digits of the current year then adds 1 for the current season year_donation = int(str(datetime.datetime.now().year)[2:]) + 1 # gets the last two digits of the current year then adds 1 for the current season
file_name = f"./OR{year_donation + 1}-L-Interview Data.xlsx" # name based off the 2025 naming system file_name = file_path
# border style # border style
border = Border( # defualt behaviour is thin border = Border( # defualt behaviour is thin
@ -100,6 +101,7 @@ Data Helper And Info:
example_data = [list(data.values())[0] for data in title_list] example_data = [list(data.values())[0] for data in title_list]
for col_num, data in enumerate(example_data, start=1): for col_num, data in enumerate(example_data, start=1):
# for special case Data Helper where there a list in a dictionary # for special case Data Helper where there a list in a dictionary
if isinstance(data, list): if isinstance(data, list):
row_num = 2 row_num = 2
@ -113,6 +115,12 @@ Data Helper And Info:
# changes the Dropdown data in status to unknown instead of the other option, only there for prep for a dropdown # changes the Dropdown data in status to unknown instead of the other option, only there for prep for a dropdown
if data == "Dropdown (Options in datahelp)": if data == "Dropdown (Options in datahelp)":
cell.value = "Unknown" cell.value = "Unknown"
elif isinstance(data, datetime.date):
cell.value = data
# Convert the example date '2024-09-16' to a datetime object
# Set the number format to 'YYYY-MM-DD'
cell.number_format = 'yyyy-mmm-d'
else: else:
cell.value = data cell.value = data
@ -126,4 +134,8 @@ Data Helper And Info:
print(f"Created {file_name} for {year_donation}") print(f"Created {file_name} for {year_donation}")
if __name__ == "__main__": if __name__ == "__main__":
NoSheet() year_donation = int(str(datetime.datetime.now().year)[2:]) + 1 # gets the last two digits of the current year then adds 1 for the current season
# name based off the 2025 naming system
# Define the path to the Excel file and the lock file
file_name = f"./Interviews/OR{year_donation}-L-Interview Data.xlsx"
NoSheet(file_name)

Binary file not shown.

View File

@ -11,11 +11,11 @@ TODO change to use new tempate
TODO change names to be more clear TODO change names to be more clear
""" """
def ReadDatabase(): def ReadDatabase(file_path):
""" """
Reads the database for which slots are available Reads the database for which slots are available
``REQUIRES``: ``None`` ``REQUIRES``: ``File_Path`` where the file is
``PROMISES``: ``JSON`` Interview Available Slots ``PROMISES``: ``JSON`` Interview Available Slots
@ -25,11 +25,10 @@ def ReadDatabase():
""" """
year_donation = int(str(datetime.datetime.now().year)[2:]) + 1 # gets the last two digits of the current year then adds 1 for the current season
# name based off the 2025 naming system
# Define the path to the Excel file and the lock file # Define the path to the Excel file and the lock file
excel_file_path = f"OR{year_donation}-L-Interview Data.xlsx" excel_file_path = file_path
lock_file_path = f"OR{year_donation}-L-Interview Data.xlsx.lock" lock_file_path = file_path + ".lock"
# Retry parameters # Retry parameters
max_retries = 60 # Maximum number of retries if the file is locked max_retries = 60 # Maximum number of retries if the file is locked
@ -41,7 +40,7 @@ def ReadDatabase():
# Attempt to acquire a shared read (non-blocking) access # Attempt to acquire a shared read (non-blocking) access
with FileLock(lock_file_path, timeout=0): # Non-blocking, checks if the lock exists with FileLock(lock_file_path, timeout=0): # Non-blocking, checks if the lock exists
# Load the Excel file into a pandas DataFrame # Load the Excel file into a pandas DataFrame
df = pd.read_excel(excel_file_path) df = pd.read_excel(excel_file_path, sheet_name="Interview TimeTable")
# Initialize the dictionary to store the structured data # Initialize the dictionary to store the structured data
interview_data = {} interview_data = {}
@ -49,11 +48,11 @@ def ReadDatabase():
# Group the DataFrame by Date, Start Time, and Slot for organization # Group the DataFrame by Date, Start Time, and Slot for organization
for _, row in df.iterrows(): for _, row in df.iterrows():
date = str(row['Date']) date = str(row['Date'])
start_time = str(row['Start Time']) start_time = str(row['Start Time Slot'])
slot = int(row['Slot']) if not pd.isna(row['Slot']) else 0 slot = int(row['Slot']) if not pd.isna(row['Slot']) else 0
# Returns the number of interviewees in the slot; returns 0 if empty # Returns the number of interviewees in the slot; returns 0 if empty
interviewee_amount = len(str(row['Interviewee Name']).split()) if str(row['Interviewee Name']) != "nan" else 0 interviewee_amount = len(str(row['Interviewee Name (What to call them)']).split()) if str(row['Interviewee Name (What to call them)']) != "nan" else 0
# Check if the slot is available for an interviewee to attend # Check if the slot is available for an interviewee to attend
available_slots = interviewee_amount != slot available_slots = interviewee_amount != slot
@ -77,10 +76,16 @@ def ReadDatabase():
# If max retries are exceeded, raise an error # If max retries are exceeded, raise an error
raise RuntimeError("Unable to access the database after multiple attempts due to a file lock.") raise RuntimeError("Unable to access the database after multiple attempts due to a file lock.")
# Example usage of the ReadDatabase function # Example usage of the ReadDatabase function
if __name__ == "__main__": if __name__ == "__main__":
import datetime
year_donation = int(str(datetime.datetime.now().year)[2:]) + 1 # gets the last two digits of the current year then adds 1 for the current season
# name based off the 2025 naming system
# Define the path to the Excel file and the lock file
file_name = f"./Interviews/OR{year_donation}-L-Interview Data.xlsx"
try: try:
data = ReadDatabase() data = ReadDatabase(file_name)
print(json.dumps(data, indent=4)) print(json.dumps(data, indent=4))
except RuntimeError as e: except RuntimeError as e:
print(e) print(e)

View File

@ -4,18 +4,17 @@ from openpyxl import load_workbook
from send_email import send_email from send_email import send_email
from filelock import FileLock from filelock import FileLock
import datetime
""" """
TODO make it work with the new template TODO make it work with the new template
TODO update names to be more clear TODO update names to be more clear
TODO try to remove pandas TODO try to remove pandas
""" """
def ReadDatabase(): def ReadDatabase(file_path, lock_path):
""" """
Reads the Database to retrieve available interview slots Reads the Database to retrieve available interview slots
``REQUIRES``: None ``REQUIRES``: ``File_Path`` ``Lock_Path`` where the file and lock are located
``PROMISES``: JSON (Available interview slots) ``PROMISES``: JSON (Available interview slots)
@ -24,15 +23,10 @@ def ReadDatabase():
``Contact``: ahmad.ahmad1@ucalgary.ca, darkicewolf50@gmail.com ``Contact``: ahmad.ahmad1@ucalgary.ca, darkicewolf50@gmail.com
""" """
year_donation = int(str(datetime.datetime.now().year)[2:]) + 1 # gets the last two digits of the current year then adds 1 for the current season
# name based off the 2025 naming system
file_path = f"OR{year_donation}-L-Interview Data.xlsx"
lock_path = f"OR{year_donation}-L-Interview Data.xlsx.lock"
# Use a file-based lock for thread-safe and process-safe access # Use a file-based lock for thread-safe and process-safe access
with FileLock(lock_path): with FileLock(lock_path):
# Load the Excel file into a pandas DataFrame with specific columns # Load the Excel file into a pandas DataFrame with specific columns
df = pd.read_excel(file_path, usecols=['Date', 'Start Time', 'Slot', 'Interviewee Name', 'Interviewee Email', 'Meeting Duration']) df = pd.read_excel(file_path, usecols=['Date', 'Start Time Slot', 'Slot', 'Interviewee Name (What to call them)', 'Interviewee Email', 'Meeting Duration'], sheet_name="Interview TimeTable")
# Initialize the dictionary to store structured data for available slots # Initialize the dictionary to store structured data for available slots
interview_data = {} interview_data = {}
@ -41,17 +35,19 @@ def ReadDatabase():
for _, row in df.iterrows(): for _, row in df.iterrows():
# Convert Date and Start Time to string format for easier comparison # Convert Date and Start Time to string format for easier comparison
date = str(row['Date']).split(" ")[0] # Format date to YYYY-MM-DD date = str(row['Date']).split(" ")[0] # Format date to YYYY-MM-DD
start_time = str(row['Start Time']) # print(date)
start_time = str(row['Start Time Slot'])
# Calculate the slot capacity and current number of interviewees # Calculate the slot capacity and current number of interviewees
slot_capacity = int(row['Slot']) if not pd.isna(row['Slot']) else 0 slot_capacity = int(row['Slot']) if not pd.isna(row['Slot']) else 0
interviewee_names = [name.strip() for name in str(row['Interviewee Name']).split(',') if name.strip()] interviewee_names = [name.strip() for name in str(row['Interviewee Name (What to call them)']).split(',') if name.strip()]
interviewee_count = len(interviewee_names) if interviewee_names != ["nan"] else 0 interviewee_count = len(interviewee_names) if interviewee_names != ["nan"] else 0
# Check if there are available slots for more interviewees # Check if there are available slots for more interviewees
if interviewee_count < slot_capacity: if interviewee_count < slot_capacity:
# Organize data by date and time, keeping track of available slots and meeting duration # Organize data by date and time, keeping track of available slots and meeting duration
if date not in interview_data: if date not in interview_data:
# print(date)
interview_data[date] = {} interview_data[date] = {}
interview_data[date][start_time] = { interview_data[date][start_time] = {
'Meeting Duration': row['Meeting Duration'], 'Meeting Duration': row['Meeting Duration'],
@ -60,11 +56,11 @@ def ReadDatabase():
return interview_data return interview_data
def AppendAppointment(date, start_time, interviewee_name, interviewee_email): def AppendAppointment(file_path, date, start_time, interviewee_name, interviewee_email):
""" """
Appends a new appointment with the interviewee's name and email if the slot is available. Appends a new appointment with the interviewee's name and email if the slot is available.
``REQUIRES``: ``str`` date, ``str`` start_time, ``str`` interviewee_name, ``str`` interviewee_email ``REQUIRES``: ``File_Path`` ``str`` date, ``str`` start_time, ``str`` interviewee_name, ``str`` interviewee_email
``PROMISES``: ``None`` Updates the Excel file with the new interviewee's name and email if there is an available slot. Returns Bool. ``PROMISES``: ``None`` Updates the Excel file with the new interviewee's name and email if there is an available slot. Returns Bool.
@ -72,45 +68,47 @@ def AppendAppointment(date, start_time, interviewee_name, interviewee_email):
``Contact``: ahmad.ahmad1@ucalgary.ca, darkicewolf50@gmail.com ``Contact``: ahmad.ahmad1@ucalgary.ca, darkicewolf50@gmail.com
""" """
# print(f"{file_path}\n{date}\n{start_time}\n{interviewee_name}\n{interviewee_email}")
lock_path = file_path + ".lock"
year_donation = int(str(datetime.datetime.now().year)[2:]) + 1 # gets the last two digits of the current year then adds 1 for the current season available_slots = ReadDatabase(file_path, lock_path)
# name based off the 2025 naming system print(date)
file_path = f"OR{year_donation}-L-Interview Data.xlsx" print(available_slots)
lock_path = f"OR{year_donation}-L-Interview Data.xlsx.lock" print(date in available_slots)
available_slots = ReadDatabase()
# Check if the requested slot is available in the `available_slots` structure # Check if the requested slot is available in the `available_slots` structure
if date in available_slots and start_time in available_slots[date]: if date in available_slots and start_time in available_slots[date]:
with FileLock(lock_path): # Ensure process-safe access to the file with FileLock(lock_path): # Ensure process-safe access to the file
# Load workbook and select "Sheet1" for updating appointments # Load workbook and select "Interview TimeTable" for updating appointments
workbook = load_workbook(file_path) workbook = load_workbook(file_path)
sheet = workbook["Interview Timetable"] sheet = workbook["Interview TimeTable"]
df = pd.read_excel(file_path) df = pd.read_excel(file_path, sheet_name="Interview TimeTable")
# Find and update the row that matches the provided date and start time # Find and update the row that matches the provided date and start time
for index, row in df.iterrows(): for index, row in df.iterrows():
row_date = str(row['Date']).split(" ")[0] row_date = str(row['Date']).split(" ")[0]
row_start_time = str(row['Start Time']) row_start_time = str(row['Start Time Slot'])
if row_date == date and row_start_time == start_time: if row_date == date and row_start_time == start_time:
# Current entries for names and emails, and append new data with comma and space # Current entries for names and emails, and append new data with comma and space
current_names = str(row['Interviewee Name']).strip() current_names = str(row['Interviewee Name (What to call them)']).strip()
current_emails = str(row['Interviewee Email']).strip() current_emails = str(row['Interviewee Email']).strip()
updated_names = f"{current_names}, {interviewee_name}" if current_names != "nan" else interviewee_name updated_names = f"{current_names}, {interviewee_name}" if current_names != "nan" else interviewee_name
updated_emails = f"{current_emails}, {interviewee_email}" if current_emails != "nan" else interviewee_email updated_emails = f"{current_emails}, {interviewee_email}" if current_emails != "nan" else interviewee_email
# Update the cells with new names and emails # Update the cells with new names and emails
name_cell = sheet.cell(row=index + 2, column=df.columns.get_loc('Interviewee Name') + 1) name_cell = sheet.cell(row=index + 2, column=df.columns.get_loc('Interviewee Name (What to call them)') + 1)
email_cell = sheet.cell(row=index + 2, column=df.columns.get_loc('Interviewee Email') + 1) email_cell = sheet.cell(row=index + 2, column=df.columns.get_loc('Interviewee Email') + 1)
name_cell.value = updated_names name_cell.value = updated_names
email_cell.value = updated_emails email_cell.value = updated_emails
status_cell = sheet.cell(row=index + 2, column=df.columns.get_loc('Status') + 1)
status_cell.value = "Unknown"
workbook.save(file_path) workbook.save(file_path)
send_email(interviewee_email, interviewee_name, date, start_time) send_email(interviewee_email, interviewee_name, date, start_time)
return True return True
print("False")
# If no slots available, return that the slot is unavailable # If no slots available, return that the slot is unavailable
return False return False
@ -123,33 +121,40 @@ def run_tests():
``PROMISES``: Prints test outcomes to validate successful booking or slot unavailability. ``PROMISES``: Prints test outcomes to validate successful booking or slot unavailability.
""" """
import datetime
year_donation = int(str(datetime.datetime.now().year)[2:]) + 1 # gets the last two digits of the current year then adds 1 for the current season
# name based off the 2025 naming system
# Define the path to the Excel file and the lock file
file_name = f"./Interviews/OR{year_donation}-L-Interview Data.xlsx"
lock = file_name + ".lock"
print("Available Slots:") print("Available Slots:")
available_slots = ReadDatabase() available_slots = ReadDatabase(file_path=file_name, lock_path=lock)
print(json.dumps(available_slots, indent=4)) print(json.dumps(available_slots, indent=4))
# Test Case 1: Append to an available slot on 2024-09-16 at 10:30:00 # Test Case 1: Append to an available slot on 2024-09-16 at 10:30:00
date_1 = "2024-09-16" date_1 = "9/16/2024"
start_time_1 = "10:30:00" start_time_1 = "10:30:00"
interviewee_name_1 = "Alice Johnson" interviewee_name_1 = "Alice Johnson"
interviewee_email_1 = "ahmadmuhammadofficial@gmail.com" interviewee_email_1 = "ahmadmuhammadofficial@gmail.com"
print(f"\nTest Case 1: Trying to book {date_1} at {start_time_1} for {interviewee_name_1} ({interviewee_email_1})") print(f"\nTest Case 1: Trying to book {date_1} at {start_time_1} for {interviewee_name_1} ({interviewee_email_1})")
AppendAppointment(date_1, start_time_1, interviewee_name_1, interviewee_email_1) AppendAppointment(file_name, date_1, start_time_1, interviewee_name_1, interviewee_email_1)
# Test Case 2: Append to an available slot on 2024-09-17 at 13:30:00 # Test Case 2: Append to an available slot on 2024-09-17 at 13:30:00
date_2 = "2024-09-17" date_2 = "9/17/2024"
start_time_2 = "13:30:00" start_time_2 = "13:30:00"
interviewee_name_2 = "Bob Smith" interviewee_name_2 = "Bob Smith"
interviewee_email_2 = "bob.smith@example.com" interviewee_email_2 = "bob.smith@example.com"
print(f"\nTest Case 2: Trying to book {date_2} at {start_time_2} for {interviewee_name_2} ({interviewee_email_2})") print(f"\nTest Case 2: Trying to book {date_2} at {start_time_2} for {interviewee_name_2} ({interviewee_email_2})")
AppendAppointment(date_2, start_time_2, interviewee_name_2, interviewee_email_2) AppendAppointment(file_name, date_2, start_time_2, interviewee_name_2, interviewee_email_2)
# Test Case 3: Attempting to book at 10:30:00 on 2024-09-16 for a different interviewee # Test Case 3: Attempting to book at 10:30:00 on 2024-09-16 for a different interviewee
date_3 = "2024-09-16" date_3 = "9/16/2024"
start_time_3 = "10:30:00" start_time_3 = "10:30:00"
interviewee_name_3 = "Charlie Brown" interviewee_name_3 = "Charlie Brown"
interviewee_email_3 = "charlie.brown@example.com" interviewee_email_3 = "charlie.brown@example.com"
print(f"\nTest Case 3: Trying to book {date_3} at {start_time_3} for {interviewee_name_3} ({interviewee_email_3})") print(f"\nTest Case 3: Trying to book {date_3} at {start_time_3} for {interviewee_name_3} ({interviewee_email_3})")
AppendAppointment(date_3, start_time_3, interviewee_name_3, interviewee_email_3) AppendAppointment(file_name, date_3, start_time_3, interviewee_name_3, interviewee_email_3)
# Run tests # Run tests
if __name__ == "__main__": if __name__ == "__main__":

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,11 +1,11 @@
from ReadDB import ReadDatabase from ReadDB import ReadDatabase
def getSchedulePackager(): def getSchedulePackager(file_name):
""" """
Packages up the response for a http response Packages up the response for a http response
``REQUIRES``: None ``REQUIRES``: ``File_Path`` where the file is
``PROMISES``: ``JSON`` http response ready ``PROMISES``: ``JSON`` http response ready
@ -15,18 +15,18 @@ def getSchedulePackager():
""" """
return { return {
"interviewDates": ReadDatabase() "interviewDates": ReadDatabase(file_path=file_name)
} }
from WriteDB import AppendAppointment from WriteDB import AppendAppointment
from email_validator import validate_email, EmailNotValidError from email_validator import validate_email, EmailNotValidError
def SelectAppointment (appointmentJson): def SelectAppointment (file_name, appointmentJson):
""" """
Packages up a response for a http request Packages up a response for a http request
``REQUIRES``: ``JSON`` with the data of interviewee name, date, starttime and interviewee email ``REQUIRES``: ``File_Path`` ``JSON`` where the file is, json has the data of interviewee name, date, starttime and interviewee email
``PROMISES``: ``JSON`` Returns if the booking was a success ``PROMISES``: ``JSON`` Returns if the booking was a success
@ -39,15 +39,15 @@ def SelectAppointment (appointmentJson):
Example of an incoming http post body Example of an incoming http post body
{ {
"intervieweeName": "Brock", "intervieweeName": "Brock",
"date": "2024-09-16", "date": "9/16/2024",
"startTime": "10:30:00", "startTime": "11:00:00",
"intervieweeEmail": "darkicewolf50@gmail.com" "intervieweeEmail": "darkicewolf50@gmail.com"
} }
""" """
try: try:
validEmail = validate_email(appointmentJson["intervieweeEmail"], check_deliverability=True) validEmail = validate_email(appointmentJson["intervieweeEmail"], check_deliverability=True)
if validEmail: if validEmail:
status = AppendAppointment(date=appointmentJson["date"], start_time=appointmentJson["startTime"], interviewee_name=appointmentJson["intervieweeName"], interviewee_email=appointmentJson["intervieweeEmail"]) status = AppendAppointment(file_path=file_name, date=appointmentJson["date"], start_time=appointmentJson["startTime"], interviewee_name=appointmentJson["intervieweeName"], interviewee_email=appointmentJson["intervieweeEmail"])
if status: if status:
resBody = {"Success": True, "validEmail": "true"} resBody = {"Success": True, "validEmail": "true"}

12
main.py
View File

@ -9,9 +9,9 @@ import os
year_donation = int(str(datetime.datetime.now().year)[2:]) + 1 # gets the last two digits of the current year then adds 1 for the current season year_donation = int(str(datetime.datetime.now().year)[2:]) + 1 # gets the last two digits of the current year then adds 1 for the current season
# name based off the 2025 naming system # name based off the 2025 naming system
# Define the path to the Excel file and the lock file # Define the path to the Excel file and the lock file
file_name = f"OR{year_donation}-L-Interview Data.xlsx" file_name = f"./Interviews/OR{year_donation}-L-Interview Data.xlsx"
if not os.path.isfile(file_name): if not os.path.isfile(file_name):
NoSheet() NoSheet(file_name)
app = FastAPI() app = FastAPI()
@ -52,7 +52,7 @@ async def getAppointments():
""" """
checks for all available slots in the database checks for all available slots in the database
``REQUIRES``: ```None`` Nothing ``REQUIRES``: ``None`` Nothing
``PROMISES``: ``JSON`` returns all of the avaialbe slots by date then time ``PROMISES``: ``JSON`` returns all of the avaialbe slots by date then time
@ -62,7 +62,7 @@ async def getAppointments():
""" """
res = getSchedulePackager() res = getSchedulePackager(file_name)
return JSONResponse( return JSONResponse(
headers={ headers={
@ -101,7 +101,7 @@ async def postSelectInterview(rawRequest: Appointment):
""" """
Books an interview, first checks if the slot is valid Books an interview, first checks if the slot is valid
``REQUIRES``: ```Appointment`` A specifically formatted request ``REQUIRES``: ``Appointment`` A specifically formatted request
``PROMISES``: ``JSON`` returns if the booking was successful or not ``PROMISES``: ``JSON`` returns if the booking was successful or not
@ -112,7 +112,7 @@ async def postSelectInterview(rawRequest: Appointment):
""" """
requestDict = {key: str(value) for key, value in rawRequest.dict().items()} requestDict = {key: str(value) for key, value in rawRequest.dict().items()}
res = SelectAppointment(requestDict) res = SelectAppointment(file_name, requestDict)
return JSONResponse( return JSONResponse(
headers={ headers={