Commit c9c076c5 authored by Jack Otto's avatar Jack Otto
Browse files

11/29/21 jack changes

parent ff6952f3
from ortools.sat.python import cp_model
from ortools.sat.python.cp_model import CpModel
# import inputs
#from inputs import classes, classrooms, professors
from ..inputs.spring_2022_inputs import classes, classrooms, professors
from .schedule_objects import Course, Room, Professor
from .tagged_objects import ScheduleObject
def full_schedule():
# create the model
model: CpModel = cp_model.CpModel()
profs = []
courses = []
rooms = []
day_names = ['Mo', 'Tu', 'We', 'Th', 'Fr']
hours = ['08', '09', '10', '11', '12', '13', '14', '15', '16']
ideal_hours = ['10', '11', '12', '14', '15', '09', '08', '16', '13']
for name, length, size, req, days, color in classes:
courses.append(Course(name, size, req, length, days, color))
for room, capacity, feature, unavailable in classrooms:
rooms.append(Room(room, capacity, feature, unavailable))
for name, load, teachable in professors:
profs.append(Professor(name, load, teachable))
# Array to store all variables
scheduled_classes = {}
for n in courses:
for day in day_names:
for hour in ideal_hours:
for c in rooms:
for p in profs:
scheduled_classes[(n, c, p, day, hour)] = model.NewBoolVar(f'{n}_{c}_{p}_{day}_{hour}')
print("Adding Class must be taught constraint...")
## The class must be taught
for n in courses:
num_classes_taught = 0
for c in rooms:
for p in profs:
for h in hours:
for d in day_names:
num_classes_taught += scheduled_classes[(n, c, p, d, h)]
model.Add(num_classes_taught == len(n.days))
print("Adding same time/day across week constraint...")
## Must be taught at the same time/room/prof each day it needs to be taught
for n in courses:
for c in rooms:
for p in profs:
for h in hours:
days_class_is_taught = []
for d in n.days:
days_class_is_taught.append(scheduled_classes[(n, c, p, d, h)])
for i in range(len(days_class_is_taught) - 1):
model.Add(days_class_is_taught[i] == days_class_is_taught[i + 1])
print("Preventing impossible conditions...")
for n in courses:
for c in rooms:
for p in profs:
for d in day_names:
for h in hours:
# constraint to make sure the classes fit in the classroom and have the right features
if n.size > c.capacity or n.room_requirement != c.features:
model.Add(scheduled_classes[(n, c, p, d, h)] == 0)
## The class must be taught on the right days that is is offered
if d not in n.days:
model.Add(scheduled_classes[(n, c, p, d, h)] == 0)
## Makes sure the professor only teaches the right classes
if n.name not in p.teachable_classes:
model.Add(scheduled_classes[(n, c, p, d, h)] == 0)
if (d, h) in c.unavailable_times:
model.Add(scheduled_classes[(n, c, p, d, h)] == 0)
# ## Makes the teacher teach below or equal to their possible load
# for p in profs:
# courses_taught = 0
# true_load = p.teaching_load
# for n in courses:
# for c in rooms:
# for h in hours:
# for d in day_names:
# courses_taught += scheduled_classes[(n, c, p, d, h)]
# true_load += scheduled_classes[(n, c, p, n.days[0], h)] * (len(n.days) - 1)
# model.Add(courses_taught <= true_load)
print("Adding no overbooking classrooms Constraint...")
## No overbooking of a classroom for a specific time and day
for c in rooms:
for d in day_names:
for h in hours:
room_used = 0
for n in courses:
for p in profs:
room_used += scheduled_classes[n, c, p, d, h]
model.Add(room_used <= 1)
print("Adding no overbooking prof Constraint")
## No overbooking of a prof for a specific time and day
for p in profs:
for d in day_names:
for h in hours:
prof_used = 0
for n in courses:
for c in rooms:
prof_used += scheduled_classes[n, c, p, d, h]
model.Add(prof_used <= 1)
print("Adding no letting classes go past 5pm constraint")
## No scheduling classes if their length will take them past end of work day
for n in courses:
if n.length != 1:
for c in rooms:
for p in profs:
for d in n.days:
last_time = len(hours)-n.length+1
for delta_t in range(last_time, len(hours)):
model.Add(scheduled_classes[(n, c, p, d, hours[delta_t])] == 0)
print("Adding constraint to not allow sections of same class to be taught together")
## Ensure Sections of each class are not taught at the same time
for n in courses:
for d in day_names:
for h in hours:
sections_taught_at_this_time = []
for c in rooms:
for p in profs:
classname = n.name.split(" ")
classname = classname[0] + classname[1]
for section in [sn for sn in courses if sn.name.split(" ")[0] + sn.name.split(" ")[1] == classname]:
sections_taught_at_this_time.append(scheduled_classes[section, c, p, d, h])
model.Add(sum(sections_taught_at_this_time) <= 1)
print("Adding constraint for classes longer than an hour")
# Make sure if a class is longer than an hour that a class can not be taught in the time span of that class --> slow working version
for n in courses:
if n.length != 1:
for c in rooms:
for p in profs:
for d in n.days:
for h in hours[0:len(hours) - n.length+1]:
subsequent_hours_of_classes=[]
for delta_t in range(1, n.length):
dh = str(int(h) + delta_t)
dh = str(((2-len(dh))*"0") + dh)
# ALL classes with the prof and room need to be 0
for other_n in courses:
for other_p in profs:
subsequent_hours_of_classes.append(scheduled_classes[other_n, c, other_p, d, dh])
model.Add(scheduled_classes[n, c, p, d, h] + sum(subsequent_hours_of_classes) <= 1)
# # Classes Longer than an hour should block out the following classes --> fast meh version
# for n in [classes for classes in courses if classes.length > 1]:
# for c in [room for room in rooms if n.size <= room.capacity or n.room_requirement == room.features]:
# for p in [professor for professor in profs if n in professor.teachable_classes]:
# for d in n.days:
# for h in hours[0:len(hours) - n.length+1]:
# subsequent_hours_of_classes=[]
# for delta_t in range(1, n.length):
# dh = str(int(h) + delta_t)
# dh = str(((2-len(dh))*"0") + dh)
#
# ## TODO FIX THIS CONSTRAINT
#
# # ALL classes with the prof and room need to be 0
# for other_n in courses:
# for other_p in profs:
# subsequent_hours_of_classes.append(scheduled_classes[other_n, c, other_p, d, dh])
#
# model.Add(scheduled_classes[n, c, p, d, h] + sum(subsequent_hours_of_classes) <= 1)
print("Adding constraint for senior design common hour to be blocked off")
for p in profs:
if "MECH 402C 10" in p.teachable_classes:
for n in courses:
for c in rooms:
model.Add(scheduled_classes[n, c, p, "Th", "13"] == 0)
model.Add(scheduled_classes[n, c, p, "Th", "14"] == 0)
print("Adding constraint for senior design class room common hour to be blocked off")
for c in rooms:
if c == "DANA137" or c == "DANA115":
for n in courses:
for p in profs:
model.Add(scheduled_classes[n, c, p, "Th", "13"] == 0)
model.Add(scheduled_classes[n, c, p, "Th", "14"] == 0)
print("Adding constraint to not schedule classes of same color at the same times")
for n in courses:
for d in day_names:
for h in hours:
classes_of_same_year = []
for c in rooms:
for p in profs:
#only want one class for each year to be taught at a time
for color_year in [cy for cy in courses if cy.color == n.color]:
classes_of_same_year.append(scheduled_classes[color_year, c, p, d, h])
model.Add(sum(classes_of_same_year) <= 1)
## Fix a class at a specific thing
#model.Add(scheduled_classes[(courses[1], rooms[0], profs[4], "Mo", "10")] == 1)
print("All constraints added.")
print("Thinking...")
solver = cp_model.CpSolver()
status = solver.Solve(model)
print(status)
final_schedule = []
solver.Solve(model)
for n in courses:
for d in day_names:
for h in hours:
for c in rooms:
for p in profs:
if solver.Value(scheduled_classes[(n, c, p, d, h)]) == 1:
print(f'{n} is taught with {p} in {c} on {d} at {h}')
# final_schedule.append(ScheduleObject(n.name, c.name, p.name, d, h, str(int(h)+n.length)))
final_schedule.append(ScheduleObject(n, c, p, d, h))
final_schedule.append(ScheduleObject(Course("MECH 402C 10", 20, "", 2, ["Th"], 4), Room("DANA115", 20, "", []), Professor("Senior Design Profs", 0, []), "Th", "13"))
final_schedule.append(ScheduleObject(Course("MECH 402C 11", 20, "", 2, ["Th"], 4), Room("DANA137", 20, "", []), Professor("Senior Design Profs", 0, []), "Th", "13"))
for event in final_schedule:
event.create_event()
# with open("tags_output.html", 'w') as file:
# for d in day_names:
# if d == "Mo":
# tempDay = "Monday"
# elif d == "Tu":
# tempDay = "Tuesday"
# elif d == "We":
# tempDay = "Wednesday"
# elif d == "Th":
# tempDay = "Thursday"
# else:
# tempDay = "Friday"
# file.write("<li class=" + '"' + "cd-schedule__group" + '"' + ">\n\t<div class=" + '"' + "cd-schedule__top-info" + '"' + "><span>" + tempDay + "</span></div>\n\n\t<ul>\n")
# for h in hours:
# file.write("\t\t<div class="+ '"' + "flex-container" + '"' + ">\n")
# for classObject in final_schedule:
# if classObject.day_taught == d:
# if classObject.hour_taught == h:
# file.write(repr(classObject))
# if d == "Th" and h == "13":
# file.write("\t\t<li class=" + '"' + "cd-schedule__event" + '"'">\n\t\t\t<a data-start=" + '"' + "13:00" + '"' + " data-end=" + '"' + "15:00" + '"' + " data-content=" + '"common hour profs"' + " data-event=" + '"DANA115"' + " href=" + '"' + "#0" + '"' + ">\n\t\t\t\t<em class=" + '"' + "cd-schedule__name" + '"' + ">" + "MECH 402C 10" + "</em>\n\t\t\t</a>\n\t\t</li>\n")
# file.write("\t\t<li class=" + '"' + "cd-schedule__event" + '"'">\n\t\t\t<a data-start=" + '"' + "13:00" + '"' + " data-end=" + '"' + "15:00" + '"' + " data-content=" + '"common hour profs"' + " data-event=" + '"DANA137"' + " href=" + '"' + "#0" + '"' + ">\n\t\t\t\t<em class=" + '"' + "cd-schedule__name" + '"' + ">" + "MECH 402C 11" + "</em>\n\t\t\t</a>\n\t\t</li>\n")
# file.write("\t\t</div>\n")
# file.write("\t</ul>\n</li>\n")
if __name__ == "__main__":
full_schedule()
......@@ -4,7 +4,6 @@ from ..gcalendar.cal_setup import get_calendar_service
from datetime import datetime, timedelta
from googleapiclient import errors
from .schedule_objects import Course, Room, Professor
from .tagged_objects import ScheduleObject
class Schedule:
def __init__(self, courses, rooms, profs, days, hours):
......@@ -19,12 +18,13 @@ class Schedule:
for n in self.courses:
for day in self.days:
for hour in ['10', '11', '12', '14', '15', '09', '08', '16', '13']: ## This is the ideal arrangement of hours
for hour in ['10', '11', '12', '14', '15', '09', '08', '16',
'13']: ## This is the ideal arrangement of hours
for c in self.rooms:
for p in self.profs:
self.scheduled_classes[(n, c, p, day, hour)] = self.model.NewBoolVar(
f'{n}_{c}_{p}_{day}_{hour}')
self.initialize_constraints()
def initialize_constraints(self):
......@@ -163,20 +163,22 @@ class Schedule:
for c in [room for room in self.rooms if n.size <= room.capacity or n.room_requirement == room.features]:
for p in [professor for professor in self.profs if n in professor.teachable_classes]:
for d in n.days:
for h in self.hours[0:len(self.hours) - n.length+1]:
subsequent_hours_of_classes=[]
for h in self.hours[0:len(self.hours) - n.length + 1]:
subsequent_hours_of_classes = []
for delta_t in range(1, n.length):
dh = str(int(h) + delta_t)
dh = str(((2-len(dh))*"0") + dh)
dh = str(((2 - len(dh)) * "0") + dh)
## TODO FIX THIS CONSTRAINT
# ALL classes with the prof and room need to be 0
for other_n in self.courses:
for other_p in self.profs:
subsequent_hours_of_classes.append(self.scheduled_classes[other_n, c, other_p, d, dh])
self.model.Add(self.scheduled_classes[n, c, p, d, h] + sum(subsequent_hours_of_classes) <= 1)
subsequent_hours_of_classes.append(
self.scheduled_classes[other_n, c, other_p, d, dh])
self.model.Add(
self.scheduled_classes[n, c, p, d, h] + sum(subsequent_hours_of_classes) <= 1)
print("Adding constraint for senior design common hour to be blocked off")
for p in self.profs:
......@@ -226,15 +228,13 @@ class Schedule:
for p in self.profs:
if self.solver.Value(self.scheduled_classes[(n, c, p, d, h)]) == 1:
print(f'{n} is taught with {p} in {c} on {d} at {h}')
self.create_event(n,c,p,d,h) # Create GCAL events
## TODO Manually add these events
#final_schedule.append(ScheduleObject(Course("MECH 402C 10", 20, "", 2, ["Th"], 4), Room("DANA115", 20, "", []), Professor("Senior Design Profs", 0, []), "Th", "13"))
#final_schedule.append(ScheduleObject(Course("MECH 402C 11", 20, "", 2, ["Th"], 4), Room("DANA137", 20, "", []), Professor("Senior Design Profs", 0, []), "Th", "13"))
self.create_event(Course("MECH 402C 10", 20, "", 2, ["Th"], 4), Room("DANA115", 20, "", []), Professor("Senior Design Profs", 0, []), "Th", "13")
self.create_event(Course("MECH 402C 11", 20, "", 2, ["Th"], 4), Room("DANA137", 20, "", []), Professor("Senior Design Profs", 0, []), "Th", "13")
self.create_event(n, c, p, d, h) # Create GCAL events
self.create_event(Course("MECH 402C 10", 20, "", 2, ["Th"], 4), Room("DANA115", 20, "", []),
Professor("Senior Design Profs", 0, []), "Th", "13")
self.create_event(Course("MECH 402C 11", 20, "", 2, ["Th"], 4), Room("DANA137", 20, "", []),
Professor("Senior Design Profs", 0, []), "Th", "13")
def print_schedule(self):
status = self.solver.Solve(self.model)
......@@ -254,7 +254,7 @@ class Schedule:
service = get_calendar_service()
dt_now = datetime.now().date()
dayOffset=0
dayOffset = 0
if d == "Mo":
dayOffset = 0
elif d == "Tu":
......@@ -267,7 +267,7 @@ class Schedule:
dayOffset = 4
# using 11/1/21 as the date to start the calendar on because 11/1/21 is a monday and thus can use the days the class is taught on to determine the day offset
setTime = datetime(dt_now.year, 11, 1+dayOffset, int(h))
setTime = datetime(dt_now.year, 11, 1 + dayOffset, int(h))
start = setTime.isoformat()
end = (setTime + timedelta(hours=n.length)).isoformat()
......@@ -281,7 +281,7 @@ class Schedule:
}
).execute()
#write the id to a file to save it
# write the id to a file to save it
with open("./backend/scheduler/eventID.txt", 'a') as file:
file.write(event_result['id'])
file.write("\n")
......@@ -314,7 +314,7 @@ class Schedule:
for n in self.courses:
if n.name == name:
return n
for c in self.rooms:
if c.name == name:
return c
......
from datetime import datetime, timedelta
from googleapiclient import errors
from ..gcalendar.cal_setup import get_calendar_service
class ScheduleObject:
def __init__(self, course, room, professor, day, hour):
self.course = course
self.room = room
self.professor = professor
self.day_taught = day
self.hour_taught = hour
def __str__(self):
return self.course.name + " in " + self.room.name + " with " + self.professor + " at " + self.day_taught + " " + self.hour_taught
def __repr__(self):
return str(self)
def create_event(self):
# creates an event that lasts the length of the class
service = get_calendar_service()
d = datetime.now().date()
dayOffset=0
if self.day_taught == "Mo":
dayOffset = 0
elif self.day_taught == "Tu":
dayOffset = 1
elif self.day_taught == "We":
dayOffset = 2
elif self.day_taught == "Th":
dayOffset = 3
else:
dayOffset = 4
# using 11/1/21 as the date to start the calendar on because 11/1/21 is a monday and thus can use the days the class is taught on to determine the day offset
setTime = datetime(d.year, 11, 1+dayOffset, int(self.hour_taught))
start = setTime.isoformat()
end = (setTime + timedelta(hours=self.course.length)).isoformat()
event_result = service.events().insert(calendarId='jlitoptimization@gmail.com',
body={
"summary": self.course.name,
"description": 'Taught in ' + self.room.name + ' by Professor ' + self.professor.name,
"start": {"dateTime": start, "timeZone": 'America/New_York'},
"end": {"dateTime": end, "timeZone": 'America/New_York'},
"colorId": self.course.color,
}
).execute()
#write the id to a file to save it
with open("./backend/scheduler/eventID.txt", 'a') as file:
file.write(event_result['id'])
file.write("\n")
print("created event")
print("id: ", event_result['id'])
print("summary: ", event_result['summary'])
print("starts at: ", event_result['start']['dateTime'])
print("ends at: ", event_result['end']['dateTime'])
def delete_events():
service = get_calendar_service()
file = open("./backend/scheduler/eventID.txt")
for line in file:
removed_id = line.strip()
try:
service.events().delete(
calendarId='jlitoptimization@gmail.com',
eventId=str(removed_id),
).execute()
print("Event deleted")
except errors.HttpError:
print("Failed to delete event")
file.close()
file = open("./backend/scheduler/eventID.txt", "w")
file.close()
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment