import datetime
import re
from dataclasses import dataclass, field
raw_data = ['ARTS 111 A', 'M', '09:00 - 12:00', 'W', '09:00 - 12:00', 'F', '02:00 - 12:00',
'COMP 111 A', 'M', '09:00 - 12:00', 'W', '09:00 - 12:00',
'COMP 200 A', 'M', '09:30 - 11:30', 'W', '09:00 - 12:00']
# the data is not structured, so let's parse it !
days_letters = ('M', 'T', 'W', 'H', 'F') # 'H' used for tHursday
timerange_pattern = re.compile(r"(\d\d):(\d\d) - (\d\d):(\d\d)")
# group(1) ^^^^ 2^^^^ 3^^^^ 4^^^^
coursename_pattern = re.compile(r"(\w+\s+\d+\s+\w)")
# group(1) ^^^^^^^^^^^^^^
@dataclass
class Course:
name: str
slots: list = field(default_factory=list)
@dataclass
class CourseSlot:
day: str
time_start: datetime.time
time_end: datetime.time
tokens = list(raw_data)
courses = []
while tokens:
token = tokens.pop(0) # get the first string
course_name_match = coursename_pattern.fullmatch(token)
assert course_name_match is not None
course_name = course_name_match.group(1)
course = Course(name=course_name)
# then read the days and hours
while tokens:
if tokens[0] in days_letters:
day = tokens.pop(0)
timerange_match = timerange_pattern.fullmatch(tokens.pop(0))
assert timerange_match is not None
start_hour = int(timerange_match.group(1))
start_minute = int(timerange_match.group(2))
end_hour = int(timerange_match.group(3))
end_minute = int(timerange_match.group(4))
course.slots.append(CourseSlot(
day=day,
time_start=datetime.time(hour=start_hour, minute=start_minute),
time_end=datetime.time(hour=end_hour, minute=end_minute),
))
else:
break
courses.append(course)
print(courses)
# [Course(name='ARTS 111 A', slots=[CourseSlot(day='M', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
# CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
# CourseSlot(day='F', time_start=datetime.time(2, 0), time_end=datetime.time(12, 0))]),
# Course(name='COMP 111 A', slots=[CourseSlot(day='M', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
# CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0))]),
# Course(name='COMP 200 A', slots=[CourseSlot(day='M', time_start=datetime.time(9, 30), time_end=datetime.time(11, 30)),
# CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0))])]
@dataclass
class Clash:
course1_name: str
course2_name: str
course1_slot: CourseSlot
course2_slot: CourseSlot
# now search for overlap :
clashes = []
for i, first_course in enumerate(courses[:-1], start=1): # from first to last-1
for second_course in courses[i:]: # from after the first to the last
for first_slot in first_course.slots:
for second_slot in second_course.slots:
if first_slot.day == second_slot.day:
if first_slot.time_start <= second_slot.time_end and \
second_slot.time_start <= first_slot.time_end: # see https://stackoverflow.com/a/3269471/11384184
# we have a match !
clashes.append(Clash(course1_name=first_course.name,
course2_name=second_course.name,
course1_slot=first_slot,
course2_slot=second_slot))
print(clashes)
# [
# Clash(course1_name='ARTS 111 A',
# course2_name='COMP 111 A',
# course1_slot=CourseSlot(day='M', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
# course2_slot=CourseSlot(day='M', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0))
# ),
# Clash(course1_name='ARTS 111 A',
# course2_name='COMP 111 A',
# course1_slot=CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
# course2_slot=CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0))
# ),
# Clash(course1_name='ARTS 111 A',
# course2_name='COMP 200 A',
# course1_slot=CourseSlot(day='M', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
# course2_slot=CourseSlot(day='M', time_start=datetime.time(9, 30), time_end=datetime.time(11, 30))
# ),
# Clash(course1_name='ARTS 111 A',
# course2_name='COMP 200 A',
# course1_slot=CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
# course2_slot=CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0))
# ),
# Clash(course1_name='COMP 111 A',
# course2_name='COMP 200 A',
# course1_slot=CourseSlot(day='M', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
# course2_slot=CourseSlot(day='M', time_start=datetime.time(9, 30), time_end=datetime.time(11, 30))
# ),
# Clash(course1_name='COMP 111 A',
# course2_name='COMP 200 A',
# course1_slot=CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
# course2_slot=CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0))
# )
# ]