mirror of
https://github.com/UofCBaja/BajaCloud.git
synced 2025-06-15 13:14:17 -06:00
feat(Packagers): combined get and post into a file, added condition of no excel file to read and write
This commit is contained in:
parent
8e98eeee64
commit
a9460dc692
@ -1,25 +0,0 @@
|
|||||||
import yaml
|
|
||||||
import json
|
|
||||||
|
|
||||||
from ReadDB import ReadDatabase
|
|
||||||
|
|
||||||
with open("./MockDB/schedule.yaml", "r") as scheduleyml:
|
|
||||||
ymlschedule = yaml.safe_load(scheduleyml)
|
|
||||||
|
|
||||||
def getSchedulePackager():
|
|
||||||
"""
|
|
||||||
Formats and allows for the
|
|
||||||
|
|
||||||
``REQUIRES``: None
|
|
||||||
|
|
||||||
``PROMISES``: ``JSON`` http response ready
|
|
||||||
|
|
||||||
``Develop in part by``: Brock T
|
|
||||||
|
|
||||||
``Contact``: darkicewolf50@gmail.ocm
|
|
||||||
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
"interviewDates": ReadDatabase()
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
|||||||
Date:
|
|
||||||
Sept 16:
|
|
||||||
Meeting Duration: 30 min
|
|
||||||
Meeting Start Times:
|
|
||||||
- 10:00 am
|
|
||||||
- 10:30 am
|
|
||||||
- 11:00 am
|
|
||||||
- 11:30 am
|
|
||||||
- 1:00 pm
|
|
||||||
- 1:30 pm
|
|
||||||
- 2:00 pm
|
|
48
NoSheet.py
48
NoSheet.py
@ -1,13 +1,24 @@
|
|||||||
import openpyxl
|
import openpyxl
|
||||||
import yaml
|
import yaml
|
||||||
import json
|
|
||||||
import datetime
|
import datetime
|
||||||
from openpyxl.styles import Font, Border, Side, PatternFill
|
from openpyxl.styles import Font, Border, Side, PatternFill
|
||||||
from openpyxl.formatting.rule import FormulaRule
|
from openpyxl.formatting.rule import FormulaRule
|
||||||
|
|
||||||
|
|
||||||
def NoSheet():
|
def NoSheet():
|
||||||
# sheet with an example properites
|
"""
|
||||||
|
Creates the Template for more data to be added
|
||||||
|
|
||||||
|
``REQUIRES``: ``None`` Ensure no other sheets are present, will overwrite them
|
||||||
|
|
||||||
|
``PROMISES``: ``XLSX File`` give the template for recuitment for the year
|
||||||
|
|
||||||
|
``Develop in part by``: Brock
|
||||||
|
|
||||||
|
``Contact``: darkicewolf50@gmail.com
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
yamlraw = """
|
yamlraw = """
|
||||||
Recruitment Responses:
|
Recruitment Responses:
|
||||||
- Frist Name (What we should call them): Steve
|
- Frist Name (What we should call them): Steve
|
||||||
@ -39,21 +50,20 @@ Interview TimeTable:
|
|||||||
- Category (if not general): Test
|
- Category (if not general): Test
|
||||||
- Interviewer(s) Name(s): Example
|
- Interviewer(s) Name(s): Example
|
||||||
- Status: Dropdown (Options in datahelp) #default is Unknown
|
- Status: Dropdown (Options in datahelp) #default is Unknown
|
||||||
Data Helper:
|
Data Helper And Info:
|
||||||
- Status Dropdown:
|
- Status Dropdown:
|
||||||
- Unknown
|
- Unknown
|
||||||
- Done
|
- Done
|
||||||
- No Show
|
- No Show
|
||||||
- Cancelled/Moved
|
- Cancelled/Moved
|
||||||
|
- First time Startup: Call getschedule for the year
|
||||||
- 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
|
||||||
yamlsheet = yaml.safe_load(yamlraw)
|
yamlsheet = yaml.safe_load(yamlraw)
|
||||||
|
|
||||||
# print(json.dumps(yamlsheet, indent=4))
|
|
||||||
|
|
||||||
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:]) # 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 = f"./OR{year_donation + 1}-L-Interview Data.xlsx" # name based off the 2025 naming system
|
||||||
|
|
||||||
# border style
|
# border style
|
||||||
border = Border( # defualt behaviour is thin
|
border = Border( # defualt behaviour is thin
|
||||||
@ -63,9 +73,6 @@ Data Helper:
|
|||||||
bottom=Side(style='thin')
|
bottom=Side(style='thin')
|
||||||
)
|
)
|
||||||
|
|
||||||
# for conditional formatting
|
|
||||||
red_fill = PatternFill(start_color="FF0000", end_color="FF0000", fill_type="solid")
|
|
||||||
|
|
||||||
# create workbook in memory
|
# create workbook in memory
|
||||||
work_book = openpyxl.Workbook()
|
work_book = openpyxl.Workbook()
|
||||||
|
|
||||||
@ -73,45 +80,50 @@ Data Helper:
|
|||||||
default_sheet = work_book.active
|
default_sheet = work_book.active
|
||||||
work_book.remove(default_sheet)
|
work_book.remove(default_sheet)
|
||||||
|
|
||||||
|
# decomposes the yaml file at the top and convertss it into a standard template
|
||||||
|
# does one sheet at a time
|
||||||
for sheet_name, title_list in yamlsheet.items():
|
for sheet_name, title_list in yamlsheet.items():
|
||||||
# add standard sheets
|
# add 1 standard sheet, by the outermost name
|
||||||
sheet = work_book.create_sheet(sheet_name)
|
sheet = work_book.create_sheet(sheet_name)
|
||||||
|
|
||||||
|
# gets header titles for each sheet, from the inner list
|
||||||
titles = [list(title.keys())[0] for title in title_list]
|
titles = [list(title.keys())[0] for title in title_list]
|
||||||
|
|
||||||
|
# makes the header titles to bold, have a border and have text
|
||||||
for col_num, title in enumerate(titles, start=1):
|
for col_num, title in enumerate(titles, start=1):
|
||||||
cell = sheet.cell(row=1, column=col_num)
|
cell = sheet.cell(row=1, column=col_num)
|
||||||
cell.value = title
|
cell.value = title
|
||||||
|
|
||||||
cell.font = Font(bold=True)
|
cell.font = Font(bold=True)
|
||||||
cell.border = border
|
cell.border = border
|
||||||
|
|
||||||
|
# example data to show on what it will look like or to copy formatting down
|
||||||
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
|
||||||
if isinstance(data, list):
|
if isinstance(data, list):
|
||||||
row_num = 2
|
row_num = 2
|
||||||
for item in data:
|
for item in data:
|
||||||
cell = sheet.cell(row=row_num, column=col_num)
|
cell = sheet.cell(row=row_num, column=col_num)
|
||||||
cell.value = item
|
cell.value = item
|
||||||
row_num += 1
|
row_num += 1
|
||||||
|
# adds data to the cells
|
||||||
else:
|
else:
|
||||||
cell = sheet.cell(row=2, column=col_num)
|
cell = sheet.cell(row=2, column=col_num)
|
||||||
cell.value = data
|
# 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"
|
||||||
|
else:
|
||||||
|
cell.value = data
|
||||||
|
|
||||||
|
|
||||||
if sheet.title == "Recruitment Responses":
|
if sheet.title == "Recruitment Responses":
|
||||||
sheet.conditional_formatting.add("A2:I2", FormulaRule(formula=['=$I2="No"'], fill=PatternFill(start_color="FF0000", end_color="FF0000", fill_type="solid")))
|
sheet.conditional_formatting.add("A2:I2", FormulaRule(formula=['=$I2="No"'], fill=PatternFill(start_color="FF0000", end_color="FF0000", fill_type="solid")))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# save to storage
|
# save to storage
|
||||||
work_book.save(file_name)
|
work_book.save(file_name)
|
||||||
|
print(f"Created {file_name} for {year_donation}")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
NoSheet()
|
NoSheet()
|
Binary file not shown.
30
ReadDB.py
30
ReadDB.py
@ -3,29 +3,39 @@ import json
|
|||||||
from filelock import FileLock, Timeout
|
from filelock import FileLock, Timeout
|
||||||
import time
|
import time
|
||||||
|
|
||||||
# Define the path to the Excel file and the lock file
|
from NoSheet import NoSheet
|
||||||
excel_file_path = "./OR-L-Interview Data.xlsx"
|
import datetime
|
||||||
lock_file_path = "./OR-L-Interview Data.xlsx.lock"
|
import os
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
TODO change to dynamic file name
|
TODO update to possibly not use pandas and update to use the new template
|
||||||
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
|
TODO update name of function to be more clear
|
||||||
file_name = f"OR{year_donation}-L-Interview Data.xlsx" # name based off the 2025 naming system
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def ReadDatabase():
|
def ReadDatabase():
|
||||||
"""
|
"""
|
||||||
Reads the database for which slots are available
|
Reads the database for which slots are available
|
||||||
|
|
||||||
``REQUIRES``: None
|
``REQUIRES``: ``None``
|
||||||
|
|
||||||
``PROMISES``: JSON (Interview Available Slots)
|
``PROMISES``: ``JSON`` Interview Available Slots
|
||||||
|
|
||||||
``Developed in part by``: Ahmad
|
``Developed in part by``: Ahmad, Brock
|
||||||
|
|
||||||
``Contact``: ahmad.ahmad1@ucalgary.ca
|
``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
|
||||||
|
# Define the path to the Excel file and the lock file
|
||||||
|
excel_file_path = f"OR{year_donation}-L-Interview Data.xlsx"
|
||||||
|
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)):
|
||||||
|
NoSheet()
|
||||||
|
else:
|
||||||
# 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
|
||||||
retry_interval = 0.5 # Wait time (in seconds) between retries
|
retry_interval = 0.5 # Wait time (in seconds) between retries
|
||||||
|
47
WriteDB.py
47
WriteDB.py
@ -4,27 +4,37 @@ 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
|
||||||
|
|
||||||
# Define the path to the Excel file and the lock file
|
from NoSheet import NoSheet
|
||||||
file_path = "./interview_database.xlsx"
|
import datetime
|
||||||
lock_path = "./interview_database.xlsx.lock" # Lock file for synchronization
|
import os
|
||||||
|
|
||||||
"""
|
"""
|
||||||
TODO chnage to dynamic file name
|
TODO update to possibly not use pandas and update to use the new template
|
||||||
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
|
TODO update name of functions to be more clear
|
||||||
file_name = f"OR{year_donation}-L-Interview Data.xlsx" # name based off the 2025 naming system
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def ReadDatabase():
|
def ReadDatabase():
|
||||||
"""
|
"""
|
||||||
Reads the Database to retrieve available interview slots.
|
Reads the Database to retrieve available interview slots
|
||||||
|
|
||||||
``REQUIRES``: None
|
``REQUIRES``: None
|
||||||
|
|
||||||
``PROMISES``: JSON (Available interview slots)
|
``PROMISES``: JSON (Available interview slots)
|
||||||
|
|
||||||
``Developed by``: Ahmad
|
``Developed by``: Ahmad, Brock
|
||||||
|
|
||||||
``Contact``: ahmad.ahmad1@ucalgary.ca
|
``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"
|
||||||
|
|
||||||
|
# checks for if the file exisits for the year otherwise it will create one
|
||||||
|
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
|
# 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
|
||||||
@ -60,14 +70,25 @@ def AppendAppointment(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``: date (str), start_time (str), interviewee_name (str), interviewee_email (str)
|
``REQUIRES``: ``str`` date, ``str`` start_time, ``str`` interviewee_name, ``str`` interviewee_email
|
||||||
|
|
||||||
``PROMISES``: 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.
|
||||||
|
|
||||||
``Developed by``: Ahmad
|
``Developed by``: Ahmad, Brock
|
||||||
|
|
||||||
``Contact``: ahmad.ahmad1@ucalgary.ca
|
``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"
|
||||||
|
|
||||||
|
# checks for if the file exisits for the year otherwise it will create one
|
||||||
|
if not (os.path.isfile(file_path) or os.path.isfile(lock_path)):
|
||||||
|
NoSheet()
|
||||||
|
else:
|
||||||
|
|
||||||
available_slots = ReadDatabase()
|
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
|
||||||
|
BIN
__pycache__/NoSheet.cpython-313.pyc
Normal file
BIN
__pycache__/NoSheet.cpython-313.pyc
Normal file
Binary file not shown.
0
interviewPackagers.py
Normal file
0
interviewPackagers.py
Normal file
4
main.py
4
main.py
@ -35,7 +35,7 @@ def get_root():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
from GetSchedulePackager import getSchedulePackager
|
from interviewPackagers import getSchedulePackager
|
||||||
|
|
||||||
@app.get("/getAppointments")
|
@app.get("/getAppointments")
|
||||||
async def getAppointments():
|
async def getAppointments():
|
||||||
@ -65,7 +65,7 @@ async def getAppointments():
|
|||||||
# status_code=200 commented out just to show how to change it if you wanted
|
# status_code=200 commented out just to show how to change it if you wanted
|
||||||
)
|
)
|
||||||
|
|
||||||
from postSelectAppointment import SelectAppointment
|
from interviewPackagers import SelectAppointment
|
||||||
|
|
||||||
class Appointment(BaseModel):
|
class Appointment(BaseModel):
|
||||||
"""
|
"""
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
from WriteDB import AppendAppointment
|
|
||||||
from email_validator import validate_email, EmailNotValidError
|
|
||||||
|
|
||||||
|
|
||||||
def SelectAppointment (appointmentJson):
|
|
||||||
"""
|
|
||||||
packages up a response for a http request
|
|
||||||
|
|
||||||
``appointmentJSON``: ``JSON``
|
|
||||||
The appointment date and time details
|
|
||||||
|
|
||||||
``returns``: ``json``
|
|
||||||
Returns the status of the booking confirmation
|
|
||||||
|
|
||||||
``Develop in part by``: Brock T
|
|
||||||
|
|
||||||
``Contact``: darkicewolf50@gmail.com
|
|
||||||
"""
|
|
||||||
"""
|
|
||||||
{
|
|
||||||
"intervieweeName": "Alice Johnson",
|
|
||||||
"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"}
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print(SelectAppointment("10:00 AM"))
|
|
@ -4,12 +4,20 @@ from email.mime.text import MIMEText
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import pytz # For timezone handling
|
import pytz # For timezone handling
|
||||||
|
|
||||||
def send_email(interviewee_email="darkicewolf50@gmail.com", interviewee_name="brock", date="10-1-2024", start_time="10:00 AM", location ="ENC25"):
|
"""
|
||||||
|
TODO add
|
||||||
|
"""
|
||||||
|
def send_email(interviewee_email="darkicewolf50@gmail.com", interviewee_name="brock", date="2024-1-10", start_time="10:00:00", location ="ENC25"):
|
||||||
"""
|
"""
|
||||||
Sends an email notification to the interviewee and a static Gmail account.
|
Sends an email notification to the interviewee and to the uofcbaja account
|
||||||
|
|
||||||
``REQUIRES``: interviewee_email (str), interviewee_name (str), date (str), start_time (str)
|
``REQUIRES``: ``str`` interviewee_email, ``str`` interviewee_name, ``str`` date, ``str`` start_time
|
||||||
``PROMISES``: Sends an email to interviewee and static email on successful appointment booking.
|
|
||||||
|
``PROMISES``: ``EMAIL`` Sends an email to interviewee and static email on successful appointment booking.
|
||||||
|
|
||||||
|
``Developed by``: Ahmad
|
||||||
|
|
||||||
|
``Contact``: ahmad.ahmad1@ucalgary.ca
|
||||||
"""
|
"""
|
||||||
# Define static email for notifications and Gmail credentials
|
# Define static email for notifications and Gmail credentials
|
||||||
static_email = "uofcbaja.noreply@gmail.com"
|
static_email = "uofcbaja.noreply@gmail.com"
|
||||||
|
37
temp.yml
37
temp.yml
@ -1,37 +0,0 @@
|
|||||||
Recruitment Responses:
|
|
||||||
- Frist Name (What we should call them): Steve
|
|
||||||
- Last Name: the Bug
|
|
||||||
- Ucalgary Email: steve.the.bug@ucalgary.ca
|
|
||||||
- What Subsystem/SubTeam are you interested in?: |
|
|
||||||
Chassis
|
|
||||||
Ergonomics
|
|
||||||
Suspension
|
|
||||||
Steering
|
|
||||||
Powertrain
|
|
||||||
Final Drive
|
|
||||||
Any Mechanical
|
|
||||||
Business - Content Creation
|
|
||||||
Business - Business Relations
|
|
||||||
Software
|
|
||||||
- Major: General (1st Year)
|
|
||||||
- Academic Year: 1st
|
|
||||||
- Why are you interested in joining UCalgary BAJA?: Example Interest
|
|
||||||
- Where did you hear about us?: Testing
|
|
||||||
- Are you available for team meetings/work days? Saturdays 10 am - 4 pm: "Yes" #add condiftional formatting for no to make whole line red
|
|
||||||
Interview TimeTable:
|
|
||||||
- Date: 9/16/2024
|
|
||||||
- Meeting Duration: 30 min
|
|
||||||
- Start Time Slot: 10:00:00 AM
|
|
||||||
- Slot: 1
|
|
||||||
- Interviewee Name (What to call them): Steve
|
|
||||||
- Interviewee Email: steve.the.bug@ucalgary.ca
|
|
||||||
- Category (if not general): Test
|
|
||||||
- Interviewer(s) Name(s): Example
|
|
||||||
- Status: Dropdown (Options in datahelp) #default is Unknown
|
|
||||||
Data Helper:
|
|
||||||
- Status Dropdown:
|
|
||||||
- Unknown
|
|
||||||
- Done
|
|
||||||
- No Show
|
|
||||||
- Cancelled/Moved
|
|
||||||
- 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
|
|
Loading…
x
Reference in New Issue
Block a user