From d41712e01037dc7edf60bfc8e39ddcc4036ec37c Mon Sep 17 00:00:00 2001 From: Hartmut Seichter Date: Wed, 29 May 2024 20:02:48 +0200 Subject: [PATCH] small update to get better query mode working --- coursebuilder/__main__.py | 56 +++---- docs/quickstart.md | 18 +-- test/Makefile | 6 +- test/schema.yaml | 318 +++++++++++++++----------------------- 4 files changed, 164 insertions(+), 234 deletions(-) diff --git a/coursebuilder/__main__.py b/coursebuilder/__main__.py index f731492..9b69f07 100644 --- a/coursebuilder/__main__.py +++ b/coursebuilder/__main__.py @@ -1,11 +1,11 @@ #!/usr/bin/env python """ -CourseBuilder +CourseBuilder -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 +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. """ @@ -27,7 +27,7 @@ class CourseBuilder: @staticmethod def generate(args): if args.schema and args.meta: - + # get actual fields actual_fields = None @@ -37,8 +37,8 @@ class CourseBuilder: actual_fields = yaml.load(ff,Loader=yaml.Loader)['fields'] else: # seem we have a list or None - actual_fields = args.fields - + actual_fields = args.fields + # get schema schema = None with open(args.schema) as f: @@ -66,12 +66,12 @@ class CourseBuilder: title_template=args.title, first_colwidth=args.leftcol) elif args.query: - + lot = schema.to_short_dict( meta=yaml.load(fm,Loader=yaml.Loader), fields=actual_fields, lang=args.lang) - + result_df.append(pd.DataFrame([lot])) else: MarkdownGenerator.generate_table( @@ -85,16 +85,17 @@ class CourseBuilder: # query mode if args.query and len(result_df): - + # got the list df = pd.concat(result_df,ignore_index=True) - + # generate a dataframe df_q = df.query(args.query) # generate a compound column --query-compound column:sum if args.query_compound: - df_q.loc[:,'form-of-instruction.sum'] = df_q['form-of-instruction'].apply(lambda x: sum(list(x.values()))) + print(args.query_compound) + df_q.loc[:,'form-of-instruction.sum'] = df_q['form-of-instruction'].apply(lambda x: sum(list(x.values()))) # --query-sort is parameterized as min:credits - hence direction:column if args.query_sort: @@ -111,7 +112,8 @@ class CourseBuilder: # set value transforms if args.query_template: - + + # no idea yet how to parameterize this ww = 'written' #df_q['form-of-exam'] = 'Schriftlich' if df_q.loc[:,'form-of-exam'] == 'written' else 'was anderes' # mm = Template("{'written':'S','oral':'mündlich'}[${v}]")? @@ -121,8 +123,8 @@ class CourseBuilder: df_summary = pd.DataFrame([{ 'sum.credits': df_q['credits'].sum() }]) - - # set labels + + # set labels directly! if args.query_labels: df_q.columns = args.query_labels @@ -135,7 +137,7 @@ class CourseBuilder: @staticmethod def run(): - + # arguments parser = ArgumentParser(description='versatile curricula generator') @@ -144,7 +146,7 @@ class CourseBuilder: 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") - + # query mode parser.add_argument('-q','--query', type=str, default=None, help="compound query to select items") parser.add_argument('-qs','--query-sort',type=str,default=None,help="sort query with a min/max over a column like min:credits") @@ -152,8 +154,8 @@ class CourseBuilder: parser.add_argument('-qf','--query-filter',type=str,default=[],action="extend", nargs="+",help="filter final list of columns for output") parser.add_argument('-ql','--query-labels',type=str,default=[],action="extend", nargs="+",help="new labels for query like") parser.add_argument('-qt','--query-template',type=str,default=[],action="extend", nargs="+",help="templates for values in the form of {value}") - - + + # create pagebreaks parser.add_argument('-p','--pagebreak',action="store_true",help="add a pagebreak after each module") parser.add_argument('--title',type=str,default=None,help="template for title - use curly brackets (i.e. {}) to mark where the title string is inserted") @@ -163,22 +165,22 @@ class CourseBuilder: parser.add_argument('--template',type=str,default=None,help='defines a template to be used with fields') parser.add_argument('-o','--out',type=str,default=None,help='set the output type') parser.add_argument('--legacy',action="store_true",help="use legacy generator mode for compatibility") - + parser.add_argument('--leftcol',type=int,default=35,help='maximum size of left column') # get arguments args = parser.parse_args() - + if args.table_gen: - tg = TableGenerator() + tg = TableGenerator() tg.generate_table(args.table_gen) return # book mode with predefined setting from a book file if args.book and args.schema: - + with open(args.book) as bf: actual_fields = [] @@ -199,13 +201,13 @@ class CourseBuilder: # gernerate section wise parts if 'modules' in section: - + # override fields args.fields = actual_fields - + # expand filenames to be relative to the book args.meta = [os.path.join(os.path.dirname(book_path),mod_path) for mod_path in section['modules']] - + CourseBuilder.generate(args=args) # verbose command line mode @@ -217,6 +219,6 @@ class CourseBuilder: # run as main if __name__ == '__main__': # recommended setting for pandas - pd.options.mode.copy_on_write = True + pd.options.mode.copy_on_write = True # run CourseBuilder.run() diff --git a/docs/quickstart.md b/docs/quickstart.md index a293ef4..d682b75 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -1,23 +1,23 @@ -# Concept +# concept -The concept behind coursebuilder is to store curricula descriptions in `YAML` files that can be versioned in a git repository. Unlike classic databases an observable and well defined versioning is paramount in these descriptions as they are the legal foundation for study and exam regulations. +The concept behind coursebuilder is to store curricula descriptions in `YAML` files that can be versioned in a git repository. Unlike classic databases, an observable and well defined versioning is paramount in these descriptions as they are the legal foundation for study and exam regulations. The following pieces play together here: - `schema` files, usually a `schema.yaml` -- `mod` files, usually something along the lines of `mod.coursecode.yaml` +- `mod` files, usually something along the lines of `mod.coursecode.yaml` - `book` files describing a whole regulation set and course global details -- some sort of transformation with `coursebuilder` into Markdown that is piped through [pandoc](https://pandoc.org) in order to generate PDF, HTML and other representation from this code +- some sort of transformation with `coursebuilder` into Markdown that is piped through [pandoc](https://pandoc.org) in order to generate PDF, HTML and other representation from this code -# schema files +# schema files Schema files are responsible to describe the used structures in a database. The following datatypes are supported: - `str` a simple string, can be accompanied with a `template` - `enum` a classic enum datatype with a fixed set of values -- `num` a numeric datatype +- `num` a numeric datatype - `multinum` an array type with the possibility to `spec` each value -- `multikey` a key-value type with additional numeric data associated with each key instance +- `multikey` a key-value type with additional numeric data associated with each key instance # mod files (modules) @@ -31,11 +31,11 @@ Modules describe a course in detail and implement an instance of the schema file ```yaml # this would reside in a schema field on top level # a field of name 'id' -id: # name of the field +id: # name of the field type: str # sets the datatype to str translatable: false # enforces the value is not translatable (default is true) label: { # label describes the meaning of the datatype in regards of the schema de: "Kürzel", # translation of the label in German (de) en: "code" # translation of the label in English (en) } -``` \ No newline at end of file +``` diff --git a/test/Makefile b/test/Makefile index 9fed28a..6aa2e73 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,4 +1,4 @@ - +# debug make file for testing build_dir := build target_en := ${build_dir}/table.en.pdf target_de := ${build_dir}/table.de.pdf @@ -25,7 +25,7 @@ ${target_de}: mod.cg.yaml ${target_de_book}: *.yaml python ${coursebuilder} -s schema.yaml -b book.yaml -p --title "### {}" -l de --leftcol 25 --legacy | pandoc ${target_flags} -V toc:true -V lang:de -o ${target_de_book} -clean: +clean: rm -f ${targets} debug: @@ -38,4 +38,4 @@ debug-query: debug-query-book: python ${coursebuilder} -s schema.yaml -b book.yaml -q "kind=='compulsory'" -qs min:credits -qc form-of-instruction -qf name credits form-of-instruction -ql Modulname Kürzel Kreditpunkte -.PHONY: clean \ No newline at end of file +.PHONY: clean diff --git a/test/schema.yaml b/test/schema.yaml index a7cf2f3..fac24c4 100644 --- a/test/schema.yaml +++ b/test/schema.yaml @@ -1,300 +1,228 @@ # fields in curricular description -# leaning on methods in OpenAPI 3.0 +# leaning on methods in OpenAPI 3.0 # # Modulname # name: - type: str - label: - de: "Modulname" - en: "name of course" + type: str + label: + de: "Modulname" + en: "name of course" # # Modulverantwortliche:r # -instructor: - type: str - label: - de: "Modulverantwortlicher / Modulverantwortliche" - en: "module instructor" - +instructor: + type: str + label: + de: "Modulverantwortlicher / Modulverantwortliche" + en: "module instructor" # # Kürzel / ID # -id: - type: str - translatable: false - label: { - de: "Kürzel", - en: "code" - } - +id: + type: str + translatable: false + label: { de: "Kürzel", en: "code" } # # Qualifikationsziele # -# Welche fachbezogenen, methodischen, fachübergreifende Kompetenzen, -# Schlüsselqualifikationen - werden erzielt (erworben)? Diese sind +# Welche fachbezogenen, methodischen, fachübergreifende Kompetenzen, +# Schlüsselqualifikationen - werden erzielt (erworben)? Diese sind # an der zu definierenden Gesamtqualifikation (angestrebter Abschluss) auszurichten. -# +# # Lernergebnisse sind Aussagen darüber, was ein Studierender nach Abschluss des Moduls weiß, -# versteht und in der Lage ist zu tun. Die Formulierung sollte sich am Qualifikationsrahmen +# versteht und in der Lage ist zu tun. Die Formulierung sollte sich am Qualifikationsrahmen # für Deutsche Hochschulabschlüsse orientieren und Inhaltswiederholungen vermeiden. -# -# Des Weiteren finden Sie im QM-Portal die „Handreichung zur Beschreibung von Lernzielen“ +# +# Des Weiteren finden Sie im QM-Portal die „Handreichung zur Beschreibung von Lernzielen“ # als Formulierungshilfe. goal: - type: str - label: { - de: "Qualifikationsziele", - en: "educational goal" - } + type: str + label: { de: "Qualifikationsziele", en: "educational goal" } # # Modulinhalte # -# Welche fachlichen, methodischen, fachpraktischen und fächerübergreifenden -# Inhalte sollen vermittelt werden? -# +# Welche fachlichen, methodischen, fachpraktischen und fächerübergreifenden +# Inhalte sollen vermittelt werden? +# # Es ist ein stichpunktartiges Inhaltsverzeichnis zu erstellen. -content: - type: str - label: { - de: "Modulinhalte", - en: "content" - } +content: + type: str + label: { de: "Modulinhalte", en: "content" } # # Lehrform # # -# Welche Lehr- und Lernformen werden angewendet? -# (Vorlesungen, Übungen, Seminare, Praktika, +# Welche Lehr- und Lernformen werden angewendet? +# (Vorlesungen, Übungen, Seminare, Praktika, # Projektarbeit, Selbststudium) # # Es sind nur Werte aus der Prüfungsordung zugelassen -# -form-of-instruction: - type: multikey - label: { - de: "Lehrform(en)", - en: "form of instruction" +# +form-of-instruction: + type: multikey + label: { de: "Lehrform(en)", en: "form of instruction" } + keys: + { + "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" }, } - keys: { - '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" - } - } - template: - de: "{key} ({value}SWS)" - en: "{key} ({value}SWS)" + template: + de: "{key} ({value}SWS)" + en: "{key} ({value}SWS)" # # Voraussetzungen für die Teilnahme # -# Für jedes Modul sind die Voraussetzungen für die Teilnahme zu beschreiben. +# Für jedes Modul sind die Voraussetzungen für die Teilnahme zu beschreiben. # Welche Kenntnisse, Fähigkeiten und Fertigkeiten sind für eine # erfolgreiche Teilnahme vorauszusetzen? -# -# Alternativ können die Module benannt werden welche für die erfolgreiche +# +# Alternativ können die Module benannt werden welche für die erfolgreiche # Teilnahme im Vorfeld zu belegen sind. -prerequisites: - type: str - label: { - de: "Voraussetzungen für die Teilnahme", - en: "prerequisites" - } +prerequisites: + type: str + label: { de: "Voraussetzungen für die Teilnahme", en: "prerequisites" } # # Literatur und multimediale Lehr- und Lernprogramme # -# +# # Wie können die Studierenden sich auf die Teilnahme an diesem Modul vorbereiten? -# -teaching-material: - type: str - label: { - de: "Literatur und multimediale Lehr- und Lernprogramme", - en: "media of instruction" +# +teaching-material: + type: str + label: + { + de: "Literatur und multimediale Lehr- und Lernprogramme", + en: "media of instruction", } # # Lehrbriefautor # -author-of-indenture: - type: str - label: { - de: "Lehrbriefautor", - en: "author of indenture" - } +author-of-indenture: + type: str + label: { de: "Lehrbriefautor", en: "author of indenture" } # # Verwendung in (Studienprogramm) # used-in: - type: str - label: { - de: "Verwendung", - en: "used in study programs" - } + type: str + label: { de: "Verwendung", en: "used in study programs" } # # Arbeitsaufwand # workload: - type: str - label: { - de: "Arbeitsaufwand / Gesamtworkload", - en: "workload" - } + type: str + label: { de: "Arbeitsaufwand / Gesamtworkload", en: "workload" } # # credits/ECTS # credits: - type: num - unit: ECTS - label: { - en: "credits and weight of mark", - de: "Kreditpunkte und Gewichtung der Note in der Gesamtnote" + type: num + unit: ECTS + label: + { + en: "credits and weight of mark", + de: "Kreditpunkte und Gewichtung der Note in der Gesamtnote", } - template: - de: "{value}CP, Gewichtung: {value}CP von 120CP " - en: "{value}CP, weight: {value} / 120 " - + template: + de: "{value}CP, Gewichtung: {value}CP von 120CP " + en: "{value}CP, weight: {value} / 120 " # # Leistungsnachweis # -form-of-exam: - type: enum - label: { - de: "Leistungsnachweis", - en: "form of examination" +form-of-exam: + type: enum + label: { de: "Leistungsnachweis", en: "form of examination" } + values: + { + "written": { de: "Schriftliche Prüfung", en: "written exam" }, + "oral": { de: "Mündliche Prüfung", en: "oral exam" }, + "alternative": + { de: "Alternative Prüfungunsleistung", en: "alternative examination" }, } - values: { - 'written' : { - de: "Schriftliche Prüfung", - en: "written exam" - }, - 'oral' : { - de: "Mündliche Prüfung", - en: "oral exam" - }, - 'alternative' : { - de: "Alternative Prüfungunsleistung", - en: "alternative examination" - } - } - spec: true - template: - de: "{value} ({spec})" - en: "{value} ({spec})" - + spec: true + template: + de: "{value} ({spec})" + en: "{value} ({spec})" # # Semester # -term: - type: multinum - label: { - de: "Semester", - en: "term" - } - template: - de: "{value}\\. Semester" - en: "{value}\\. semester" +term: + type: multinum + label: { de: "Semester", en: "term" } + template: + de: "{value}\\. Semester" + en: "{value}\\. semester" # # Häufigkeit des Angebots # -frequency: - type: enum - label: { - de: "Häufigkeit des Angebots", - en: "frequency of Offer" - } - values: { - 'once_per_term' : { - de: "jedes Semester", - en: "every semester" - }, - 'once_per_year' : { - de: "einmal im Studienjahr", - en: "once per study year" - } +frequency: + type: enum + label: { de: "Häufigkeit des Angebots", en: "frequency of Offer" } + values: + { + "once_per_term": { de: "jedes Semester", en: "every semester" }, + "once_per_year": + { de: "einmal im Studienjahr", en: "once per study year" }, } # # Dauer des Angebots # -duration: - type: int - label: - de: Dauer - en: duration - template: - de: "{value} Semester" - en: "{value} term(s)" +duration: + type: int + label: + de: Dauer + en: duration + template: + de: "{value} Semester" + en: "{value} term(s)" # # Art der Veranstaltung # kind: - type: enum - label: { - de: 'Art der Veranstaltung (Pflicht, Wahl, etc.)', - en: 'kind of module (compulsory, elective)' + type: enum + label: + { + de: "Art der Veranstaltung (Pflicht, Wahl, etc.)", + en: "kind of module (compulsory, elective)", } - values: { - 'compulsory': { - de: "Pflicht", - en: "compulsory" - }, - 'elective' : { - de: "Wahl/Wahlpflicht", - en: "elective" - } + values: + { + "compulsory": { de: "Pflicht", en: "compulsory" }, + "elective": { de: "Wahl/Wahlpflicht", en: "elective" }, } # # Freiform Bemerkungen # -remarks: - type: str - label: { - de: "Besonderes", - en: "remarks" - } - - - \ No newline at end of file +remarks: + type: str + label: { de: "Besonderes", en: "remarks" }