Source code for derobertis_cv.pltemplates.syllabus

from typing import Optional

import pyexlatex as pl

from derobertis_cv.models.course import CourseModel


[docs] class Syllabus(pl.Template):
[docs] def __init__(self, model: CourseModel): self.model = model self.add_package(pl.Package("hyperref", modifier_str="hidelinks=true")) self.contents = self._get_contents() super().__init__()
def _get_contents(self): return [ self.overview_table, self.course_materials_section, pl.VSpace(), self.general_infomation_section, pl.VSpace(), self.grading_section, pl.VSpace(), self.topics_covered_section, self.resources_section, ] @property def overview_table(self) -> pl.Tabular: align = "C{5.5cm}" * 3 return pl.Tabular( [ pl.TopRule(), pl.TableLineBreak(), pl.TableLineBreak(), pl.ValuesTable.from_list_of_lists( [ [ f'{pl.TextSize(2)} {pl.Bold("Instructor")}', f'{pl.TextSize(2)} {pl.Bold("Email")}', f'{pl.TextSize(2)} {pl.Bold("Office Hours")}', ], ] ), # pl.TextSize(0), pl.TableLineBreak(), pl.ValuesTable.from_list_of_lists( [ [ self.model.instructor, self.model.instructor_email, self.model.office_hours, ], ] ), pl.TableLineBreak(), pl.TableLineBreak(), # pl.BottomRule() ], align=align, ) @property def course_materials_section(self) -> pl.Section: contents = [] if self.model.website_url is not None: contents.append( pl.SubSection( [ pl.TextColor( pl.Underline( pl.Hyperlink( self.model.website_url, self.model.website_url ) ), "blue", ), pl.UnorderedList( [ "The course website includes all of the materials for the course, including the " "course schedule, lecture videos, lab exercises, practice problems, and projects." ] ), ], title="Course Website", ) ) if self.model.textbook is not None: contents.append( pl.SubSection( [ self.model.textbook.to_str(), pl.UnorderedList([self.model.textbook.description]), ], title="Textbook", ) ) if self.model.daily_prep is not None: contents.append( pl.SubSection([self.model.daily_prep], title="Prepare for Class") ) return pl.Section(contents, title="Course Materials") @property def general_infomation_section(self) -> pl.Section: contents = [] if self.model.long_description is not None: contents.append( pl.SubSection(self.model.long_description, title="Description") ) if self.model.prerequisites is not None: prereq_contents = [] if self.model.prerequisites.courses_description is not None: prereq_contents.append( pl.SubSubSection( self.model.prerequisites.courses_description, title="Courses" ) ) if self.model.prerequisites.technical_skills_description is not None: prereq_contents.append( pl.SubSubSection( self.model.prerequisites.technical_skills_description, title="Technical Skills", ) ) if self.model.prerequisites.technical_skills: prereq_contents.append(self.prereq_list) if prereq_contents is not None: contents.append(pl.SubSection(prereq_contents, title="Prerequisites")) if self.model.class_structure_body is not None: contents.append( pl.SubSection(self.model.class_structure_body, title="Class Structure") ) return pl.Section(contents, title="General Information") @property def prereq_list(self) -> Optional[pl.UnorderedList]: if ( self.model.prerequisites is None or self.model.prerequisites.technical_skills is None ): return None bullet_items = [] for i, cat in enumerate(self.model.prerequisites.technical_skills): if cat.parents: continue if not cat.children: bullet_items.append(cat.title) else: child_items = [c.title for c in cat.children] bullet_items.extend([cat.title, pl.UnorderedList(child_items)]) if bullet_items: return pl.UnorderedList(bullet_items) return None @property def grade_breakdown_table(self) -> Optional[pl.Tabular]: if self.model.grading is None or self.model.grading.breakdown is None: return None align = "lc" return pl.Tabular( [ pl.TopRule(), pl.ValuesTable.from_list_of_lists( [[pl.Bold("Item"), pl.Bold("Grade Percentage")]] ), pl.MidRule(), pl.ValuesTable.from_list_of_lists( [ [category, f"{pct:.0%}"] for category, pct in self.model.grading.breakdown.categories.items() ] ), pl.BottomRule(), ], align=align, ) @property def grading_scale_table(self) -> Optional[pl.Tabular]: if self.model.grading is None or self.model.grading.scale is None: return None align = "lc" return pl.Tabular( [ pl.TopRule(), pl.ValuesTable.from_list_of_lists( [[pl.Bold("Grade"), pl.Bold("Grade Percentage")]] ), pl.MidRule(), pl.ValuesTable.from_list_of_lists( [ [grade, f"{bot} - {top}%"] for grade, ( bot, top, ) in self.model.grading.scale.grade_ranges.items() ] ), pl.BottomRule(), ], align=align, ) @property def grading_section(self) -> pl.Section: bd_table = self.grade_breakdown_table scale_table = self.grading_scale_table contents = [] if bd_table is not None: contents.append(pl.SubSection(pl.Center(bd_table), title="Breakdown")) if scale_table is not None: contents.append( pl.SubSection(pl.Center(scale_table), title="Grading Scale") ) if self.model.grading is not None and self.model.grading.extra_info is not None: contents.append(self.model.grading.extra_info) return pl.Section(contents, title="Grades") @property def topics_covered_section(self) -> pl.Section: topic_sections = [] if self.model.topics is not None: for topic in self.model.topics: child_topics = list(topic.children) if child_topics: topic_contents = pl.UnorderedList([t.title for t in child_topics]) else: topic_contents = [] topic_section = pl.SubSection(topic_contents, title=topic.title) topic_sections.append(topic_section) contents = ["Subject to change but in approximate order.", *topic_sections] return pl.Section(contents, title="Main Topics Covered") @property def resources_section(self) -> Optional[pl.Section]: if self.model.resources is None: return None sub_sections = [ resource.to_pyexlatex_subsection() for resource in self.model.resources ] out_sections = [] for i, section in enumerate(sub_sections): out_sections.append(section) if i != len(sub_sections) - 1: out_sections.append(pl.VSpace()) return pl.Section(sub_sections, title="Resources")