From e2e96c9f4326e2a79a619958cba4964064df004a Mon Sep 17 00:00:00 2001 From: Hartmut Seichter Date: Wed, 8 Nov 2023 15:32:41 +0100 Subject: [PATCH] first MVP --- README.md | 19 +++++ coursebuilder/__main__.py | 153 +++++++++++--------------------------- test/simple/mod.cg.yaml | 27 +++++++ test/simple/schema.yaml | 68 +++++++++++++++-- 4 files changed, 151 insertions(+), 116 deletions(-) diff --git a/README.md b/README.md index cb71675..14470ab 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,25 @@ Coursebuilder is a helper and validator for curricula. It helps to amalgate and (c) Copyright 2020-2023 Hartmut Seichter +# Usage + +```sh +$> python coursebuilder +coursebuilder [-h] [-m META [META ...]] [-l LANG] [-f FIELDS [FIELDS ...]] [-s SCHEMA] + +options: + -h, --help show this help message and exit + -m META [META ...], --meta META [META ...] + course description(s) as YAML file(s) + -l LANG, --lang LANG Language to parse from meta file (use de or en) + -f FIELDS [FIELDS ...], --fields FIELDS [FIELDS ...] + Fields to be used + -s SCHEMA, --schema SCHEMA + using provided schema +``` + + # Licence Coursebuilder is licensed under the terms of the MIT License. For details consult https://opensource.org/license/mit/ or the attached license file + diff --git a/coursebuilder/__main__.py b/coursebuilder/__main__.py index 0e1b1e1..57c4d7d 100644 --- a/coursebuilder/__main__.py +++ b/coursebuilder/__main__.py @@ -3,107 +3,47 @@ """ CourseBuilder -Coursebuilder is a preprocessor tool to generate curricula with pandoc from -structured representations. Data scheme and values are kept in YAML files -in order to version content with Git. +Coursebuilder is a preprocessor tool for [pandoc](https://pandoc.org) +to generate multi-lingual curricula documentation tables from +structured representations as a flatfile database. Data scheme and +actual values are kept in YAML files in order to version them with git. """ from argparse import ArgumentParser import itertools import yaml -import os import textwrap +import string -config_file = 'modulhandbuch.yaml' -line_length = 128 -column_ratio= 0.28 - -def build_curriculum(input_path,lang='de',pagebreak=False,title=False): - - # open the config file - file_path = os.path.realpath(__file__) - config_path = os.path.join(os.path.dirname(file_path),config_file) - transforms = None # for translation - - # load transforms - with open(config_path,'r') as cf: - transforms = yaml.load(cf,Loader=Loader) - - # open meta.yaml in the directory - with open(os.path.join(input_path),'r') as fp: - - # get configuration data - desc = yaml.load(fp,Loader=Loader) - - # collect transformations - ti = [] - - # fix for now - for k,v in desc[lang].items(): - v = v if v else "" - ti.append((transforms[lang][k],str(v))) - - # to limit - h_len = int(line_length * column_ratio) - d_len = line_length-h_len - - if title: - print('#',desc[lang]['name'],'\n') - - - print(''.join(['+',"".ljust(h_len,'-'),'+',"".ljust(d_len,'-'),'+'])) - - headline = False - - for k,v in ti: - - h = textwrap.wrap(k, h_len, break_long_words=False) - wrapper = textwrap.TextWrapper(d_len) - t = [wrapper.wrap(i) for i in v.split('\n') if i != ''] - t = list(itertools.chain.from_iterable(t)) - - # get rows - rows = list(itertools.zip_longest(h,t,fillvalue="")) - - # expand rows - for r in rows: - print(''.join(['|',r[0].ljust(h_len,' '),'|',r[1].ljust(d_len,' '),'|'])) - - if headline: - print(''.join(['+',"".ljust(h_len,'-'),'+',"".ljust(d_len,'-'),'+'])) - else: - print(''.join(['+',"".ljust(h_len,'='),'+',"".ljust(d_len,'='),'+'])) - headline = True - - # to control pagebreaks for pandoc - if pagebreak: - print('\n\\newpage') - -# create a meta file -def create_meta(): - - # open the config file - file_path = os.path.realpath(__file__) - config_path = os.path.join(os.path.dirname(file_path),config_file) - transforms = None # for translation - - # load transforms - with open(config_path,'r') as cf: - while line := cf.readline(): - print(line.rstrip()) class CourseBuilder: def __init__(self) -> None: self.__schema = None + def set_schema(self,schema = None): self.__schema = schema + def get_template(self,field,lang='de'): + if hasattr(self.__schema[field],'template'): + return self.__schema[field]['template'][lang] + else: + return "$value" + - def generate_markdown(self,ti): + def is_translatable(self,field): + if hasattr(self.__schema[field],'translatable'): + return self.__schema[field]['translatable'] + else: + return True + + def generate_markdown(self,ti,pagebreak = False) -> str: + + line_length = 128 + column_ratio= 0.28 h_len = int(line_length * column_ratio) d_len = line_length-h_len @@ -136,14 +76,27 @@ class CourseBuilder: headline = True # to control pagebreaks for pandoc - # if pagebreak: - # print('\n\\newpage') + if pagebreak: + print('\n\\newpage') + def process_label(self,field,lang='de'): + # processes the label of a field item + return self.__schema[field]['label'][lang] - def process(self,meta,fields,lang): - - # fields = ['name','goal','form-of-exam'] - # fields = ['form-of-exam'] + def process_str(self,meta,field,lang='de'): + # + return [self.process_label(field,lang),meta[field][lang]] + + def process_enum(self,meta,field,lang='de'): + v = meta[field]['value'] + return [self.process_label(field,lang),self.__schema[field]['values'][v][lang]] + + def process_int(self,meta,field,lang='de'): + v = meta[field]['value'] + t = string.Template(self.get_template(field,lang)) + return [self.process_label(field,lang),t.substitute({'value' : v})] + + def process(self,meta,fields = [],lang = 'de'): table_items = [] @@ -151,29 +104,11 @@ class CourseBuilder: match self.__schema[field]['type']: case 'str': table_items.append(self.process_str(meta,field,lang)) case 'enum': table_items.append(self.process_enum(meta,field,lang)) + case 'int': table_items.append(self.process_int(meta,field,lang)) - # print(table_items) - self.generate_markdown(table_items) - def process_label(self,field,lang='de'): - pass - - def process_str(self,meta,field,lang='de'): - return [self.__schema[field]['label'][lang],meta[field][lang]] - # print(self.__schema[field]['label'][lang],':',meta[field][lang]) - - def process_enum(self,meta,field,lang='de'): - v = meta[field]['value'] - return [self.__schema[field]['label'][lang],self.__schema[field]['values'][v][lang]] - # print(self.__schema[field]['label'][lang],':',self.__schema[field]['values'][v][lang]) - - def process_int(self,lang='de'): - pass - - - def main(): # get command line parameters @@ -186,10 +121,6 @@ def main(): parser.add_argument('-l','--lang',help="Language to parse from meta file (use de or en)",default='de') parser.add_argument('-f','--fields',help="Fields to be used",action="extend", nargs="+", type=str) - # parser.add_argument('-c','--create',action='store_true',help="Create a meta file from description") - # parser.add_argument('-n','--newpage',action='store_true',help="Create a pagebreak after each table") - # parser.add_argument('-t','--title',action='store_true',help="Create a title ahead of each table") - parser.add_argument('-s','--schema',help="using provided schema") diff --git a/test/simple/mod.cg.yaml b/test/simple/mod.cg.yaml index f9dd7ee..91353be 100644 --- a/test/simple/mod.cg.yaml +++ b/test/simple/mod.cg.yaml @@ -3,6 +3,9 @@ name: { en: "Computer Graphics" } +credits: + value: 5 + # common common: id: CG # fix for any variante @@ -72,6 +75,30 @@ content: * Overview visualizations * Graphical User Interfaces +media-of-instruction: + de: | + * H5P Lernmodule + * Lernforum + * Übungen + * Folien + * Auszug aus der Literaturliste: + * Bar-Zeev, Avi. Scenegraphs: Past, Present and Future, 2003 http://www.realityprime.com/scenegraph.php + * Burley, Brent. “Physically-Based Shading at Disney.” In ACM SIGGRAPH, 2012:1-7, 2012 + * Goldstein, E. Bruce. Sensation and Perception. 3rd ed. Belmont, Calif.: Wadsworth Pub. Co., 1989 + * Hughes, John F. Computer Graphics: Principles and Practice. Third edition. Upper Saddle River, New Jersey: Addison-Wesley, 2014 + * Shirley, Peter, and R. Keith Morley. Realistic Ray Tracing. 2. ed. Natick, Mass: A K Peters, 2003 + en: | + * H5P learning modules + * learning forum + * exersises + * slides and quizzes + * Literature: + * Bar-Zeev, Avi. Scenegraphs: Past, Present and Future, 2003 http://www.realityprime.com/scenegraph.php. + * Burley, Brent. “Physically-Based Shading at Disney.” In ACM SIGGRAPH, 2012:1–7, 2012 + * Goldstein, E. Bruce. Sensation and Perception. 3rd ed. Belmont, Calif.: Wadsworth Pub. Co., 1989 + * Hughes, John F. Computer Graphics: Principles and Practice. Third edition. Upper Saddle River, New Jersey: Addison-Wesley, 2014 + * Shirley, Peter, and R. Keith Morley. Realistic Ray Tracing. 2. ed. Natick, Mass: A K Peters, 2003 + # German Variant de: diff --git a/test/simple/schema.yaml b/test/simple/schema.yaml index 9647da3..cc44d10 100644 --- a/test/simple/schema.yaml +++ b/test/simple/schema.yaml @@ -6,6 +6,7 @@ # name: type: str + translatable: false label: { de: "Modulname", en: "name of course" @@ -16,6 +17,7 @@ name: # id: type: str + translatable: false label: { de: "Kürzel", en: "code" @@ -26,6 +28,7 @@ id: # instructor: type: str + translatable: false label: { de: "Modulverantwortliche:r", en: "module instructor" @@ -51,8 +54,42 @@ content: en: "content" } +# +# Lehrform +# form: - type: enum + label: { + de: "Lehrform", + en: "form of instruction" + } + type: enum + values: { + 'lecture' : { + de: "Vorlesung", + en: "lecture" + }, + 'lecture_seminar' : { + de: "Seminaristische Vorlesung", + en: "lecture and seminar" + }, + 'seminar' : { + de: "Seminar", + en: "seminar" + }, + 'exersise' : { + de: "Übung", + en: "lab exersise" + }, + 'pc_lab' : { + de: "Rechnergestütztes Praktikum", + en: "PC exersise" + }, + 'project' : { + de: "Project", + en: "project" + } + } + # # Voraussetzungen für die Teilnahme # @@ -93,7 +130,15 @@ used-in: en: "used in study programs" } -workload: #tricky! { 'presence': 10, 'exersise': 10, 'exam-prep' : 0 } +# +# Arbeitsaufwand +# +workload: + type: str + label: { + de: "Arbeitsaufwand / Gesamtworkload", + en: "workload" + } # # credits/ECTS # @@ -103,7 +148,11 @@ credits: de: "Kreditpunkte und Gewichtung der Note in der Gesamtnote" } type: int - template: "${value}CP (${value} / 120) " + template: + de: "${value}CP Gewichtung: ${value}CP von 120CP " + en: "${value}CP weight: ${value} / 120 " + + # # Leistungsnachweis # @@ -127,8 +176,9 @@ form-of-exam: en: "alternative examination" } } + # -# term +# Semester # term: label: { @@ -150,6 +200,7 @@ term: en: "winter and summer term" } } + # # Häufigkeit des Angebots # @@ -169,7 +220,9 @@ frequency: en: "once per study year" } } + duration: enum + kind: type: enum label: { @@ -187,7 +240,12 @@ kind: } } -remarks: str +remarks: + type: str + label: { + de: "Bemerkungen", + en: "Remarks" + } # test: # label: { de: "Name", en: "A Test" }