Merge branch 'SelectAppointment' into Packagers

This commit is contained in:
darkicewolf50 2024-11-30 14:08:50 -07:00
commit 85ee822e5c
12 changed files with 416 additions and 0 deletions

44
ConversionToExcel.py Normal file
View 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.")

11
TestBookAppointment.py Normal file
View 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()

141
WriteDB.py Normal file
View File

@ -0,0 +1,141 @@
import pandas as pd
import json
from openpyxl import load_workbook
from send_email import send_email
from filelock import FileLock
# Define the path to the Excel file and the lock file
file_path = "./interview_database.xlsx"
lock_path = "./interview_database.xlsx.lock" # Lock file for synchronization
def ReadDatabase():
"""
Reads the Database to retrieve available interview slots.
``REQUIRES``: None
``PROMISES``: JSON (Available interview slots)
``Developed by``: Ahmad
``Contact``: ahmad.ahmad1@ucalgary.ca
"""
# 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 = {}
# 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'])
# 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']).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(date, start_time, interviewee_name, interviewee_email):
"""
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)
``PROMISES``: Updates the Excel file with the new interviewee's name and email if there is an available slot. Returns Bool.
``Developed by``: Ahmad
``Contact``: ahmad.ahmad1@ucalgary.ca
"""
available_slots = ReadDatabase()
# 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 "Sheet1" for updating appointments
workbook = load_workbook(file_path)
sheet = workbook["Sheet1"]
df = pd.read_excel(file_path)
# 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'])
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']).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') + 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
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.
"""
print("Available Slots:")
available_slots = ReadDatabase()
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 = "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)
# Test Case 2: Append to an available slot on 2024-09-17 at 13:30:00
date_2 = "2024-09-17"
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(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 = "2024-09-16"
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(date_3, start_time_3, interviewee_name_3, interviewee_email_3)
# Run tests
if __name__ == "__main__":
run_tests()

Binary file not shown.

Binary file not shown.

8
composetest/Dockerfile Normal file
View 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"]

33
composetest/app.py Normal file
View File

@ -0,0 +1,33 @@
import time
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()

View File

@ -0,0 +1,8 @@
services:
web:
container_name: test
build: .
ports:
- "8000:8000"
volumes:
- ./:/code

View File

64
interview_database.yaml Normal file
View 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

82
send_email.py Normal file
View File

@ -0,0 +1,82 @@
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from datetime import datetime, timedelta
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"):
"""
Sends an email notification to the interviewee and a static Gmail account.
``REQUIRES``: interviewee_email (str), interviewee_name (str), date (str), start_time (str)
``PROMISES``: Sends an email to interviewee and static email on successful appointment booking.
"""
# Define static email for notifications and Gmail credentials
static_email = "uofcbaja.noreply@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://picsum.photos/200/" alt="UCalgary Baja Team" />
</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()

25
web1.html Normal file
View File

@ -0,0 +1,25 @@
<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://picsum.photos/200/" alt="UCalgary Baja Team" />
</body>
</html>