From 6f51fbf76c5a75e4088ad37811beab72727ffe96 Mon Sep 17 00:00:00 2001 From: Hartmut Seichter Date: Thu, 25 Apr 2024 19:35:13 +0200 Subject: [PATCH 1/8] adding a proper exception handler to find actual problems in external files --- coursebuilder/__main__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/coursebuilder/__main__.py b/coursebuilder/__main__.py index 2ce0fc5..f9cf2b1 100644 --- a/coursebuilder/__main__.py +++ b/coursebuilder/__main__.py @@ -13,7 +13,7 @@ actual values are kept in YAML files in order to version them with git. from argparse import ArgumentParser import yaml import string -import os +import os,sys from tablegenerator import TableGenerator from markdowngenerator import MarkdownGenerator @@ -145,7 +145,10 @@ class CourseBuilder: mod_path = os.path.join(os.path.dirname(bookpath),m) with open(mod_path) as fm: - self.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=lang,pagebreak=pagebreak,createTitle=create_title,header_level=header_level) + try: + self.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=lang,pagebreak=pagebreak,createTitle=create_title,header_level=header_level) + except: + print(f'Error in {mod_path}',file=sys.stderr) From 28378e3819cc8b0a6560a095906818031850ee51 Mon Sep 17 00:00:00 2001 From: Hartmut Seichter Date: Wed, 1 May 2024 08:10:27 +0200 Subject: [PATCH 2/8] intermediate version --- TODO.md | 1 + coursebuilder/__main__.py | 19 ++++++++++++------- coursebuilder/markdowngenerator.py | 9 +++++---- coursebuilder/templategenerator.py | 11 +++++++++++ test/simple/Makefile | 11 ++++++++--- test/simple/book.yaml | 2 ++ 6 files changed, 39 insertions(+), 14 deletions(-) create mode 100644 coursebuilder/templategenerator.py diff --git a/TODO.md b/TODO.md index 484edcc..610b7c8 100644 --- a/TODO.md +++ b/TODO.md @@ -5,3 +5,4 @@ * [x] fix overlong table cells (pandoc longtable only deals with overlong tables but not cells) * [ ] add a book mode for mixing input and headers (# Blah -m mod.cg.yaml) * [ ] table generator +* [ ] overlay of compulsory with other modes ... \ No newline at end of file diff --git a/coursebuilder/__main__.py b/coursebuilder/__main__.py index f9cf2b1..43f67db 100644 --- a/coursebuilder/__main__.py +++ b/coursebuilder/__main__.py @@ -17,14 +17,13 @@ import os,sys from tablegenerator import TableGenerator from markdowngenerator import MarkdownGenerator - +from templategenerator import TemplateGenerator class CourseBuilder: def __init__(self) -> None: self.__schema = None - def set_schema(self,schema = None): self.__schema = schema @@ -111,7 +110,7 @@ class CourseBuilder: return [k,', '.join(parts)] - def process(self,meta,fields = [],lang = 'de',pagebreak = False,createTitle=False,header_level=1): + def process(self,meta,fields = [],lang = 'de',pagebreak = False,createTitle=False,header_level=1,template=None): table_items = [] @@ -123,8 +122,13 @@ class CourseBuilder: case 'multinum' : table_items.append(self.process_multinum(meta,field,lang)) case 'multikey': table_items.append(self.process_multikey(meta,field,lang)) - mdg = MarkdownGenerator() - mdg.generate_markdown(table_items,pagebreak,createTitle,header_level=header_level) + if template != None: + # use template generator + TemplateGenerator.generate(table_items,pagebreak,createTitle,header_level=header_level) + pass + else: + # conventional MD mode + MarkdownGenerator.generate(table_items,pagebreak,createTitle,header_level=header_level) def process_book_section(self,section,lang='de'): pass @@ -168,7 +172,7 @@ def main(): parser.add_argument('-b','--book',type=str,help="process a whole curriculum book with sections") parser.add_argument('--level',type=int,default=1,help="level of header tags") parser.add_argument('--table-gen',type=str,default=None,help='runs table generator') - + parser.add_argument('--template',type=str,default=None,help='defines a template to be used with fields') # get arguments args = parser.parse_args() @@ -214,7 +218,8 @@ def main(): for m in args.meta: with open(m) as fm: - cb.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=args.lang,pagebreak=args.pagebreak,createTitle=args.title,header_level=args.level) + + cb.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=args.lang,pagebreak=args.pagebreak,createTitle=args.title,header_level=args.level,template=args.template) else: parser.print_help() diff --git a/coursebuilder/markdowngenerator.py b/coursebuilder/markdowngenerator.py index f7ff98a..ad78b49 100644 --- a/coursebuilder/markdowngenerator.py +++ b/coursebuilder/markdowngenerator.py @@ -4,11 +4,10 @@ import textwrap,itertools class MarkdownGenerator: - def __init__(self) -> None: - pass - def generate_markdown(self,ti,pagebreak = False,title = False,header_level = 1) -> str: + @staticmethod + def generate(ti,pagebreak = False,title = False,header_level = 1) -> str: line_length = 128 column_ratio= 0.28 @@ -16,6 +15,9 @@ class MarkdownGenerator: h_len = int(line_length * column_ratio) d_len = line_length-h_len + # + # generate title (currently the first one) + # if title: print('#' * header_level,ti[0][1],'\n') @@ -26,7 +28,6 @@ class MarkdownGenerator: # # this implements a Markdown Grid-Table # - for k,v in ti: if v == None: diff --git a/coursebuilder/templategenerator.py b/coursebuilder/templategenerator.py new file mode 100644 index 0000000..0e7a23a --- /dev/null +++ b/coursebuilder/templategenerator.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +import textwrap,itertools + + +class TemplateGenerator: + + @staticmethod + def generate(ti,pagebreak = False,title = False,header_level = 1) -> str: + print(ti) + pass \ No newline at end of file diff --git a/test/simple/Makefile b/test/simple/Makefile index 8ea5c11..02c04ef 100644 --- a/test/simple/Makefile +++ b/test/simple/Makefile @@ -1,17 +1,22 @@ + +coursebuilder := ../../coursebuilder + table.en.pdf: @echo "creating English version ..." - python ../../coursebuilder -s schema.yaml -m mod.cg.yaml -l en -f fields.yaml | pandoc --template pandoc-template/eisvogel.latex -o table.en.pdf + python ${coursebuilder} -s schema.yaml -m mod.cg.yaml -l en -f fields.yaml | pandoc --template pandoc-template/eisvogel.latex -o table.en.pdf table.de.pdf: @echo "creating German version ..." - python ../../coursebuilder -s schema.yaml -m mod.cg.yaml -l de -f fields.yaml | pandoc --template pandoc-template/eisvogel.latex -o table.de.pdf + python ${coursebuilder} -s schema.yaml -m mod.cg.yaml -l de -f fields.yaml | pandoc --template pandoc-template/eisvogel.latex -o table.de.pdf all: table.en.pdf table.de.pdf clean: rm -f table.en.pdf table.de.pdf +debug-list: + python ${coursebuilder} -s schema.yaml -m mod.cg.yaml mod.interactsys.yaml -l de -f name credits --template "" debug: - python ../../coursebuilder -s schema.yaml -m mod.cg.yaml -l de -f fields.yaml + python ${coursebuilder} -s schema.yaml -m mod.cg.yaml -l de -f fields.yaml diff --git a/test/simple/book.yaml b/test/simple/book.yaml index 6cd1b46..f8544df 100644 --- a/test/simple/book.yaml +++ b/test/simple/book.yaml @@ -29,3 +29,5 @@ book: - modules: - mod.cg.yaml + + From 595a0352c5523fd05d057104ccdac0a94d788060 Mon Sep 17 00:00:00 2001 From: Hartmut Seichter Date: Wed, 1 May 2024 14:58:41 +0200 Subject: [PATCH 3/8] refactoring for division of concerns --- LICENSE.md | 2 +- README.md | 1 - TODO.md | 7 +- coursebuilder/__main__.py | 247 +++++++---------------------- coursebuilder/markdowngenerator.py | 13 ++ coursebuilder/metagenerator.py | 145 +++++++++++++++++ test/simple/Makefile | 6 +- 7 files changed, 223 insertions(+), 198 deletions(-) create mode 100644 coursebuilder/metagenerator.py diff --git a/LICENSE.md b/LICENSE.md index a749f99..a480400 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright 2020-2023 Hartmut Seichter +Copyright 2020-2024 Hartmut Seichter Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/README.md b/README.md index ec5f847..368648b 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,6 @@ options: © Copyright 2020-2024 Hartmut Seichter - # 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/TODO.md b/TODO.md index 610b7c8..c35be4a 100644 --- a/TODO.md +++ b/TODO.md @@ -3,6 +3,7 @@ * [ ] proper referencing of tables * [ ] custom python code in tables * [x] fix overlong table cells (pandoc longtable only deals with overlong tables but not cells) -* [ ] add a book mode for mixing input and headers (# Blah -m mod.cg.yaml) -* [ ] table generator -* [ ] overlay of compulsory with other modes ... \ No newline at end of file +* [x] add a book mode for mixing input and headers (# Blah -m mod.cg.yaml) +* [~] table generator +* [ ] overlay of compulsory with other modes ... +* [ ] add template based generator \ No newline at end of file diff --git a/coursebuilder/__main__.py b/coursebuilder/__main__.py index 43f67db..37b3d4b 100644 --- a/coursebuilder/__main__.py +++ b/coursebuilder/__main__.py @@ -18,210 +18,75 @@ import os,sys from tablegenerator import TableGenerator from markdowngenerator import MarkdownGenerator from templategenerator import TemplateGenerator +from metagenerator import MetaGenerator + 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 'template' in self.__schema[field]: - return self.__schema[field]['template'][lang] - else: - return "$value" + @staticmethod + def run(): - def is_translatable(self,field): - if 'translatable' in self.__schema[field]: - return self.__schema[field]['translatable'] - else: - return True + # arguments + parser = ArgumentParser(description='versatile curricula generator') - def needs_spec(self,field): - if 'spec' in self.__schema[field]: - return self.__schema[field] - else: - return False + parser.add_argument('-m','--meta',action="extend", nargs="+", type=str,help="course description(s) as YAML file(s)") + 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, the table will be build accordingly",action="extend", nargs="+", type=str) + parser.add_argument('-s','--schema',help="using provided schema") + parser.add_argument('-p','--pagebreak',action="store_true",help="add a pagebreak after each module") + parser.add_argument('-t','--title',action="store_true",help="take first value in list as title") + parser.add_argument('-b','--book',type=str,help="process a whole curriculum book with sections") + parser.add_argument('--level',type=int,default=1,help="level of header tags") + parser.add_argument('--table-gen',type=str,default=None,help='runs table generator') + parser.add_argument('--template',type=str,default=None,help='defines a template to be used with fields') - def process_label(self,field,lang='de'): - # processes the label of a field item - return self.__schema[field]['label'][lang] + # get arguments + args = parser.parse_args() + + if args.table_gen: - def process_str(self,meta,field,lang='de'): - if self.is_translatable(field): - - return [self.process_label(field,lang),meta[field][lang]] - else: - if not 'value' in meta[field]: - raise AssertionError(field,'incomplete') + tg = TableGenerator() - return [self.process_label(field,lang),meta[field]['value']] + tg.generate_table(args.table_gen) - def process_enum(self,meta,field,lang='de'): - """ - enum have a specification 'specs' option - that can be forced by the scheme - """ - vv = meta[field]['value'] - enum_val = self.__schema[field]['values'][vv][lang] + return - if self.needs_spec(field): - t = string.Template(self.get_template(field=field,lang=lang)) + # book mode with predefined setting from a book file + if args.book and args.schema: + + generator = MetaGenerator() - spec = meta[field]['spec'][lang] + with open(args.schema) as sf: + generator.set_schema(yaml.load(sf,Loader=yaml.Loader)) - return [self.process_label(field,lang),t.substitute({'value': enum_val,'spec': spec})] + with open(args.book) as bf: + generator.process_book(yaml.load(bf,Loader=yaml.Loader),os.path.abspath(args.book),lang=args.lang,pagebreak=args.pagebreak,create_title=args.title,header_level=args.level) + + + # verbose command line mode + elif args.schema and args.meta and len(args.fields) > 0: + + generator = MetaGenerator() + + actual_fields = [] + + if os.path.isfile(args.fields[0]): + with open(args.fields[0]) as ff: + actual_fields = yaml.load(ff,Loader=yaml.Loader)['fields'] + else: + actual_fields = args.fields + + + with open(args.schema) as f: + generator.set_schema(yaml.load(f,Loader=yaml.Loader)) + + for m in args.meta: + with open(m) as fm: + + generator.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=args.lang,pagebreak=args.pagebreak,createTitle=args.title,header_level=args.level,template=args.template) else: - return [self.process_label(field,lang),enum_val] - - def process_num(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_multinum(self,meta,field,lang='de'): - v = meta[field]['value'] - t = string.Template(self.get_template(field,lang)) - if hasattr(v, "__len__"): - vv = [t.substitute({'value' : ev}) for ev in v] - return [self.process_label(field,lang),', '.join(vv)] - else: - return self.process_num(meta=meta,field=field,lang=lang) - - - def process_multikey(self,meta,field,lang='de'): - """ - multikey need to assign a numeric value to a key - """ - vs = meta[field]['value'] - t = string.Template(self.get_template(field,lang)) - - k = self.process_label(field,lang) - - parts = [] - - for e in vs: - kk = self.__schema[field]['keys'][e][lang] - parts.append(t.substitute({'key': kk, 'value' : vs[e]})) - - return [k,', '.join(parts)] - - - def process(self,meta,fields = [],lang = 'de',pagebreak = False,createTitle=False,header_level=1,template=None): - - table_items = [] - - for field in fields: - 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' | 'num' : table_items.append(self.process_num(meta,field,lang)) - case 'multinum' : table_items.append(self.process_multinum(meta,field,lang)) - case 'multikey': table_items.append(self.process_multikey(meta,field,lang)) - - if template != None: - # use template generator - TemplateGenerator.generate(table_items,pagebreak,createTitle,header_level=header_level) - pass - else: - # conventional MD mode - MarkdownGenerator.generate(table_items,pagebreak,createTitle,header_level=header_level) - - def process_book_section(self,section,lang='de'): - pass - - def process_book(self,book,bookpath,create_title,pagebreak,lang='de',header_level=2): - - actual_fields = [] - - for bi in book['book']: - if 'fields' in bi: - actual_fields = bi['fields'] - if 'sections' in bi: - for section in bi['sections']: - if 'text' in section: - print(section['text'][lang]) - if 'modules' in section: - for m in section['modules']: - mod_path = os.path.join(os.path.dirname(bookpath),m) - - with open(mod_path) as fm: - try: - self.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=lang,pagebreak=pagebreak,createTitle=create_title,header_level=header_level) - except: - print(f'Error in {mod_path}',file=sys.stderr) - - - - - -def main(): - - # arguments - parser = ArgumentParser(description='versatile curricula generator') - - parser.add_argument('-m','--meta',action="extend", nargs="+", type=str,help="course description(s) as YAML file(s)") - 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, the table will be build accordingly",action="extend", nargs="+", type=str) - parser.add_argument('-s','--schema',help="using provided schema") - parser.add_argument('-p','--pagebreak',action="store_true",help="add a pagebreak after each module") - parser.add_argument('-t','--title',action="store_true",help="take first value in list as title") - parser.add_argument('-b','--book',type=str,help="process a whole curriculum book with sections") - parser.add_argument('--level',type=int,default=1,help="level of header tags") - parser.add_argument('--table-gen',type=str,default=None,help='runs table generator') - parser.add_argument('--template',type=str,default=None,help='defines a template to be used with fields') - - # get arguments - args = parser.parse_args() - - if args.table_gen: - - tg = TableGenerator() - - tg.generate_table(args.table_gen) - - return - - - - # book mode with predefined setting from a book file - if args.book and args.schema: - - cb = CourseBuilder() - - with open(args.schema) as sf: - cb.set_schema(yaml.load(sf,Loader=yaml.Loader)) - - with open(args.book) as bf: - cb.process_book(yaml.load(bf,Loader=yaml.Loader),os.path.abspath(args.book),lang=args.lang,pagebreak=args.pagebreak,create_title=args.title,header_level=args.level) - - - # verbose command line mode - elif args.schema and args.meta and len(args.fields) > 0: - - cb = CourseBuilder() - - actual_fields = [] - - if os.path.isfile(args.fields[0]): - with open(args.fields[0]) as ff: - actual_fields = yaml.load(ff,Loader=yaml.Loader)['fields'] - else: - actual_fields = args.fields - - - with open(args.schema) as f: - cb.set_schema(yaml.load(f,Loader=yaml.Loader)) - - for m in args.meta: - with open(m) as fm: - - cb.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=args.lang,pagebreak=args.pagebreak,createTitle=args.title,header_level=args.level,template=args.template) - else: - parser.print_help() + parser.print_help() if __name__ == '__main__': - main() + CourseBuilder.run() diff --git a/coursebuilder/markdowngenerator.py b/coursebuilder/markdowngenerator.py index ad78b49..59f4323 100644 --- a/coursebuilder/markdowngenerator.py +++ b/coursebuilder/markdowngenerator.py @@ -2,9 +2,18 @@ import textwrap,itertools +# we need raw value maybe add a third item - tuple the input? + +# alternative use a dictionary +# { name: XYZ } + +# or make it the class class MarkdownGenerator: + @staticmethod + def generate_tablerow() -> str: + pass @staticmethod def generate(ti,pagebreak = False,title = False,header_level = 1) -> str: @@ -28,8 +37,12 @@ class MarkdownGenerator: # # this implements a Markdown Grid-Table # + + # test if this affected by a third item! + for k,v in ti: + # if v == None: v = '' diff --git a/coursebuilder/metagenerator.py b/coursebuilder/metagenerator.py new file mode 100644 index 0000000..68482cf --- /dev/null +++ b/coursebuilder/metagenerator.py @@ -0,0 +1,145 @@ +import string + +class MetaGenerator: + + def __init__(self) -> None: + self.__schema = None + + def set_schema(self,schema = None): + self.__schema = schema + + def get_template(self,field,lang='de'): + if 'template' in self.__schema[field]: + return self.__schema[field]['template'][lang] + else: + return "$value" + + def is_translatable(self,field): + if 'translatable' in self.__schema[field]: + return self.__schema[field]['translatable'] + else: + return True + + def needs_spec(self,field): + if 'spec' in self.__schema[field]: + return self.__schema[field] + else: + return False + + def process_label(self,field,lang='de'): + # processes the label of a field item + return self.__schema[field]['label'][lang] + + def process_str(self,meta,field,lang='de'): + if self.is_translatable(field): + + return [self.process_label(field,lang),meta[field][lang]] + else: + if not 'value' in meta[field]: + raise AssertionError(field,'incomplete') + + return [self.process_label(field,lang),meta[field]['value']] + + def process_enum(self,meta,field,lang='de'): + """ + enum have a specification 'specs' option + that can be forced by the scheme + """ + vv = meta[field]['value'] + enum_val = self.__schema[field]['values'][vv][lang] + + if self.needs_spec(field): + + t = string.Template(self.get_template(field=field,lang=lang)) + + spec = meta[field]['spec'][lang] + + return [self.process_label(field,lang),t.substitute({'value': enum_val,'spec': spec})] + else: + return [self.process_label(field,lang),enum_val] + + def process_num(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_multinum(self,meta,field,lang='de'): + v = meta[field]['value'] + t = string.Template(self.get_template(field,lang)) + if hasattr(v, "__len__"): + vv = [t.substitute({'value' : ev}) for ev in v] + return [self.process_label(field,lang),', '.join(vv)] + else: + return self.process_num(meta=meta,field=field,lang=lang) + + + def process_multikey(self,meta,field,lang='de'): + """ + multikey need to assign a numeric value to a key + """ + vs = meta[field]['value'] + t = string.Template(self.get_template(field,lang)) + + k = self.process_label(field,lang) + + parts = [] + + for e in vs: + kk = self.__schema[field]['keys'][e][lang] + parts.append(t.substitute({'key': kk, 'value' : vs[e]})) + + return [k,', '.join(parts)] + + + def process(self,meta,fields = [],lang = 'de',pagebreak = False,createTitle=False,header_level=1,template=None) -> []: + + table_items = [] + + for field in fields: + 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' | 'num' : table_items.append(self.process_num(meta,field,lang)) + case 'multinum' : table_items.append(self.process_multinum(meta,field,lang)) + case 'multikey': table_items.append(self.process_multikey(meta,field,lang)) + + # maybe return tableitems as np.Dataframe? + return table_items + + + + # if template != None: + # # use template generator + # TemplateGenerator.generate(table_items,pagebreak,createTitle,header_level=header_level) + # pass + # else: + # # conventional MD mode + # MarkdownGenerator.generate(table_items,pagebreak,createTitle,header_level=header_level) + + def process_book_section(self,section,lang='de'): + pass + + def process_book(self,book,bookpath,create_title,pagebreak,lang='de',header_level=2): + + actual_fields = [] + + for bi in book['book']: + if 'fields' in bi: + actual_fields = bi['fields'] + if 'sections' in bi: + for section in bi['sections']: + if 'text' in section: + print(section['text'][lang]) + if 'modules' in section: + for m in section['modules']: + mod_path = os.path.join(os.path.dirname(bookpath),m) + + with open(mod_path) as fm: + try: + self.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=lang,pagebreak=pagebreak,createTitle=create_title,header_level=header_level) + except: + print(f'Error in {mod_path}',file=sys.stderr) + + + + diff --git a/test/simple/Makefile b/test/simple/Makefile index 02c04ef..c282752 100644 --- a/test/simple/Makefile +++ b/test/simple/Makefile @@ -14,9 +14,11 @@ all: table.en.pdf table.de.pdf clean: rm -f table.en.pdf table.de.pdf -debug-list: - python ${coursebuilder} -s schema.yaml -m mod.cg.yaml mod.interactsys.yaml -l de -f name credits --template "" +debug-template: + python ${coursebuilder} -s schema.yaml -m mod.cg.yaml mod.interactsys.yaml -l de -f name credits --template "$$name | $$credits" +debug-markdown: + python ${coursebuilder} -s schema.yaml -m mod.cg.yaml mod.interactsys.yaml -l de -f name credits debug: python ${coursebuilder} -s schema.yaml -m mod.cg.yaml -l de -f fields.yaml From 35df5e2aa1d4f4ddf8ab969285f2543d043a2195 Mon Sep 17 00:00:00 2001 From: Hartmut Seichter Date: Wed, 1 May 2024 17:14:23 +0200 Subject: [PATCH 4/8] command line mode --- coursebuilder/__main__.py | 5 ++++- coursebuilder/metagenerator.py | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/coursebuilder/__main__.py b/coursebuilder/__main__.py index 37b3d4b..caa551c 100644 --- a/coursebuilder/__main__.py +++ b/coursebuilder/__main__.py @@ -84,7 +84,10 @@ class CourseBuilder: for m in args.meta: with open(m) as fm: - generator.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=args.lang,pagebreak=args.pagebreak,createTitle=args.title,header_level=args.level,template=args.template) + table_items = generator.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=args.lang,pagebreak=args.pagebreak,createTitle=args.title,header_level=args.level,template=args.template) + + print(table_items) + else: parser.print_help() diff --git a/coursebuilder/metagenerator.py b/coursebuilder/metagenerator.py index 68482cf..b0d73fb 100644 --- a/coursebuilder/metagenerator.py +++ b/coursebuilder/metagenerator.py @@ -95,7 +95,9 @@ class MetaGenerator: table_items = [] + # iterate over requested fields for field in fields: + # correlate with schema and append 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)) From 5a4b2c8f3af34edb22b6a7e64df029b70598971f Mon Sep 17 00:00:00 2001 From: Hartmut Seichter Date: Fri, 3 May 2024 07:42:51 +0200 Subject: [PATCH 5/8] repair book mode with refactored rendering --- coursebuilder/__main__.py | 47 ++++++++++++++++++++--- coursebuilder/metagenerator.py | 64 ++++++++++++++++++-------------- test/simple/Makefile | 4 ++ test/simple/book.yaml | 8 +++- test/simple/fields.yaml | 2 +- test/simple/mod.cg.yaml | 2 +- test/simple/mod.interactsys.yaml | 5 ++- test/simple/schema.yaml | 2 +- 8 files changed, 94 insertions(+), 40 deletions(-) diff --git a/coursebuilder/__main__.py b/coursebuilder/__main__.py index caa551c..e8c44da 100644 --- a/coursebuilder/__main__.py +++ b/coursebuilder/__main__.py @@ -61,14 +61,38 @@ class CourseBuilder: generator.set_schema(yaml.load(sf,Loader=yaml.Loader)) with open(args.book) as bf: - generator.process_book(yaml.load(bf,Loader=yaml.Loader),os.path.abspath(args.book),lang=args.lang,pagebreak=args.pagebreak,create_title=args.title,header_level=args.level) + + actual_fields = [] + + book = yaml.load(bf,Loader=yaml.Loader) + book_path = os.path.abspath(args.book) + + for bi in book['book']: + if 'fields' in bi: + actual_fields = bi['fields'] + if 'sections' in bi: + for section in bi['sections']: + if 'text' in section: + print(section['text'][args.lang]) + if 'modules' in section: + for m in section['modules']: + mod_path = os.path.join(os.path.dirname(book_path),m) + + with open(mod_path) as fm: + try: + generator = MetaGenerator() + table_items = generator.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=args.lang,pagebreak=args.pagebreak,createTitle=args.title,header_level=args.level) + + MarkdownGenerator.generate(table_items,pagebreak=args.pagebreak,title=args.title,header_level=args.level) + + except Exception as exc: + print(f'{type(exc).__name__} in {mod_path}: {exc}',file=sys.stderr) # verbose command line mode elif args.schema and args.meta and len(args.fields) > 0: - generator = MetaGenerator() - + # get actual fields actual_fields = [] if os.path.isfile(args.fields[0]): @@ -77,16 +101,27 @@ class CourseBuilder: else: actual_fields = args.fields - + + # get schema + actual_schema = None with open(args.schema) as f: - generator.set_schema(yaml.load(f,Loader=yaml.Loader)) + actual_schema = yaml.load(f,Loader=yaml.Loader) + # iterate through meta files for m in args.meta: with open(m) as fm: + + generator = MetaGenerator() + generator.set_schema(actual_schema) table_items = generator.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=args.lang,pagebreak=args.pagebreak,createTitle=args.title,header_level=args.level,template=args.template) - print(table_items) + if args.template: + TemplateGenerator.generate(table_items) + else: + MarkdownGenerator.generate(table_items,pagebreak=args.pagebreak,title=args.title,header_level=args.level) + + # print(table_items) else: parser.print_help() diff --git a/coursebuilder/metagenerator.py b/coursebuilder/metagenerator.py index b0d73fb..d16c25c 100644 --- a/coursebuilder/metagenerator.py +++ b/coursebuilder/metagenerator.py @@ -1,4 +1,5 @@ -import string +import os,string,sys +import yaml class MetaGenerator: @@ -91,19 +92,22 @@ class MetaGenerator: return [k,', '.join(parts)] - def process(self,meta,fields = [],lang = 'de',pagebreak = False,createTitle=False,header_level=1,template=None) -> []: + def process(self,meta,fields = [],lang = 'de',pagebreak = False,createTitle=False,header_level=1,template=None): table_items = [] # iterate over requested fields for field in fields: - # correlate with schema and append - 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' | 'num' : table_items.append(self.process_num(meta,field,lang)) - case 'multinum' : table_items.append(self.process_multinum(meta,field,lang)) - case 'multikey': table_items.append(self.process_multikey(meta,field,lang)) + try: + # correlate with schema and append + 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' | 'num' : table_items.append(self.process_num(meta,field,lang)) + case 'multinum' : table_items.append(self.process_multinum(meta,field,lang)) + case 'multikey': table_items.append(self.process_multikey(meta,field,lang)) + except Exception as exp: + print(field,exp,exp.args) # maybe return tableitems as np.Dataframe? return table_items @@ -118,29 +122,33 @@ class MetaGenerator: # # conventional MD mode # MarkdownGenerator.generate(table_items,pagebreak,createTitle,header_level=header_level) - def process_book_section(self,section,lang='de'): - pass + # def process_book_section(self,section,lang='de'): + # pass - def process_book(self,book,bookpath,create_title,pagebreak,lang='de',header_level=2): + # book mode + # def process_book(self,book,bookpath,create_title,pagebreak,lang='de',header_level=2): - actual_fields = [] + # actual_fields = [] - for bi in book['book']: - if 'fields' in bi: - actual_fields = bi['fields'] - if 'sections' in bi: - for section in bi['sections']: - if 'text' in section: - print(section['text'][lang]) - if 'modules' in section: - for m in section['modules']: - mod_path = os.path.join(os.path.dirname(bookpath),m) + # for bi in book['book']: + # if 'fields' in bi: + # actual_fields = bi['fields'] + # if 'sections' in bi: + # for section in bi['sections']: + # if 'text' in section: + # print(section['text'][lang]) + # if 'modules' in section: + # for m in section['modules']: + # mod_path = os.path.join(os.path.dirname(bookpath),m) - with open(mod_path) as fm: - try: - self.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=lang,pagebreak=pagebreak,createTitle=create_title,header_level=header_level) - except: - print(f'Error in {mod_path}',file=sys.stderr) + # with open(mod_path) as fm: + # try: + # table_items = self.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=lang,pagebreak=pagebreak,createTitle=create_title,header_level=header_level) + + # print(table_items) + + # except Exception as exc: + # print(f'{type(exc).__name__} in {mod_path}: {exc}',file=sys.stderr) diff --git a/test/simple/Makefile b/test/simple/Makefile index c282752..3047ab0 100644 --- a/test/simple/Makefile +++ b/test/simple/Makefile @@ -19,6 +19,10 @@ debug-template: debug-markdown: python ${coursebuilder} -s schema.yaml -m mod.cg.yaml mod.interactsys.yaml -l de -f name credits + +debug-book: + python ${coursebuilder} -s schema.yaml -b book.yaml -l de + debug: python ${coursebuilder} -s schema.yaml -m mod.cg.yaml -l de -f fields.yaml diff --git a/test/simple/book.yaml b/test/simple/book.yaml index f8544df..5ec66d5 100644 --- a/test/simple/book.yaml +++ b/test/simple/book.yaml @@ -11,7 +11,7 @@ book: - content - form-of-instruction - prerequisites - - media-of-instruction + - teaching-material - author-of-indenture - used-in - workload @@ -28,6 +28,10 @@ book: en: "## compulsory courses {.unnumbered}" - modules: - mod.cg.yaml - + - text: + de: "## Wahlbereich {.unnumbered}" + en: "## elective courses {.unnumbered}" + - modules: + - mod.interactsys.yaml diff --git a/test/simple/fields.yaml b/test/simple/fields.yaml index 92a0a08..bede013 100644 --- a/test/simple/fields.yaml +++ b/test/simple/fields.yaml @@ -5,7 +5,7 @@ fields: - content - form-of-instruction - prerequisites - - media-of-instruction + - teaching-material - author-of-indenture - used-in - workload diff --git a/test/simple/mod.cg.yaml b/test/simple/mod.cg.yaml index 55d76c4..e5b4201 100644 --- a/test/simple/mod.cg.yaml +++ b/test/simple/mod.cg.yaml @@ -74,7 +74,7 @@ content: * Overview visualizations * Graphical User Interfaces -media-of-instruction: +teaching-material: de: | * H5P Lernmodule * Lernforum diff --git a/test/simple/mod.interactsys.yaml b/test/simple/mod.interactsys.yaml index d8efd55..c20086e 100644 --- a/test/simple/mod.interactsys.yaml +++ b/test/simple/mod.interactsys.yaml @@ -61,7 +61,7 @@ content: * evaluation methods of interactive systems * statistical methods for UX design -media-of-instruction: +teaching-material: de: | H5P Lernmodule, Lernforum und Übungen am PC @@ -101,6 +101,9 @@ workload: form-of-exam: value: alternative + spec: + de: + en: frequency: value: once_per_year diff --git a/test/simple/schema.yaml b/test/simple/schema.yaml index 9a46893..8d54fab 100644 --- a/test/simple/schema.yaml +++ b/test/simple/schema.yaml @@ -142,7 +142,7 @@ prerequisites: # # Wie können die Studierenden sich auf die Teilnahme an diesem Modul vorbereiten? # -media-of-instruction: +teaching-material: type: str label: { de: "Literatur und multimediale Lehr- und Lernprogramme", From 445a976354fec73dc558c8a33cc53b870471b997 Mon Sep 17 00:00:00 2001 From: Hartmut Seichter Date: Mon, 6 May 2024 16:37:21 +0200 Subject: [PATCH 6/8] use generator properly in book mode --- coursebuilder/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coursebuilder/__main__.py b/coursebuilder/__main__.py index e8c44da..996e426 100644 --- a/coursebuilder/__main__.py +++ b/coursebuilder/__main__.py @@ -80,9 +80,9 @@ class CourseBuilder: with open(mod_path) as fm: try: - generator = MetaGenerator() - table_items = generator.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=args.lang,pagebreak=args.pagebreak,createTitle=args.title,header_level=args.level) + table_items = generator.process(yaml.load(fm,Loader=yaml.Loader),fields=actual_fields,lang=args.lang,pagebreak=args.pagebreak,createTitle=args.title,header_level=args.level) + MarkdownGenerator.generate(table_items,pagebreak=args.pagebreak,title=args.title,header_level=args.level) except Exception as exc: From 010aa5e72f83e4fc4bb3cfdbbd850fce59834796 Mon Sep 17 00:00:00 2001 From: Hartmut Seichter Date: Mon, 6 May 2024 16:37:34 +0200 Subject: [PATCH 7/8] better error handling --- coursebuilder/metagenerator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coursebuilder/metagenerator.py b/coursebuilder/metagenerator.py index d16c25c..fe70f33 100644 --- a/coursebuilder/metagenerator.py +++ b/coursebuilder/metagenerator.py @@ -107,7 +107,7 @@ class MetaGenerator: case 'multinum' : table_items.append(self.process_multinum(meta,field,lang)) case 'multikey': table_items.append(self.process_multikey(meta,field,lang)) except Exception as exp: - print(field,exp,exp.args) + print(field,' not resolvable in ',self.__schema,exp) # maybe return tableitems as np.Dataframe? return table_items From 0a5db9f8ad732bf65ac9648c426d9e0784966f18 Mon Sep 17 00:00:00 2001 From: Hartmut Seichter Date: Tue, 29 Oct 2024 17:46:46 +0100 Subject: [PATCH 8/8] just cleanup --- coursebuilder/tablegenerator.py | 18 +++++++++--------- test/simple/Makefile | 8 +++++--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/coursebuilder/tablegenerator.py b/coursebuilder/tablegenerator.py index 3bdc6f5..40b8afd 100644 --- a/coursebuilder/tablegenerator.py +++ b/coursebuilder/tablegenerator.py @@ -7,7 +7,7 @@ import os class TableGenerator: """ - Really hacky method to create latex > dvi > SVG to create images to include + Really hacky method to create latex > dvi > SVG to create images to include """ def __init__(self) -> None: @@ -22,7 +22,7 @@ class TableGenerator: for token in data.split(','): - t = tuple(token.split(':')[:2]) + t = tuple(token.split(':')[:2]) row = [t[0]] for k in list(self.__cols_map[lang].keys())[1:]: if k in t[1]: @@ -33,10 +33,10 @@ class TableGenerator: rows.append(' & '.join(row) + '\\\\') self.run_template(rows=rows) - - def run_template(self,rows = [],lang = 'de'): - + + def run_template(self,rows = [],lang = 'de') -> None: + t = string.Template(self.get_latex_template()) with tempfile.NamedTemporaryFile('w',delete=False,prefix='cb-') as fp: @@ -45,7 +45,7 @@ class TableGenerator: subprocess.run(["latex",fp.name]) subprocess.run(["dvisvgm",os.path.basename(fp.name) + '.dvi']) # subprocess.run(["mv",os.path.basename(fp.name) + '.svg','.']) - + def get_latex_template(self,lang = 'de') -> str: @@ -63,7 +63,7 @@ class TableGenerator: r'\begin{table}[ht]' + '\\begin{{tabular}} {{ {0} }}'.format(' '.join(layout)) + r'\hline' - r' ${th}' + r' ${th}' r'\hline' r' ${td}' + r'\hline' @@ -71,7 +71,7 @@ class TableGenerator: r'\end{table}' r'\end{document}') - + # # Kompetenz & Kennen & Wertung \\ @@ -79,4 +79,4 @@ class TableGenerator: # 2 & Latex & ++ \\ # 3 & Writer & +- \\ # -# latex image.tex;dvisvgm image.dvi \ No newline at end of file +# latex image.tex;dvisvgm image.dvi diff --git a/test/simple/Makefile b/test/simple/Makefile index 3047ab0..76e1d83 100644 --- a/test/simple/Makefile +++ b/test/simple/Makefile @@ -11,18 +11,20 @@ table.de.pdf: all: table.en.pdf table.de.pdf -clean: +clean: rm -f table.en.pdf table.de.pdf debug-template: python ${coursebuilder} -s schema.yaml -m mod.cg.yaml mod.interactsys.yaml -l de -f name credits --template "$$name | $$credits" debug-markdown: - python ${coursebuilder} -s schema.yaml -m mod.cg.yaml mod.interactsys.yaml -l de -f name credits + python ${coursebuilder} -s schema.yaml -m mod.cg.yaml mod.interactsys.yaml -l de -f name credits debug-book: - python ${coursebuilder} -s schema.yaml -b book.yaml -l de + python ${coursebuilder} -s schema.yaml -b book.yaml -l de debug: python ${coursebuilder} -s schema.yaml -m mod.cg.yaml -l de -f fields.yaml +table: + python ${coursebuilder} -s schema.yaml -b book.yaml -l de -f fields.yaml