import abc import datetime from typing import List import TermInfo from Constants import * # All constants, and only constants, are in ALL_CAPS. from ScheduleSession import Session, RegularClassSession, ReviewSession, \ EveningExamSession, InClassExamSession, CapstoneProjectSession class ScheduleDay(abc.ABC): """ Data associated with a single day in the Schedule. """ def __init__(self, date: datetime.date = None, session: Session = None, message: str = None): self.date = date self.session = session self.message = message def __repr__(self): return "{}/{} {}".format(self.date.month, self.date.day, self.session) def date_as_string(self): return self.date.strftime('%B') + " " + str(self.date.day) @abc.abstractmethod def make_html(self) -> str: """ Returns the HTML for this ScheduleDay. """ # Subclasses must implement this method. def make_html_for_topics(self): # Most of the following HTML should be in the templates, not here. if len(self.session.topics) == 1: html = '
\n' else: html = '
\n' html += " \n" else: current_level = 0 for k in range(len(self.session.topics)): topic = self.session.topics[k] # Down a level: if current_level == 1 and topic.level == 2: html += '\n \n \n" # Same level: elif current_level == topic.level: html += "\n" current_level = topic.level # Now do the item: if current_level == 1: html += '
  • ' html += topic.topic elif current_level == 2: html += '
  • ' html += topic.topic elif current_level == 3: html += ' ' html += topic.topic html += "" html += "
  • \n" if current_level >= 3: html += " \n \n" if current_level >= 2: html += " \n \n" if current_level >= 1: html += " \n" html += "
    " return html class WeekNumberDay(ScheduleDay): def __init__(self, week_number): self.week_number = week_number super().__init__() with open(SCHEDULE_WEEK_NUMBER_TEMPLATE, "r") as file_handle: self.template = file_handle.read() def make_html(self) -> str: if self.week_number >= 0: return self.template.replace("__WEEK_NUMBER__", str(self.week_number)) else: return "   " class BeforeClassesStartDay(ScheduleDay): def __init__(self, date: datetime.date): super().__init__(date) with open(SCHEDULE_NO_CLASS_TEMPLATE, "r") as file_handle: self.template = file_handle.read() def make_html(self) -> str: return self.template.replace( "DATE", self.date_as_string()) class BreakDay(ScheduleDay): def __init__(self, date: datetime.date, term: str): super().__init__(date) self.term = term with open(SCHEDULE_BREAK_TEMPLATE, "r") as file_handle: self.template = file_handle.read() def make_html(self) -> str: return self.template.replace( "TERM", self.term).replace( "DATE", self.date_as_string()) class SessionDay(ScheduleDay): def __init__(self, date: datetime.date, session: Session, template_name: str): super().__init__(date, session) with open(template_name, "r") as file_handle: self.template = file_handle.read() def get_two_digit_session_number(self): if self.session.session_number < 10: return "0{}".format(self.session.session_number) else: return "{}".format(self.session.session_number) def make_html(self) -> str: two_digit_number = self.get_two_digit_session_number() return self.template.replace( "SESSION_NUMBER", str(self.session.session_number)).replace( "SESSION_DATE", self.date_as_string()).replace( "SESSION_FOLDER_NUMBER", two_digit_number).replace( "SESSION_TITLE", self.session.session_title).replace( "SESSION_TOPICS", self.make_html_for_topics()) class RegularClassSessionDay(SessionDay): def __init__(self, date: datetime.date, session: Session): super().__init__(date, session, SCHEDULE_SESSION_TEMPLATE) # CONSIDER: Can I combine some of the functionality of this into SessionDay? class EveningExamSessionDay(SessionDay): def __init__(self, date: datetime.date, session: EveningExamSession): super().__init__(date, session, SCHEDULE_EXAM_TEMPLATE) self.session = session # Redundant, here to set the type for type hints def make_html(self) -> str: html = super().make_html() return html.replace( "__EXAM_NUMBER__", str(self.session.exam_number)).replace( "__EXAM_DAY_OF_WEEK__", self.session.day_of_week).replace( "__EXAM_DATE__", self.session.date).replace( "__EXAM_TIME__", self.session.time).replace( "__REGULAR_CLASS_DAY__", self.session.no_regular_class) class ReviewSessionDay(RegularClassSessionDay): pass class InClassExamSessionDay(RegularClassSessionDay): pass class CapstoneProjectSessionDay(RegularClassSessionDay): pass class Schedule: def __init__(self, term_info: TermInfo, sessions: List[Session]): self.term_info = term_info self.sessions = sessions with open(SCHEDULE_TEMPLATE, "r") as file_handle: self.template = file_handle.read() self.template = self.template.replace(" SESSIONS", "SESSIONS") with open(SCHEDULE_NO_CLASS_TEMPLATE, "r") as file_handle: self.no_class_template = file_handle.read() with open(SCHEDULE_BREAK_TEMPLATE, "r") as file_handle: self.break_template = file_handle.read() self.schedule_days = self.make_schedule_days() def make_schedule_days(self) -> List[ScheduleDay]: # Start on the Sunday prior to the first day of the term: if self.term_info.first_day_of_term.weekday() == 3: # Fall term date = self.term_info.first_day_of_term - datetime.timedelta(4) else: date = self.term_info.first_day_of_term - datetime.timedelta(1) session_index = 0 # For indexing through the Sessions schedule_days = [] # From Sunday of Week 1 to Friday of Week 10 if self.term_info.term == "Fall": week_number = 0 # Fall term starts on a Thursday, not Monday else: week_number = 1 previous_week_number = -1 while True: # Go through each date from the start of term to end of term. # Done when we get to the last date of the term. if date > self.term_info.last_day_of_tenth_week: break # Deal only with dates that are on class days-of-week. # But treat Sunday as the week number. if date.weekday() == 6: if week_number == previous_week_number: schedule_days.append(WeekNumberDay(-1)) else: previous_week_number = week_number schedule_days.append(WeekNumberDay(week_number)) if date.weekday() in self.term_info.class_meeting_days_of_week: if date < self.term_info.first_day_of_term: schedule_days.append(BeforeClassesStartDay(date)) elif self.term_info.start_date_of_break <= date \ <= self.term_info.end_date_of_break: schedule_days.append(BreakDay(date, self.term_info.term)) else: # CONSIDER: Can make the following more elegant. session = self.sessions[session_index] if isinstance(session, RegularClassSession): day_type = RegularClassSessionDay elif isinstance(session, ReviewSession): day_type = ReviewSessionDay elif isinstance(session, EveningExamSession): day_type = EveningExamSessionDay elif isinstance(session, InClassExamSession): day_type = InClassExamSessionDay elif isinstance(session, CapstoneProjectSession): day_type = CapstoneProjectSessionDay else: raise ValueError("Unknown session type") print("day type is:", day_type) schedule_days.append(day_type(date, session)) session_index = session_index + 1 if session_index % 3 == 2: previous_week_number = week_number week_number = week_number + 1 date = date + datetime.timedelta(1) return schedule_days def make_html(self) -> str: days_of_week = self.term_info.class_meeting_days_of_week template = self.template.replace( "DAY-OF-WEEK-1", DAYS_OF_WEEK[days_of_week[0]]).replace( "DAY-OF-WEEK-2", DAYS_OF_WEEK[days_of_week[1]]).replace( "DAY-OF-WEEK-3", DAYS_OF_WEEK[days_of_week[2]]) html = "" for day in self.schedule_days: html += day.make_html() html = template.replace("SESSIONS", html) return html