mirror of
https://github.com/UofCBaja/BajaCloud.git
synced 2025-06-15 13:14:17 -06:00
init (InterviewBackend)
This commit is contained in:
commit
0de072a5ad
4
.dockerignore
Normal file
4
.dockerignore
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.git
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
.env
|
70
.github/workflows/Actions.yaml
vendored
Normal file
70
.github/workflows/Actions.yaml
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
|
||||||
|
# name of the workflow.
|
||||||
|
# this is optional.
|
||||||
|
name: Interview Cloud Actions
|
||||||
|
|
||||||
|
# events that will trigger this workflow.
|
||||||
|
# here, we only have "pull_request", so the workflow will run
|
||||||
|
# whenever we create a pull request.
|
||||||
|
# other examples: [push] and [pull_request, push]
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
# each workflow must have at least one job.
|
||||||
|
# jobs run in parallel by default (we can change that).
|
||||||
|
# each job groups together a series of steps to accomplish a purpose.
|
||||||
|
jobs:
|
||||||
|
# name of the job
|
||||||
|
ruffLint:
|
||||||
|
# the platform or OS that the workflow will run on.
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# series of steps to finish the job.
|
||||||
|
steps:
|
||||||
|
# name of the step.
|
||||||
|
# steps run sequentially.
|
||||||
|
# this is optionale
|
||||||
|
- name: checkout
|
||||||
|
# each step can either have "uses" or "run".
|
||||||
|
# "uses" run an action written somewhere other than this workflow .
|
||||||
|
# usually from the community.
|
||||||
|
# this action checks out the repo code to the runner (instance)
|
||||||
|
# running the action
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# another step.
|
||||||
|
# this step runs a bash (Ubuntu's default shell) command
|
||||||
|
- name: install ruff
|
||||||
|
run: pip install ruff
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: ruff check ./*/*.py --ignore E402
|
||||||
|
|
||||||
|
Dockerhub:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: ruffLint # will only run if linter is successful
|
||||||
|
if: ${{ github.ref == 'refs/heads/main' || github.event.pull_request.merged == true }} # Runs if it's a push to 'main' or a merged PR to 'main'
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Login to Dockerhub # log into docker hub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }} # Using secret for Docker username
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }} # Using secret for Docker password
|
||||||
|
id: docker-login
|
||||||
|
|
||||||
|
- name: build container image # build the container
|
||||||
|
run: docker compose build --no-cache
|
||||||
|
id: docker-build
|
||||||
|
|
||||||
|
- name: Upload to Dockerhub
|
||||||
|
run: docker push darkicewolf50/uofcbajacloud:latest
|
||||||
|
if: ${{ steps.docker-login.outcome == 'success' && steps.docker-build.outcome == 'success' }}
|
||||||
|
|
||||||
|
|
19
Dockerfile
Normal file
19
Dockerfile
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Use an official Python runtime as a parent image
|
||||||
|
FROM python:3.10-slim
|
||||||
|
|
||||||
|
# Set the working directory inside the container
|
||||||
|
WORKDIR /BajaCloudBackend
|
||||||
|
|
||||||
|
# Copy the current directory contents into the container at /app
|
||||||
|
COPY ./ContainerContents /BajaCloudBackend
|
||||||
|
|
||||||
|
# Install any necessary dependencies
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Expose port 8080 for the container to listen on
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Command to run the Python server when the container starts
|
||||||
|
#CMD ["fastapi", "run" "main.py"]
|
||||||
|
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--reload"]
|
||||||
|
|
141
InterviewBooking/ContainerContents/NoSheet.py
Normal file
141
InterviewBooking/ContainerContents/NoSheet.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import openpyxl
|
||||||
|
import yaml
|
||||||
|
import datetime
|
||||||
|
from openpyxl.styles import Font, Border, Side, PatternFill
|
||||||
|
from openpyxl.formatting.rule import FormulaRule
|
||||||
|
|
||||||
|
|
||||||
|
def NoSheet(file_path):
|
||||||
|
"""
|
||||||
|
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 = """
|
||||||
|
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: "No" #add condiftional formatting for no to make whole line red
|
||||||
|
Interview TimeTable:
|
||||||
|
- Date: 2024-09-16
|
||||||
|
- 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 And Info:
|
||||||
|
- Status Dropdown:
|
||||||
|
- Unknown
|
||||||
|
- Done
|
||||||
|
- No Show
|
||||||
|
- Cancelled/Moved
|
||||||
|
- 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
|
||||||
|
"""
|
||||||
|
# uses the base above "yaml file" to create the base template
|
||||||
|
yamlsheet = yaml.safe_load(yamlraw)
|
||||||
|
|
||||||
|
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 = file_path
|
||||||
|
|
||||||
|
# border style
|
||||||
|
border = Border( # defualt behaviour is thin
|
||||||
|
left=Side(style='thin'),
|
||||||
|
right=Side(style='thin'),
|
||||||
|
top=Side(style='thin'),
|
||||||
|
bottom=Side(style='thin')
|
||||||
|
)
|
||||||
|
|
||||||
|
# create workbook in memory
|
||||||
|
work_book = openpyxl.Workbook()
|
||||||
|
|
||||||
|
# remove default sheet
|
||||||
|
default_sheet = work_book.active
|
||||||
|
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():
|
||||||
|
# add 1 standard sheet, by the outermost 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]
|
||||||
|
|
||||||
|
# makes the header titles to bold, have a border and have text
|
||||||
|
for col_num, title in enumerate(titles, start=1):
|
||||||
|
cell = sheet.cell(row=1, column=col_num)
|
||||||
|
cell.value = title
|
||||||
|
cell.font = Font(bold=True)
|
||||||
|
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]
|
||||||
|
|
||||||
|
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):
|
||||||
|
row_num = 2
|
||||||
|
for item in data:
|
||||||
|
cell = sheet.cell(row=row_num, column=col_num)
|
||||||
|
cell.value = item
|
||||||
|
row_num += 1
|
||||||
|
# adds data to the cells
|
||||||
|
else:
|
||||||
|
cell = sheet.cell(row=2, column=col_num)
|
||||||
|
# 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)":
|
||||||
|
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:
|
||||||
|
cell.value = data
|
||||||
|
|
||||||
|
|
||||||
|
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")))
|
||||||
|
|
||||||
|
|
||||||
|
# save to storage
|
||||||
|
work_book.save(file_name)
|
||||||
|
print(f"Created {file_name} for {year_donation}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
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)
|
90
InterviewBooking/ContainerContents/ReadDB.py
Normal file
90
InterviewBooking/ContainerContents/ReadDB.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import pandas as pd
|
||||||
|
import json
|
||||||
|
from filelock import FileLock, Timeout
|
||||||
|
import time
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
"""
|
||||||
|
TODO change to use new tempate
|
||||||
|
TODO change names to be more clear
|
||||||
|
"""
|
||||||
|
|
||||||
|
def ReadDatabase(file_path):
|
||||||
|
"""
|
||||||
|
Reads the database for which slots are available
|
||||||
|
|
||||||
|
``REQUIRES``: ``File_Path`` where the file is
|
||||||
|
|
||||||
|
``PROMISES``: ``JSON`` Interview Available Slots
|
||||||
|
|
||||||
|
``Developed in part by``: Ahmad, Brock
|
||||||
|
|
||||||
|
``Contact``: ahmad.ahmad1@ucalgary.ca, darkicewolf50@gmail.com
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# Define the path to the Excel file and the lock file
|
||||||
|
excel_file_path = file_path
|
||||||
|
lock_file_path = file_path + ".lock"
|
||||||
|
|
||||||
|
# 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
|
||||||
|
while retries < max_retries:
|
||||||
|
try:
|
||||||
|
# Attempt to acquire a shared read (non-blocking) access
|
||||||
|
with FileLock(lock_file_path, timeout=0): # Non-blocking, checks if the lock exists
|
||||||
|
# Load the Excel file into a pandas DataFrame
|
||||||
|
df = pd.read_excel(excel_file_path, sheet_name="Interview TimeTable")
|
||||||
|
|
||||||
|
# Initialize the dictionary to store the structured data
|
||||||
|
interview_data = {}
|
||||||
|
|
||||||
|
# Group the DataFrame by Date, Start Time, and Slot for organization
|
||||||
|
for _, row in df.iterrows():
|
||||||
|
date = str(row['Date']).split(" ")[0]
|
||||||
|
start_time = str(row['Start Time Slot'])
|
||||||
|
slot = int(row['Slot']) if not pd.isna(row['Slot']) else 0
|
||||||
|
|
||||||
|
# Returns the number of interviewees in the slot; returns 0 if empty
|
||||||
|
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
|
||||||
|
available_slots = interviewee_amount != slot
|
||||||
|
if available_slots:
|
||||||
|
# Initialize nested structure if not present
|
||||||
|
if date not in interview_data:
|
||||||
|
interview_data[date] = {}
|
||||||
|
# Add the start time and duration if not present
|
||||||
|
if start_time not in interview_data[date]:
|
||||||
|
interview_data[date][start_time] = {
|
||||||
|
'Meeting Duration': row['Meeting Duration'],
|
||||||
|
}
|
||||||
|
return interview_data # Successfully read the database
|
||||||
|
|
||||||
|
except Timeout:
|
||||||
|
# File is locked; wait and retry
|
||||||
|
retries += 1
|
||||||
|
print(f"File is locked, retrying ({retries}/{max_retries})...")
|
||||||
|
time.sleep(retry_interval)
|
||||||
|
|
||||||
|
# If max retries are exceeded, raise an error
|
||||||
|
raise RuntimeError("Unable to access the database after multiple attempts due to a file lock.")
|
||||||
|
|
||||||
|
|
||||||
|
# Example usage of the ReadDatabase function
|
||||||
|
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:
|
||||||
|
data = ReadDatabase(file_name)
|
||||||
|
print(json.dumps(data, indent=4))
|
||||||
|
except RuntimeError as e:
|
||||||
|
print(e)
|
157
InterviewBooking/ContainerContents/WriteDB.py
Normal file
157
InterviewBooking/ContainerContents/WriteDB.py
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
import pandas as pd
|
||||||
|
import json
|
||||||
|
from openpyxl import load_workbook
|
||||||
|
from send_email import send_email
|
||||||
|
from filelock import FileLock
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
TODO make it work with the new template
|
||||||
|
TODO update names to be more clear
|
||||||
|
TODO try to remove pandas
|
||||||
|
"""
|
||||||
|
def ReadDatabase(file_path, lock_path):
|
||||||
|
"""
|
||||||
|
Reads the Database to retrieve available interview slots
|
||||||
|
|
||||||
|
``REQUIRES``: ``File_Path`` ``Lock_Path`` where the file and lock are located
|
||||||
|
|
||||||
|
``PROMISES``: JSON (Available interview slots)
|
||||||
|
|
||||||
|
``Developed by``: Ahmad, Brock
|
||||||
|
|
||||||
|
``Contact``: ahmad.ahmad1@ucalgary.ca, darkicewolf50@gmail.com
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 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', '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
|
||||||
|
interview_data = {}
|
||||||
|
|
||||||
|
# Process each row in the DataFrame to structure data by date and time
|
||||||
|
for _, row in df.iterrows():
|
||||||
|
# Convert Date and Start Time to string format for easier comparison
|
||||||
|
date = str(row['Date']).split(" ")[0] # Format date to YYYY-MM-DD
|
||||||
|
start_time = str(row['Start Time Slot'])
|
||||||
|
|
||||||
|
# Calculate the slot capacity and current number of interviewees
|
||||||
|
slot_capacity = int(row['Slot']) if not pd.isna(row['Slot']) else 0
|
||||||
|
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
|
||||||
|
|
||||||
|
# Check if there are available slots for more interviewees
|
||||||
|
if interviewee_count < slot_capacity:
|
||||||
|
# Organize data by date and time, keeping track of available slots and meeting duration
|
||||||
|
if date not in interview_data:
|
||||||
|
interview_data[date] = {}
|
||||||
|
interview_data[date][start_time] = {
|
||||||
|
'Meeting Duration': row['Meeting Duration'],
|
||||||
|
'Available Slots': slot_capacity - interviewee_count
|
||||||
|
}
|
||||||
|
|
||||||
|
return interview_data
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
``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.
|
||||||
|
|
||||||
|
``Developed by``: Ahmad, Brock
|
||||||
|
|
||||||
|
``Contact``: ahmad.ahmad1@ucalgary.ca, darkicewolf50@gmail.com
|
||||||
|
"""
|
||||||
|
|
||||||
|
lock_path = file_path + ".lock"
|
||||||
|
|
||||||
|
available_slots = ReadDatabase(file_path, lock_path)
|
||||||
|
|
||||||
|
# Check if the requested slot is available in the `available_slots` structure
|
||||||
|
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 "Interview TimeTable" for updating appointments
|
||||||
|
workbook = load_workbook(file_path)
|
||||||
|
sheet = workbook["Interview TimeTable"]
|
||||||
|
df = pd.read_excel(file_path, sheet_name="Interview TimeTable")
|
||||||
|
|
||||||
|
# Find and update the row that matches the provided date and start time
|
||||||
|
for index, row in df.iterrows():
|
||||||
|
row_date = str(row['Date']).split(" ")[0]
|
||||||
|
row_start_time = str(row['Start Time Slot'])
|
||||||
|
|
||||||
|
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_names = str(row['Interviewee Name (What to call them)']).strip()
|
||||||
|
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
|
||||||
|
|
||||||
|
# Update the cells with new names and emails
|
||||||
|
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)
|
||||||
|
name_cell.value = updated_names
|
||||||
|
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)
|
||||||
|
send_email(interviewee_email, interviewee_name, date, start_time)
|
||||||
|
return True
|
||||||
|
|
||||||
|
# If no slots available, return that the slot is unavailable
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def run_tests():
|
||||||
|
"""
|
||||||
|
Executes test cases to verify appointment scheduling and slot availability.
|
||||||
|
|
||||||
|
``REQUIRES``: None
|
||||||
|
|
||||||
|
``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:")
|
||||||
|
available_slots = ReadDatabase(file_path=file_name, lock_path=lock)
|
||||||
|
print(json.dumps(available_slots, indent=4))
|
||||||
|
|
||||||
|
# Test Case 1: Append to an available slot on 2024-09-16 at 10:30:00
|
||||||
|
date_1 = "9/16/2024"
|
||||||
|
start_time_1 = "10:30:00"
|
||||||
|
interviewee_name_1 = "Alice Johnson"
|
||||||
|
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})")
|
||||||
|
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
|
||||||
|
date_2 = "9/17/2024"
|
||||||
|
start_time_2 = "13:30:00"
|
||||||
|
interviewee_name_2 = "Bob Smith"
|
||||||
|
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})")
|
||||||
|
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
|
||||||
|
date_3 = "9/16/2024"
|
||||||
|
start_time_3 = "10:30:00"
|
||||||
|
interviewee_name_3 = "Charlie Brown"
|
||||||
|
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})")
|
||||||
|
AppendAppointment(file_name, date_3, start_time_3, interviewee_name_3, interviewee_email_3)
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run_tests()
|
55
InterviewBooking/ContainerContents/interviewPackagers.py
Normal file
55
InterviewBooking/ContainerContents/interviewPackagers.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
from ReadDB import ReadDatabase
|
||||||
|
|
||||||
|
|
||||||
|
def getSchedulePackager(file_name):
|
||||||
|
"""
|
||||||
|
Packages up the response for a http response
|
||||||
|
|
||||||
|
``REQUIRES``: ``File_Path`` where the file is
|
||||||
|
|
||||||
|
``PROMISES``: ``JSON`` http response ready
|
||||||
|
|
||||||
|
``Develop in part by``: Brock T
|
||||||
|
|
||||||
|
``Contact``: darkicewolf50@gmail.ocm
|
||||||
|
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"interviewDates": ReadDatabase(file_path=file_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
from WriteDB import AppendAppointment
|
||||||
|
from email_validator import validate_email, EmailNotValidError
|
||||||
|
|
||||||
|
|
||||||
|
def SelectAppointment (file_name, appointmentJson):
|
||||||
|
"""
|
||||||
|
Packages up a response for a http request
|
||||||
|
|
||||||
|
``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
|
||||||
|
|
||||||
|
``Developed in part by``: Brock
|
||||||
|
|
||||||
|
``Contact``: darkicewolf50@gmail.com
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
validEmail = validate_email(appointmentJson["intervieweeEmail"], check_deliverability=True)
|
||||||
|
if validEmail:
|
||||||
|
status = AppendAppointment(file_path=file_name, 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:
|
||||||
|
print(e)
|
||||||
|
return {"Success": False, "validEmail": "false"}
|
||||||
|
|
128
InterviewBooking/ContainerContents/main.py
Normal file
128
InterviewBooking/ContainerContents/main.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
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"/Interviews/OR{year_donation}-L-Interview Data.xlsx"
|
||||||
|
if not os.path.isfile(file_name):
|
||||||
|
os.makedirs(os.path.dirname(file_name), exist_ok=True)
|
||||||
|
NoSheet(file_name)
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def get_root():
|
||||||
|
"""
|
||||||
|
This does nothing, allows for pings to check for life
|
||||||
|
|
||||||
|
``REQUIRES``: ```None`` Nothing
|
||||||
|
|
||||||
|
``PROMISES``: ``JSON`` returns a short message in the body
|
||||||
|
|
||||||
|
``Develop in part by``: Brock
|
||||||
|
|
||||||
|
``Contact``: darkicewolf50@gmail.com
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
res = {"message": "Hello I am alive, this does nothing"}
|
||||||
|
|
||||||
|
# Return the response with the custom header
|
||||||
|
return JSONResponse(
|
||||||
|
headers={
|
||||||
|
"isBase64Encoded": "false", # Header Modification
|
||||||
|
},
|
||||||
|
content={
|
||||||
|
"body": res # Ensure res is a dict or do json.dumps to enusre it is stringified
|
||||||
|
},
|
||||||
|
|
||||||
|
# status_code=200 commented out just to show how to change it if you wanted
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
from interviewPackagers import getSchedulePackager
|
||||||
|
|
||||||
|
@app.get("/getAppointments")
|
||||||
|
async def getAppointments():
|
||||||
|
"""
|
||||||
|
checks for all available slots in the database
|
||||||
|
|
||||||
|
``REQUIRES``: ``None`` Nothing
|
||||||
|
|
||||||
|
``PROMISES``: ``JSON`` returns all of the avaialbe slots by date then time
|
||||||
|
|
||||||
|
``Develop in part by``: Brock
|
||||||
|
|
||||||
|
``Contact``: darkicewolf50@gmail.com
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
res = getSchedulePackager(file_name)
|
||||||
|
|
||||||
|
return JSONResponse(
|
||||||
|
headers={
|
||||||
|
"isBase64Encoded": "false", # Header Modification
|
||||||
|
},
|
||||||
|
content={
|
||||||
|
"body": res # Ensure res is a dict or do json.dumps to enusre it is stringified
|
||||||
|
},
|
||||||
|
|
||||||
|
# status_code=200 commented out just to show how to change it if you wanted
|
||||||
|
)
|
||||||
|
|
||||||
|
from interviewPackagers import SelectAppointment
|
||||||
|
|
||||||
|
class Appointment(BaseModel):
|
||||||
|
"""
|
||||||
|
The formatted
|
||||||
|
|
||||||
|
``REQUIRES``: Correct Format
|
||||||
|
|
||||||
|
``PROMISES``: Formatted class, needs to be converted into dict os that it can be used in another file
|
||||||
|
|
||||||
|
``Develop in part by``: Brock
|
||||||
|
|
||||||
|
``Contact``: darkicewolf50@gmail.com
|
||||||
|
|
||||||
|
"""
|
||||||
|
intervieweeName: str
|
||||||
|
date: str
|
||||||
|
startTime: str
|
||||||
|
intervieweeEmail: str
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/SelectInterview")
|
||||||
|
async def postSelectInterview(rawRequest: Appointment):
|
||||||
|
"""
|
||||||
|
Books an interview, first checks if the slot is valid
|
||||||
|
|
||||||
|
``REQUIRES``: ``Appointment`` A specifically formatted request
|
||||||
|
|
||||||
|
``PROMISES``: ``JSON`` returns if the booking was successful or not
|
||||||
|
|
||||||
|
``Develop in part by``: Brock
|
||||||
|
|
||||||
|
``Contact``: darkicewolf50@gmail.com
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
requestDict = {key: str(value) for key, value in rawRequest.dict().items()}
|
||||||
|
res = SelectAppointment(file_name, requestDict)
|
||||||
|
|
||||||
|
return JSONResponse(
|
||||||
|
headers={
|
||||||
|
"isBase64Encoded": "false", # Header Modification
|
||||||
|
},
|
||||||
|
content={
|
||||||
|
"body": res # Ensure res is a dict or do json.dumps to enusre it is stringified
|
||||||
|
},
|
||||||
|
|
||||||
|
# status_code=200 commented out just to show how to change it if you wanted
|
||||||
|
)
|
||||||
|
|
5
InterviewBooking/ContainerContents/requirements.txt
Normal file
5
InterviewBooking/ContainerContents/requirements.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
fastapi[standard]
|
||||||
|
pandas
|
||||||
|
openpyxl
|
||||||
|
PyYAML
|
||||||
|
filelock
|
94
InterviewBooking/ContainerContents/send_email.py
Normal file
94
InterviewBooking/ContainerContents/send_email.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import smtplib
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import pytz # For timezone handling
|
||||||
|
|
||||||
|
"""
|
||||||
|
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 to the uofcbaja account
|
||||||
|
|
||||||
|
``REQUIRES``: ``str`` interviewee_email, ``str`` interviewee_name, ``str`` date, ``str`` start_time
|
||||||
|
|
||||||
|
``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
|
||||||
|
static_email = "uofcbaja@gmail.com"
|
||||||
|
gmail_user = "uofcbaja.noreply@gmail.com"
|
||||||
|
gmail_apppassword = "pver lpnt upjd zvld"
|
||||||
|
|
||||||
|
# Define Mountain Standard Time
|
||||||
|
mst = pytz.timezone("America/Edmonton") # MST with Daylight Savings considered
|
||||||
|
|
||||||
|
# Parse the input date and time, localize to MST
|
||||||
|
start_datetime = mst.localize(datetime.strptime(f"{date} {start_time}", "%Y-%m-%d %H:%M:%S"))
|
||||||
|
end_datetime = start_datetime + timedelta(minutes=30)
|
||||||
|
|
||||||
|
# Format date and time for the calendar URLs
|
||||||
|
start_time_google = start_datetime.strftime("%Y%m%dT%H%M%S") # Google Calendar (Local time without Z)
|
||||||
|
end_time_google = end_datetime.strftime("%Y%m%dT%H%M%S") # Google Calendar (Local time without Z)
|
||||||
|
outlook_start = start_datetime.isoformat() # Outlook Calendar (ISO local time)
|
||||||
|
outlook_end = end_datetime.isoformat() # Outlook Calendar (ISO local time)
|
||||||
|
|
||||||
|
# Create message object
|
||||||
|
msg = MIMEMultipart()
|
||||||
|
msg['From'] = gmail_user
|
||||||
|
msg['To'] = f"{interviewee_email}, {static_email}"
|
||||||
|
msg['Subject'] = "Interview Appointment Confirmation"
|
||||||
|
|
||||||
|
# Message body
|
||||||
|
body = f'''
|
||||||
|
<html lang="en-US">
|
||||||
|
<head>
|
||||||
|
<title>Interview Invitation</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Dear {interviewee_name},</p>
|
||||||
|
<p>Your interview has been scheduled on {date} at {start_time} MST.</p>
|
||||||
|
<p> Your interview location is at {location} or will be emailed to you. </p>
|
||||||
|
<p>Please ensure to be available at the designated time.</p>
|
||||||
|
<a
|
||||||
|
href="https://calendar.google.com/calendar/render?action=TEMPLATE&text=UCalgary+Baja+Interview+with+{interviewee_name}&dates={start_time_google}/{end_time_google}&details=Interview+with+UCalgary+Baja+Team&location={location}"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<button type="button" class="AddtoCal">Add to Google Calendar</button>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://outlook.live.com/calendar/0/deeplink/compose?subject=UCalgary+Baja+Interview+with+{interviewee_name}&body=Interview+with+UCalgary+Baja+Team&location={location}&startdt={outlook_start}&enddt={outlook_end}"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<button type="button" class="AddtoCal">Add to Outlook Calendar</button>
|
||||||
|
</a>
|
||||||
|
<p>Best regards,</p>
|
||||||
|
<p>UCalgary Baja Interview Team</p>
|
||||||
|
<img
|
||||||
|
src="https://res.cloudinary.com/dpgrgsh7g/image/upload/v1733003224/UCalgaryBAJA_Logo-2024_mpmljh.png"
|
||||||
|
alt="UCalgary Baja Team"
|
||||||
|
height="120svh"
|
||||||
|
/>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
'''
|
||||||
|
|
||||||
|
msg.attach(MIMEText(body, 'html'))
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Setup the server and send the email
|
||||||
|
server = smtplib.SMTP('smtp.gmail.com', 587)
|
||||||
|
server.starttls()
|
||||||
|
server.login(gmail_user, gmail_apppassword)
|
||||||
|
server.sendmail(gmail_user, [interviewee_email, static_email], msg.as_string())
|
||||||
|
server.quit()
|
||||||
|
# print(f"Email sent successfully to {interviewee_email} and {static_email}.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to send email: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
send_email()
|
BIN
InterviewBooking/Other Items/AfterMergeCodeapceTestResults.png
Normal file
BIN
InterviewBooking/Other Items/AfterMergeCodeapceTestResults.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
44
InterviewBooking/Other Items/ConversionToExcel.py
Normal file
44
InterviewBooking/Other Items/ConversionToExcel.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import pandas as pd
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
# Load the YAML file
|
||||||
|
with open("./interview_database.yaml", 'r') as file:
|
||||||
|
interview_data = yaml.safe_load(file)
|
||||||
|
|
||||||
|
# Access the nested structure within 'Data'
|
||||||
|
flattened_data = []
|
||||||
|
for date, date_info in interview_data['Data'].items():
|
||||||
|
meeting_duration = date_info.get('Meeting Duration')
|
||||||
|
|
||||||
|
for start_time_info in date_info.get('Meeting Start Times', []):
|
||||||
|
for start_time, meeting_info in start_time_info.items():
|
||||||
|
interviewer_name = meeting_info['Interviewer'][0].get('Name')
|
||||||
|
interviewer_email = meeting_info['Interviewer'][1].get('Email')
|
||||||
|
interviewee_name = meeting_info['Interviewee'][0].get('Name')
|
||||||
|
interviewee_email = meeting_info['Interviewee'][1].get('Email')
|
||||||
|
category = meeting_info.get('Category')
|
||||||
|
status = meeting_info.get('Status')
|
||||||
|
slot = meeting_info.get('Slot')
|
||||||
|
|
||||||
|
# Add flattened row to list
|
||||||
|
flattened_data.append({
|
||||||
|
'Date': date,
|
||||||
|
'Meeting Duration': meeting_duration,
|
||||||
|
'Start Time': start_time,
|
||||||
|
'Interviewer Name': interviewer_name,
|
||||||
|
'Interviewer Email': interviewer_email,
|
||||||
|
'Interviewee Name': interviewee_name,
|
||||||
|
'Interviewee Email': interviewee_email,
|
||||||
|
'Category': category,
|
||||||
|
'Status': status,
|
||||||
|
'Slot': slot
|
||||||
|
})
|
||||||
|
|
||||||
|
# Convert to DataFrame if flattened data is not empty
|
||||||
|
if flattened_data:
|
||||||
|
df = pd.DataFrame(flattened_data)
|
||||||
|
# Write the DataFrame to an Excel file
|
||||||
|
df.to_excel("interview_database.xlsx", index=False)
|
||||||
|
print("Data has been written to interview_database.xlsx")
|
||||||
|
else:
|
||||||
|
print("No data found to write.")
|
17
InterviewBooking/Other Items/Docker Test/Dockerfile
Normal file
17
InterviewBooking/Other Items/Docker Test/Dockerfile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Use an official Python runtime as a parent image
|
||||||
|
FROM python:3.10-slim
|
||||||
|
|
||||||
|
# Set the working directory inside the container
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy the current directory contents into the container at /app
|
||||||
|
COPY ./mock /app
|
||||||
|
|
||||||
|
# Install any necessary dependencies
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Expose port 8080 for the container to listen on
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Command to run the Python server when the container starts
|
||||||
|
CMD ["python", "server.py"]
|
10
InterviewBooking/Other Items/Docker Test/docker-compose.yaml
Normal file
10
InterviewBooking/Other Items/Docker Test/docker-compose.yaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
services:
|
||||||
|
test-http-container:
|
||||||
|
container_name: test-http-container
|
||||||
|
ports:
|
||||||
|
- 8080:8080
|
||||||
|
volumes:
|
||||||
|
- ./mock:/app
|
||||||
|
build: .
|
||||||
|
# networks:
|
||||||
|
# - testnet
|
Binary file not shown.
49
InterviewBooking/Other Items/Docker Test/mock/server.py
Normal file
49
InterviewBooking/Other Items/Docker Test/mock/server.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import json
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||||
|
from django.http import HttpResponse
|
||||||
|
|
||||||
|
|
||||||
|
# Function to generate the response
|
||||||
|
def send_funct():
|
||||||
|
ymlschedule = {"message": False}
|
||||||
|
to_send = {
|
||||||
|
"statusCode": 200,
|
||||||
|
"isBase64ENcoded": "false",
|
||||||
|
"body": json.dumps(ymlschedule)
|
||||||
|
}
|
||||||
|
return json.dumps(to_send)
|
||||||
|
|
||||||
|
# Define request handler
|
||||||
|
class RequestHandler(BaseHTTPRequestHandler):
|
||||||
|
def do_GET(self):
|
||||||
|
# Send response headers
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header('Content-type', 'application/json')
|
||||||
|
self.end_headers()
|
||||||
|
|
||||||
|
# Send the response body (the JSON data)
|
||||||
|
self.wfile.write(send_funct().encode('utf-8'))
|
||||||
|
|
||||||
|
# Graceful shutdown handler
|
||||||
|
def signal_handler(sig, frame):
|
||||||
|
print("\nShutting down server gracefully...")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Set up and start the server
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Register signal handler for graceful shutdown (e.g., Ctrl+C)
|
||||||
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
|
||||||
|
# Set the server address (localhost) and port (8080)
|
||||||
|
server_address = ('', 8080)
|
||||||
|
httpd = HTTPServer(server_address, RequestHandler)
|
||||||
|
|
||||||
|
print("Server started on port 8080")
|
||||||
|
try:
|
||||||
|
# Start the server and listen for requests
|
||||||
|
httpd.serve_forever()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
# Server shutdown is handled in signal_handler
|
||||||
|
pass
|
BIN
InterviewBooking/Other Items/FastAPI test 1000 requests.png
Normal file
BIN
InterviewBooking/Other Items/FastAPI test 1000 requests.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
InterviewBooking/Other Items/OR-L-Interview Data.xlsx
Normal file
BIN
InterviewBooking/Other Items/OR-L-Interview Data.xlsx
Normal file
Binary file not shown.
1
InterviewBooking/Other Items/README.md
Normal file
1
InterviewBooking/Other Items/README.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# Interview-Backend
|
11
InterviewBooking/Other Items/TestBookAppointment.py
Normal file
11
InterviewBooking/Other Items/TestBookAppointment.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
from WriteDB import AppendAppointment
|
||||||
|
|
||||||
|
def TestBookAppointment():
|
||||||
|
date_1 = "2024-09-16"
|
||||||
|
start_time_1 = "10:30:00"
|
||||||
|
interviewee_name_1 = "Alice Johnson"
|
||||||
|
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})")
|
||||||
|
AppendAppointment(date_1, start_time_1, interviewee_name_1, interviewee_email_1)
|
||||||
|
|
||||||
|
TestBookAppointment()
|
BIN
InterviewBooking/Other Items/TestResultsCodeSpace.png
Normal file
BIN
InterviewBooking/Other Items/TestResultsCodeSpace.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
8
InterviewBooking/Other Items/composetest/Dockerfile
Normal file
8
InterviewBooking/Other Items/composetest/Dockerfile
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM python:3.10-slim
|
||||||
|
WORKDIR /code
|
||||||
|
COPY requirements.txt requirements.txt
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
EXPOSE 5000
|
||||||
|
COPY . .
|
||||||
|
CMD ["python", "app.py"]
|
32
InterviewBooking/Other Items/composetest/app.py
Normal file
32
InterviewBooking/Other Items/composetest/app.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||||
|
|
||||||
|
hit_count = 0 # In-memory counter
|
||||||
|
|
||||||
|
def get_hit_count():
|
||||||
|
global hit_count
|
||||||
|
hit_count += 1
|
||||||
|
return hit_count
|
||||||
|
|
||||||
|
class RequestHandler(BaseHTTPRequestHandler):
|
||||||
|
def do_GET(self):
|
||||||
|
if self.path == '/':
|
||||||
|
count = get_hit_count()
|
||||||
|
response = f'Hello World! I have been seen {count} times.\n'
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header('Content-type', 'text/plain')
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(response.encode('utf-8'))
|
||||||
|
else:
|
||||||
|
self.send_response(404)
|
||||||
|
self.send_header('Content-type', 'text/plain')
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(b'Not Found\n')
|
||||||
|
|
||||||
|
def run(server_class=HTTPServer, handler_class=RequestHandler, port=8000):
|
||||||
|
server_address = ('', port)
|
||||||
|
httpd = server_class(server_address, handler_class)
|
||||||
|
print(f'Starting server on port {port}...')
|
||||||
|
httpd.serve_forever()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run()
|
@ -0,0 +1,8 @@
|
|||||||
|
services:
|
||||||
|
web:
|
||||||
|
container_name: test
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
volumes:
|
||||||
|
- ./:/code
|
64
InterviewBooking/Other Items/interview_database.yaml
Normal file
64
InterviewBooking/Other Items/interview_database.yaml
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
Data:
|
||||||
|
September 16:
|
||||||
|
Meeting Duration: 30 min
|
||||||
|
Meeting Start Times:
|
||||||
|
- 10:00 AM:
|
||||||
|
Interviewer:
|
||||||
|
- Name: John
|
||||||
|
- Email: john@example.com
|
||||||
|
Interviewee:
|
||||||
|
- Name: Jane
|
||||||
|
- Email: jane@example.com
|
||||||
|
Category: PowerDrive
|
||||||
|
Status: Done
|
||||||
|
Slot: 2
|
||||||
|
- 10:30 AM:
|
||||||
|
Interviewer:
|
||||||
|
- Name: Jay
|
||||||
|
- Email: jay@example.com
|
||||||
|
Interviewee:
|
||||||
|
- Name: Zoz
|
||||||
|
- Email: Zoz@example.com
|
||||||
|
Category: PowerTrain
|
||||||
|
Status: Pending
|
||||||
|
Slot: 3
|
||||||
|
- 11:00 AM:
|
||||||
|
Interviewer:
|
||||||
|
- Name: Ali
|
||||||
|
- Email: Ali@example.com
|
||||||
|
Interviewee:
|
||||||
|
- Name: Bob
|
||||||
|
- Email: Bob@example.com
|
||||||
|
Category: Software
|
||||||
|
Status: Rescheduled
|
||||||
|
Slot: 1
|
||||||
|
- 11:30 AM:
|
||||||
|
Interviewer:
|
||||||
|
- Name:
|
||||||
|
- Email:
|
||||||
|
Interviewee:
|
||||||
|
- Name:
|
||||||
|
- Email:
|
||||||
|
Category:
|
||||||
|
Status:
|
||||||
|
Slot: 1
|
||||||
|
- 12:00 PM:
|
||||||
|
Interviewer:
|
||||||
|
- Name: Ali, John
|
||||||
|
- Email: Ali@example.com, Jhon@example.com
|
||||||
|
Interviewee:
|
||||||
|
- Name: Bob
|
||||||
|
- Email: Bob@example.com
|
||||||
|
Category: Software
|
||||||
|
Status: Cancelled
|
||||||
|
Slot: 1
|
||||||
|
- 12:30 PM:
|
||||||
|
Interviewer:
|
||||||
|
- Name: Ali
|
||||||
|
- Email: Ali@example.com
|
||||||
|
Interviewee:
|
||||||
|
- Name: Bob, Jish
|
||||||
|
- Email: Bob@example.com, jish@example.com
|
||||||
|
Category: Software
|
||||||
|
Status: Cancelled
|
||||||
|
Slot: 2
|
BIN
InterviewBooking/Other Items/mock/OR25-L-Interview Data.xlsx
Normal file
BIN
InterviewBooking/Other Items/mock/OR25-L-Interview Data.xlsx
Normal file
Binary file not shown.
17
InterviewBooking/Other Items/testcontainer.py
Normal file
17
InterviewBooking/Other Items/testcontainer.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
getres = requests.get("http://bajacloud.ddnsking.com:43443/getAppointments")
|
||||||
|
print(getres)
|
||||||
|
print(json.dumps(json.loads(getres.text), indent=4))
|
||||||
|
# example of a request
|
||||||
|
# postdata = {
|
||||||
|
# "intervieweeName": "Brock",
|
||||||
|
# "date": "2024-09-16",
|
||||||
|
# "startTime": "11:00:00",
|
||||||
|
# "intervieweeEmail": "darkicewolf50@gmail.com"
|
||||||
|
# }
|
||||||
|
# res = requests.post("http://bajacloud.ddnsking.com:43443/SelectInterview", json.dumps(postdata))
|
||||||
|
# print(res)
|
||||||
|
# print(res.text)
|
25
InterviewBooking/Other Items/testhttp.py
Normal file
25
InterviewBooking/Other Items/testhttp.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import json
|
||||||
|
import requests
|
||||||
|
import timeit
|
||||||
|
|
||||||
|
def BenchMarkServer():
|
||||||
|
rawRes = requests.get("http://localhost:8080/getAppointments")
|
||||||
|
res = json.loads(rawRes.text)
|
||||||
|
print(json.dumps(res, indent=1))
|
||||||
|
|
||||||
|
def BenchMarkDjango():
|
||||||
|
rawRes = requests.get("http://127.0.0.1:8000/getAppointments")
|
||||||
|
res = json.loads(rawRes.text)
|
||||||
|
print(json.dumps(res, indent=1))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test = 1
|
||||||
|
if test:
|
||||||
|
djangoTime = timeit.timeit(stmt=BenchMarkDjango, number=1000)
|
||||||
|
# pythonTime = timeit.timeit(stmt=BenchMarkServer, number=10)
|
||||||
|
print(f"FastAPI: {djangoTime}\nPython: ")
|
||||||
|
|
||||||
|
# reqbody = {
|
||||||
|
# "body": {"message": "hello"}
|
||||||
|
# }
|
||||||
|
# rawRes = requests.post("http://localhost:8000/SelectInterview", reqbody)
|
29
InterviewBooking/Other Items/web1.html
Normal file
29
InterviewBooking/Other Items/web1.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<html lang="en-US">
|
||||||
|
<head>
|
||||||
|
<title>Interview Invitation</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Dear {interviewee_name},</p>
|
||||||
|
<p>Your interview has been scheduled on {date} at {start_time}</p>
|
||||||
|
<p>Please ensure to be available at the designated time.</p>
|
||||||
|
<a
|
||||||
|
href="https://calendar.google.com/calendar/render?action=TEMPLATE&text=Interview+with+{interviewee_name}&dates=20241130T100000Z/20241130T110000Z&details=Interview+with+UCalgary+Baja+Team&location=ENC37"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<button type="button" class="AddtoCal">Add to Google Calendar</button>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://outlook.live.com/calendar/0/deeplink/compose?subject=Interview+with+{interviewee_name}&body=Interview+with+UCalgary+Baja+Team&location=ENC37&startdt=2024-11-30T10:00:00&enddt=2024-11-30T11:00:00"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<button type="button" class="AddtoCal">Add to Outlook Calendar</button>
|
||||||
|
</a>
|
||||||
|
<p>Best regards,</p>
|
||||||
|
<p>UCalgary Baja Interview Team</p>
|
||||||
|
<img
|
||||||
|
src="https://res.cloudinary.com/dpgrgsh7g/image/upload/v1733003224/UCalgaryBAJA_Logo-2024_mpmljh.png"
|
||||||
|
alt="UCalgary Baja Team"
|
||||||
|
height="120svh"
|
||||||
|
/>
|
||||||
|
</body>
|
||||||
|
</html>
|
5
README.MD
Normal file
5
README.MD
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Deprecaited Moved to Central Repo
|
||||||
|
|
||||||
|
Found [Here](https://github.com/UofCBaja/BajaCloud)
|
||||||
|
|
||||||
|
Reason in "Baja Data 2024 - 2025/W - Software/Brock's Learning.docx"
|
@ -1,2 +1,5 @@
|
|||||||
# BajaCloud
|
# Deprecaited Moved to Central Repo
|
||||||
The Docker Project that runs on the Baja Nas
|
|
||||||
|
Found [Here](https://github.com/UofCBaja/BajaCloud)
|
||||||
|
|
||||||
|
Reason in "Baja Data 2024 - 2025/W - Software/Brock's Learning.docx"
|
BIN
bajacloud.tar.gz
Normal file
BIN
bajacloud.tar.gz
Normal file
Binary file not shown.
11
docker-compose.yaml
Normal file
11
docker-compose.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
services:
|
||||||
|
bajacloud:
|
||||||
|
container_name: uofcbajacloud
|
||||||
|
image: darkicewolf50/uofcbajacloud:latest
|
||||||
|
ports:
|
||||||
|
- 43443:8000
|
||||||
|
volumes:
|
||||||
|
- ./L - Logistics/Interviews:/Interviews
|
||||||
|
# networks:
|
||||||
|
# - testnet
|
||||||
|
build: . # do not include in delpoyment version
|
Loading…
x
Reference in New Issue
Block a user