mirror of
https://github.com/UofCBaja/Interview-Backend.git
synced 2025-06-15 13:24:19 -06:00
fix(Packagers): changed to first time run
This commit is contained in:
parent
a9460dc692
commit
49313e8450
Binary file not shown.
85
ReadDB.py
85
ReadDB.py
@ -5,12 +5,10 @@ import time
|
|||||||
|
|
||||||
from NoSheet import NoSheet
|
from NoSheet import NoSheet
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
TODO update to possibly not use pandas and update to use the new template
|
TODO change to use new tempate
|
||||||
TODO update name of function to be more clear
|
TODO change names to be more clear
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def ReadDatabase():
|
def ReadDatabase():
|
||||||
@ -33,54 +31,51 @@ def ReadDatabase():
|
|||||||
excel_file_path = f"OR{year_donation}-L-Interview Data.xlsx"
|
excel_file_path = f"OR{year_donation}-L-Interview Data.xlsx"
|
||||||
lock_file_path = f"OR{year_donation}-L-Interview Data.xlsx.lock"
|
lock_file_path = f"OR{year_donation}-L-Interview Data.xlsx.lock"
|
||||||
|
|
||||||
if not (os.path.isfile(excel_file_path) or os.path.isfile(lock_file_path)):
|
# Retry parameters
|
||||||
NoSheet()
|
max_retries = 60 # Maximum number of retries if the file is locked
|
||||||
else:
|
retry_interval = 0.5 # Wait time (in seconds) between retries
|
||||||
# Retry parameters
|
|
||||||
max_retries = 60 # Maximum number of retries if the file is locked
|
|
||||||
retry_interval = 0.5 # Wait time (in seconds) between retries
|
|
||||||
|
|
||||||
retries = 0
|
retries = 0
|
||||||
while retries < max_retries:
|
while retries < max_retries:
|
||||||
try:
|
try:
|
||||||
# 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)
|
||||||
|
|
||||||
# Initialize the dictionary to store the structured data
|
# Initialize the dictionary to store the structured data
|
||||||
interview_data = {}
|
interview_data = {}
|
||||||
|
|
||||||
# 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 = 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']).split()) if str(row['Interviewee Name']) != "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
|
||||||
if available_slots:
|
if available_slots:
|
||||||
# Initialize nested structure if not present
|
# Initialize nested structure if not present
|
||||||
if date not in interview_data:
|
if date not in interview_data:
|
||||||
interview_data[date] = {}
|
interview_data[date] = {}
|
||||||
# Add the start time and duration if not present
|
# Add the start time and duration if not present
|
||||||
if start_time not in interview_data[date]:
|
if start_time not in interview_data[date]:
|
||||||
interview_data[date][start_time] = {
|
interview_data[date][start_time] = {
|
||||||
'Meeting Duration': row['Meeting Duration'],
|
'Meeting Duration': row['Meeting Duration'],
|
||||||
}
|
}
|
||||||
return interview_data # Successfully read the database
|
return interview_data # Successfully read the database
|
||||||
|
|
||||||
except Timeout:
|
except Timeout:
|
||||||
# File is locked; wait and retry
|
# File is locked; wait and retry
|
||||||
retries += 1
|
retries += 1
|
||||||
print(f"File is locked, retrying ({retries}/{max_retries})...")
|
print(f"File is locked, retrying ({retries}/{max_retries})...")
|
||||||
time.sleep(retry_interval)
|
time.sleep(retry_interval)
|
||||||
|
|
||||||
# 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__":
|
||||||
|
127
WriteDB.py
127
WriteDB.py
@ -4,15 +4,13 @@ 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
|
||||||
|
|
||||||
from NoSheet import NoSheet
|
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
TODO update to possibly not use pandas and update to use the new template
|
TODO make it work with the new template
|
||||||
TODO update name of functions to be more clear
|
TODO update names to be more clear
|
||||||
|
TODO try to remove pandas
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def ReadDatabase():
|
def ReadDatabase():
|
||||||
"""
|
"""
|
||||||
Reads the Database to retrieve available interview slots
|
Reads the Database to retrieve available interview slots
|
||||||
@ -30,41 +28,37 @@ def ReadDatabase():
|
|||||||
# name based off the 2025 naming system
|
# name based off the 2025 naming system
|
||||||
file_path = f"OR{year_donation}-L-Interview Data.xlsx"
|
file_path = f"OR{year_donation}-L-Interview Data.xlsx"
|
||||||
lock_path = f"OR{year_donation}-L-Interview Data.xlsx.lock"
|
lock_path = f"OR{year_donation}-L-Interview Data.xlsx.lock"
|
||||||
|
|
||||||
|
# Use a file-based lock for thread-safe and process-safe access
|
||||||
|
with FileLock(lock_path):
|
||||||
|
# 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'])
|
||||||
|
|
||||||
# checks for if the file exisits for the year otherwise it will create one
|
# Initialize the dictionary to store structured data for available slots
|
||||||
if not (os.path.isfile(file_path) or os.path.isfile(lock_path)):
|
|
||||||
NoSheet()
|
|
||||||
else:
|
|
||||||
# Use a file-based lock for thread-safe and process-safe access
|
|
||||||
with FileLock(lock_path):
|
|
||||||
# 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'])
|
|
||||||
|
|
||||||
# Initialize the dictionary to store structured data for available slots
|
|
||||||
interview_data = {}
|
interview_data = {}
|
||||||
|
|
||||||
# Process each row in the DataFrame to structure data by date and time
|
# Process each row in the DataFrame to structure data by date and time
|
||||||
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'])
|
start_time = str(row['Start Time'])
|
||||||
|
|
||||||
# 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']).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:
|
||||||
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'],
|
||||||
'Available Slots': slot_capacity - interviewee_count
|
'Available Slots': slot_capacity - interviewee_count
|
||||||
}
|
}
|
||||||
|
|
||||||
return interview_data
|
return interview_data
|
||||||
|
|
||||||
def AppendAppointment(date, start_time, interviewee_name, interviewee_email):
|
def AppendAppointment(date, start_time, interviewee_name, interviewee_email):
|
||||||
"""
|
"""
|
||||||
@ -84,46 +78,41 @@ def AppendAppointment(date, start_time, interviewee_name, interviewee_email):
|
|||||||
file_path = f"OR{year_donation}-L-Interview Data.xlsx"
|
file_path = f"OR{year_donation}-L-Interview Data.xlsx"
|
||||||
lock_path = f"OR{year_donation}-L-Interview Data.xlsx.lock"
|
lock_path = f"OR{year_donation}-L-Interview Data.xlsx.lock"
|
||||||
|
|
||||||
# checks for if the file exisits for the year otherwise it will create one
|
available_slots = ReadDatabase()
|
||||||
if not (os.path.isfile(file_path) or os.path.isfile(lock_path)):
|
|
||||||
NoSheet()
|
# Check if the requested slot is available in the `available_slots` structure
|
||||||
else:
|
if date in available_slots and start_time in available_slots[date]:
|
||||||
|
with FileLock(lock_path): # Ensure process-safe access to the file
|
||||||
|
# Load workbook and select "Sheet1" for updating appointments
|
||||||
|
workbook = load_workbook(file_path)
|
||||||
|
sheet = workbook["Interview Timetable"]
|
||||||
|
df = pd.read_excel(file_path)
|
||||||
|
|
||||||
available_slots = ReadDatabase()
|
# Find and update the row that matches the provided date and start time
|
||||||
|
for index, row in df.iterrows():
|
||||||
# Check if the requested slot is available in the `available_slots` structure
|
row_date = str(row['Date']).split(" ")[0]
|
||||||
if date in available_slots and start_time in available_slots[date]:
|
row_start_time = str(row['Start Time'])
|
||||||
with FileLock(lock_path): # Ensure process-safe access to the file
|
|
||||||
# Load workbook and select "Sheet1" for updating appointments
|
|
||||||
workbook = load_workbook(file_path)
|
|
||||||
sheet = workbook["Interview Timetable"]
|
|
||||||
df = pd.read_excel(file_path)
|
|
||||||
|
|
||||||
# Find and update the row that matches the provided date and start time
|
if row_date == date and row_start_time == start_time:
|
||||||
for index, row in df.iterrows():
|
# Current entries for names and emails, and append new data with comma and space
|
||||||
row_date = str(row['Date']).split(" ")[0]
|
current_names = str(row['Interviewee Name']).strip()
|
||||||
row_start_time = str(row['Start Time'])
|
current_emails = str(row['Interviewee Email']).strip()
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
if row_date == date and row_start_time == start_time:
|
# Update the cells with new names and emails
|
||||||
# Current entries for names and emails, and append new data with comma and space
|
name_cell = sheet.cell(row=index + 2, column=df.columns.get_loc('Interviewee Name') + 1)
|
||||||
current_names = str(row['Interviewee Name']).strip()
|
email_cell = sheet.cell(row=index + 2, column=df.columns.get_loc('Interviewee Email') + 1)
|
||||||
current_emails = str(row['Interviewee Email']).strip()
|
name_cell.value = updated_names
|
||||||
|
email_cell.value = updated_emails
|
||||||
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
|
|
||||||
|
|
||||||
# Update the cells with new names and emails
|
workbook.save(file_path)
|
||||||
name_cell = sheet.cell(row=index + 2, column=df.columns.get_loc('Interviewee Name') + 1)
|
send_email(interviewee_email, interviewee_name, date, start_time)
|
||||||
email_cell = sheet.cell(row=index + 2, column=df.columns.get_loc('Interviewee Email') + 1)
|
return True
|
||||||
name_cell.value = updated_names
|
|
||||||
email_cell.value = updated_emails
|
|
||||||
|
|
||||||
workbook.save(file_path)
|
# If no slots available, return that the slot is unavailable
|
||||||
send_email(interviewee_email, interviewee_name, date, start_time)
|
return False
|
||||||
return True
|
|
||||||
|
|
||||||
# If no slots available, return that the slot is unavailable
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def run_tests():
|
def run_tests():
|
||||||
|
Binary file not shown.
Binary file not shown.
BIN
__pycache__/interviewPackagers.cpython-313.pyc
Normal file
BIN
__pycache__/interviewPackagers.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,62 @@
|
|||||||
|
from ReadDB import ReadDatabase
|
||||||
|
|
||||||
|
|
||||||
|
def getSchedulePackager():
|
||||||
|
"""
|
||||||
|
Packages up the response for a http response
|
||||||
|
|
||||||
|
``REQUIRES``: None
|
||||||
|
|
||||||
|
``PROMISES``: ``JSON`` http response ready
|
||||||
|
|
||||||
|
``Develop in part by``: Brock T
|
||||||
|
|
||||||
|
``Contact``: darkicewolf50@gmail.ocm
|
||||||
|
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"interviewDates": ReadDatabase()
|
||||||
|
}
|
||||||
|
|
||||||
|
from WriteDB import AppendAppointment
|
||||||
|
from email_validator import validate_email, EmailNotValidError
|
||||||
|
|
||||||
|
|
||||||
|
def SelectAppointment (appointmentJson):
|
||||||
|
"""
|
||||||
|
Packages up a response for a http request
|
||||||
|
|
||||||
|
``REQUIRES``: ``JSON`` with the data of interviewee name, date, starttime and interviewee email
|
||||||
|
|
||||||
|
``PROMISES``: ``JSON`` Returns if the booking was a success
|
||||||
|
|
||||||
|
``Developed in part by``: Brock
|
||||||
|
|
||||||
|
``Contact``: darkicewolf50@gmail.com
|
||||||
|
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
Example of an incoming http post body
|
||||||
|
{
|
||||||
|
"intervieweeName": "Brock",
|
||||||
|
"date": "2024-09-16",
|
||||||
|
"startTime": "10:30:00",
|
||||||
|
"intervieweeEmail": "darkicewolf50@gmail.com"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
validEmail = validate_email(appointmentJson["intervieweeEmail"], check_deliverability=True)
|
||||||
|
if validEmail:
|
||||||
|
status = AppendAppointment(date=appointmentJson["date"], start_time=appointmentJson["startTime"], interviewee_name=appointmentJson["intervieweeName"], interviewee_email=appointmentJson["intervieweeEmail"])
|
||||||
|
|
||||||
|
if status:
|
||||||
|
resBody = {"Success": True, "validEmail": "true"}
|
||||||
|
else:
|
||||||
|
resBody = {"Success": False, "validEmail": "true"}
|
||||||
|
|
||||||
|
# resBody["message"] = appointmentJson for testing
|
||||||
|
return resBody
|
||||||
|
|
||||||
|
except EmailNotValidError as e:
|
||||||
|
return {"Success": False, "validEmail": "false"}
|
||||||
|
|
12
main.py
12
main.py
@ -1,8 +1,18 @@
|
|||||||
import json
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from NoSheet import NoSheet
|
||||||
|
import datetime
|
||||||
|
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
|
||||||
|
# name based off the 2025 naming system
|
||||||
|
# Define the path to the Excel file and the lock file
|
||||||
|
file_name = f"OR{year_donation}-L-Interview Data.xlsx"
|
||||||
|
if not os.path.isfile(file_name):
|
||||||
|
NoSheet()
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user