fix(Packagers): changed to first time run

This commit is contained in:
darkicewolf50 2024-12-07 20:07:57 -07:00
parent a9460dc692
commit 49313e8450
10 changed files with 171 additions and 115 deletions

Binary file not shown.

View File

@ -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__":

View File

@ -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.

Binary file not shown.

Binary file not shown.

View File

@ -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
View File

@ -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("/")