From cee9aa15ff85f4dc09fecf45c5e1523690266bb9 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Wed, 23 Aug 2023 11:29:14 +0200 Subject: support buffers between sections --- scripts/gen_map/gen_map.py | 142 +++++++++++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 56 deletions(-) (limited to 'scripts/gen_map/gen_map.py') diff --git a/scripts/gen_map/gen_map.py b/scripts/gen_map/gen_map.py index 2f1089de..75aa7a48 100755 --- a/scripts/gen_map/gen_map.py +++ b/scripts/gen_map/gen_map.py @@ -1,4 +1,16 @@ #!/usr/bin/env python3 +# Copyright 2023 Florian Fischer +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see . +"""simple script to generate a rough markdown map of a school year""" import argparse from datetime import datetime, timedelta @@ -7,6 +19,7 @@ import os import re import sys import urllib +import typing as t from bs4 import BeautifulSoup import icalevents @@ -45,6 +58,12 @@ WEEKLY_HOURS = { } +def die(msg: str, status=1): + """Exit with status printing a msg to stderr""" + print(msg, file=sys.stderr) + sys.exit(status) + + def default_year() -> int: """return the default year""" now = datetime.now() @@ -107,6 +126,7 @@ def get_public_holidays(land: int, start: datetime, class LehrplanSection: + """Representation of a section of Lehrplanplus""" def __init__(self, num, summary, duration, subsections=None): self.num = num @@ -114,18 +134,37 @@ class LehrplanSection: self.duration = duration self.subsections = subsections or [] - def has_subsections(self) -> bool: - return bool(self.subsections) - def __str__(self): return f'{self.num}: {self.summary} ({self.duration}h) {self.subsections}' def __repr__(self): - return f'LehrplanSection({self.num}, {self.summary}, {self.duration}h, {self.subsections})' + return (f'LehrplanSection({self.num}, {self.summary}, ' + f'{self.duration}h, {self.subsections})') Lehrplan = list[LehrplanSection] +SUMMARY_RE = re.compile(r'(.*) \(ca\. ([1-9][0-9]*) Std\.\)') + + +def parse_section_html(section, summary_class: str) -> t.Tuple[str, str, int]: + """Parse the html for a section from the Lehrplanplus webpage""" + num = list(section.find(**{ + 'class': 'head-absatz-num' + }).children)[2].strip() + + summary = section.find(**{'class': summary_class}).string.strip() + summary = summary.replace('\xa0', ' ') + + match = SUMMARY_RE.match(summary) + if not match is None: + summary = match.group(1) + duration = int(match.group(2)) + else: + die(f'summary: {summary} does not match our re') + + return num, summary, duration + def get_lehrplan(subject: str, form: str) -> Lehrplan: """Retrieve the sections and expected duration from lehrplanplus""" @@ -134,51 +173,44 @@ def get_lehrplan(subject: str, form: str) -> Lehrplan: resp = requests.get(lpp_url, timeout=10) html = BeautifulSoup(resp.text) - summary_re = re.compile(r'(.*) \(ca\. ([1-9][0-9]*) Std\.\)') - lehrplan: Lehrplan = [] top_level_sections = html.find_all(**{'class': 'headline_lvl1'}) for section in top_level_sections: - num = list(section.find(**{ - 'class': 'head-absatz-num' - }).children)[2].strip() - - summary = section.find(**{ - 'class': 'head-absatz-title-short' - }).string.strip() - summary = summary.replace('\xa0', ' ') - - m = summary_re.match(summary) - summary = m.group(1) - duration = int(m.group(2)) + num, summary, duration = parse_section_html(section, + 'head-absatz-title-short') lehrplan.append(LehrplanSection(num, summary, duration)) second_level_sections = html.find_all(**{'class': 'headline_lvl2'}) for section in second_level_sections: - summary = section.find(**{ - 'class': 'head-absatz-title-long' - }).string.strip() - summary = summary.replace('\xa0', ' ') - - m = summary_re.match(summary) - summary = m.group(1) - duration = int(m.group(2)) - num = list(section.find(**{ - 'class': 'head-absatz-num' - }).children)[2].strip() + num, summary, duration = parse_section_html(section, + 'head-absatz-title-long') lehrplan[int(num.split('.')[0]) - 1].subsections.append( LehrplanSection(num, summary, duration)) return lehrplan -def gen_section_iter(lehrplan): - sections = [] +def gen_section_iter(lehrplan, inter_section_buffer_duration: int): + """Create an iterator returning the current section""" + end_section_buffer = LehrplanSection('', 'Puffer', 0) + inter_section_buffer = LehrplanSection('', 'Puffer', + inter_section_buffer_duration) + + _sections = [] for s in lehrplan: - if not s.has_subsections(): - sections.append(s) + if not s.subsections: + _sections.append(s) else: - sections.extend(s.subsections) + _sections.extend(s.subsections) + + # Add buffer sections to the lehrplan + sections = [] + for s in _sections: + sections.append(s) + if inter_section_buffer_duration: + sections.append(inter_section_buffer) + + sections.append(end_section_buffer) next_section_in = sections[0].duration cur = 0 @@ -186,29 +218,27 @@ def gen_section_iter(lehrplan): def _section_iter(weekly_hours, total_hours) -> list[LehrplanSection]: nonlocal cur if cur == len(sections) - 1: - return [sections[-1]] - - cur_sections = [] + return sections[-1] nonlocal next_section_in # advance section - if total_hours > next_section_in: - if total_hours - weekly_hours < next_section_in: - cur_sections.append(sections[cur]) - + if total_hours > next_section_in + (weekly_hours - + next_section_in % weekly_hours): cur += 1 next_section_in += sections[cur].duration - cur_sections.append(sections[cur]) - - return cur_sections + return sections[cur] return _section_iter -def gen_latex_table(weekly_hours: int, lehrplan: Lehrplan, school_holidays, - public_holidays) -> list[str]: - """generate a latex table matching lehrplan section on kalendarweeks""" +def gen_latex_table( + weekly_hours: int, + lehrplan: Lehrplan, + school_holidays: list[icalevents.icalparser.Event], + public_holidays: list[icalevents.icalparser.Event], # # pylint: disable=unused-argument + inter_section_buffer: int) -> list[str]: + """generate a latex table matching lehrplan section on calendar weeks""" rows: list[str] = [] @@ -220,7 +250,7 @@ def gen_latex_table(weekly_hours: int, lehrplan: Lehrplan, school_holidays, holiday_iter = iter(school_holidays[1:]) next_holiday = next(holiday_iter) - section_iter = gen_section_iter(lehrplan) + section_iter = gen_section_iter(lehrplan, inter_section_buffer) total_hours = 0 week_iter = start + timedelta(days=1) @@ -234,13 +264,11 @@ def gen_latex_table(weekly_hours: int, lehrplan: Lehrplan, school_holidays, next_holiday = next(holiday_iter) continue - lp_sections = section_iter(weekly_hours, total_hours) - - lp_level = ' \\& '.join([s.num for s in lp_sections]) - lp_summary = ' \\& '.join([s.summary for s in lp_sections]) + lp_section = section_iter(weekly_hours, total_hours) rows.append( - f'{week_iter.isocalendar().week} & {lp_level} & {lp_summary} \\\\') + f'{week_iter.isocalendar().week} & {lp_section.num} & {lp_section.summary} \\\\' + ) week_iter += timedelta(days=7) total_hours += weekly_hours @@ -248,8 +276,9 @@ def gen_latex_table(weekly_hours: int, lehrplan: Lehrplan, school_holidays, return rows -def print_map(rows: list[str], template: Path, outfile): - with open(template, 'r', encoding='utf-8') as template_file: +def print_map(rows: list[str], template_path: Path, outfile): + """Print the template including the generated table""" + with open(template_path, 'r', encoding='utf-8') as template_file: template = template_file.readlines() template_prefix = template[:-6] @@ -269,6 +298,7 @@ def main(): parser.add_argument('--year', default=default_year()) parser.add_argument('--land', default=BAYERN) parser.add_argument('-o', '--output', default=sys.stdout) + parser.add_argument('-b', '--inter-section-buffer', type=int) args = parser.parse_args() @@ -280,7 +310,7 @@ def main(): lehrplan = get_lehrplan(args.subject, str(args.form)) rows = gen_latex_table(weekly_hours, lehrplan, school_holidays, - public_holidays) + public_holidays, args.inter_section_buffer) if args.template_dir: template_path = Path(args.template_dir) / TEMPLATE_FILE -- cgit v1.2.3