#!/usr/bin/env python3 """ =============================================================== Script: courses.py Author: Kris Yotam (aka. khr1st) Date: 2025-09-29 License: MIT License Description: Defines Course and Courses abstractions. - Course: represents one LaTeX course (metadata + lectures). - Courses: collection of Course objects with support for setting the "current" course via symlink and watch file. Inspiration: Adapted and extended from Gilles Castel's lecture note workflow. =============================================================== """ from pathlib import Path import yaml from lectures import Lectures from config import ( ROOT, CURRENT_COURSE_ROOT, CURRENT_COURSE_SYMLINK, CURRENT_COURSE_WATCH_FILE, ) # ----------------------------- # Course object # ----------------------------- class Course: def __init__(self, path: Path): self.path = path self.name = path.stem # Load metadata from info.yaml with (path / "info.yaml").open() as f: self.info = yaml.safe_load(f) self._lectures = None @property def lectures(self) -> Lectures: """ Lazily initialize lectures for this course. """ if not self._lectures: self._lectures = Lectures(self) return self._lectures def __eq__(self, other) -> bool: """ Courses are equal if their paths match. """ if other is None: return False return self.path == other.path # ----------------------------- # Courses collection # ----------------------------- class Courses(list): def __init__(self): super().__init__(self.read_files()) def read_files(self): """ Scan ROOT for course directories and return Course objects. """ course_dirs = [x for x in ROOT.iterdir() if x.is_dir()] courses = [Course(path) for path in course_dirs] return sorted(courses, key=lambda c: c.name) @property def current(self) -> Course: """ Return the currently active course. """ return Course(CURRENT_COURSE_ROOT.resolve()) @current.setter def current(self, course: Course): """ Set the current course by updating symlink and watch file. """ if CURRENT_COURSE_SYMLINK.exists() or CURRENT_COURSE_SYMLINK.is_symlink(): CURRENT_COURSE_SYMLINK.unlink() CURRENT_COURSE_SYMLINK.symlink_to(course.path) CURRENT_COURSE_WATCH_FILE.write_text(f"{course.info['short']}\n")