aboutsummaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2023-08-23 11:29:14 +0200
committerFlorian Fischer <florian.fischer@muhq.space>2023-08-23 11:29:14 +0200
commitcee9aa15ff85f4dc09fecf45c5e1523690266bb9 (patch)
treea7e341a2d0253bcc6322d63ccf149fb95587f4ea /scripts
parent515ca7fadd11276421ba9d9be217a07b2e6b1652 (diff)
downloadunterricht-cee9aa15ff85f4dc09fecf45c5e1523690266bb9.tar.gz
unterricht-cee9aa15ff85f4dc09fecf45c5e1523690266bb9.zip
support buffers between sections
Diffstat (limited to 'scripts')
-rw-r--r--scripts/gen_map/Pipfile1
-rw-r--r--scripts/gen_map/Pipfile.lock17
-rwxr-xr-xscripts/gen_map/gen_map.py142
3 files changed, 103 insertions, 57 deletions
diff --git a/scripts/gen_map/Pipfile b/scripts/gen_map/Pipfile
index b9679260..12ffb01f 100644
--- a/scripts/gen_map/Pipfile
+++ b/scripts/gen_map/Pipfile
@@ -12,6 +12,7 @@ requests = "*"
yapf = "*"
pylint = "*"
mypy = "*"
+types-requests = "*"
[requires]
python_version = "3.11"
diff --git a/scripts/gen_map/Pipfile.lock b/scripts/gen_map/Pipfile.lock
index 57cd50e0..ae76a390 100644
--- a/scripts/gen_map/Pipfile.lock
+++ b/scripts/gen_map/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "830ab904fb5b6e26018ed1c9d3178f092b491f05f62e1bc43c170bc174f7587c"
+ "sha256": "2614162fe5887d0eb4939203f1a844084f3afeb19bf210f7da73548d8f1a0bea"
},
"pipfile-spec": 6,
"requires": {
@@ -408,6 +408,21 @@
"markers": "python_version >= '3.7'",
"version": "==0.12.1"
},
+ "types-requests": {
+ "hashes": [
+ "sha256:56d181c85b5925cbc59f4489a57e72a8b2166f18273fd8ba7b6fe0c0b986f12a",
+ "sha256:6aa3f7faf0ea52d728bb18c0a0d1522d9bfd8c72d26ff6f61bfc3d06a411cf40"
+ ],
+ "index": "pypi",
+ "version": "==2.31.0.2"
+ },
+ "types-urllib3": {
+ "hashes": [
+ "sha256:229b7f577c951b8c1b92c1bc2b2fdb0b49847bd2af6d1cc2a2e3dd340f3bda8f",
+ "sha256:9683bbb7fb72e32bfe9d2be6e04875fbe1b3eeec3cbb4ea231435aa7fd6b4f0e"
+ ],
+ "version": "==1.26.25.14"
+ },
"typing-extensions": {
"hashes": [
"sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36",
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 <https://www.gnu.org/licenses/>.
+"""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