diff --git a/coursebuilder/app.py b/coursebuilder/app.py index 1b1b57b..9581644 100644 --- a/coursebuilder/app.py +++ b/coursebuilder/app.py @@ -10,10 +10,6 @@ import yaml from coursebuilder.schema import Schema -class LanguageSelector: - context_lang: str - - class Course: i18n_name: str = "lang.{}.yaml" mod_name: str = "mod.yaml" @@ -28,19 +24,19 @@ class Course: for p in path.parent.glob(Course.i18n_name.format("*")) } - def validate(self, *, schema: Schema, lang: str) -> None: - print(self.__data) - pass - def __getitem__(self, name: str, /) -> Any: - return ( - self.__data[name] - if name in self.__data.keys() - else self.__i18n[LanguageSelector.context_lang][name] - ) + """this overload magic function takes a key and a language code like my_key.en or my_key.de""" + s = name.split(".") - def __getattr__(self, name: str, /) -> Any: - return self.__data[name] + if len(s) != 2: + raise ValueError( + "query with item selector requires form ['key.lang'] alternative is ['key.*']" + ) + return ( + self.__i18n[s[-1]][s[0]] + if s[-1] in self.__i18n.keys() and s[0] in self.__i18n[s[-1]].keys() + else self.__data[s[0]] + ) def __str__(self): return f"data:{self.__data}\ni18n:{self.__i18n}" @@ -83,22 +79,52 @@ def main(): type=str, help="schema to validate against", ) + parser.add_argument( + "-f", + "--fields", + help="Fields to be used, the table will be build accordingly", + action="extend", + nargs="+", + type=str, + ) + parser.add_argument( + "-c", + "--create", + help="Fields to be used, the table will be build accordingly", + type=str, + ) # get arguments args = parser.parse_args() - # just input + # with input if args.input and args.schema: with open(args.schema) as f_schema: - schema = Schema(schema=yaml.load(f_schema, Loader=yaml.Loader)) - sc = StudyCourse(path=(Path(".") / args.input).absolute()) + schema = Schema(schema=yaml.load(f_schema, Loader=yaml.Loader)) # Schema + sc = StudyCourse(path=(Path(".") / args.input).absolute()) # Database - LanguageSelector.context_lang = "de" + actual_fields = args.fields if args.fields else schema.keys() - for k in schema.keys(): - print(k) - for shortcode, course in sc.courses.items(): - print(course[k]) + for field in actual_fields: + for lang in sc.languages: + for shortcode, course in sc.courses.items(): + print( + field, + "@", + shortcode, + ".", + lang, + "\n", + course[f"{field}.{lang}"], + ) + + elif args.schema and args.create: + schema = Schema( + schema=yaml.load(open(args.schema), Loader=yaml.Loader) + ) # Schema + print(schema.fields()) + print(schema.types()) + print(schema.facets()) # run as main diff --git a/coursebuilder/schema.py b/coursebuilder/schema.py index 64e61d1..ffe3736 100644 --- a/coursebuilder/schema.py +++ b/coursebuilder/schema.py @@ -1,142 +1,159 @@ class Schema: + __yes_vals: list[str] = ["true", "yes", "on"] + + __unique: str = "unique" + __spec: str = "spec" + __facets: str = "facets" + __fields: str = "fields" + __type: str = "type" + __label: str = "label" + def __init__(self, *, schema: dict) -> None: self.__schema = schema - def __getitem__(self, field): - return self.__schema[field] + def facets(self) -> list[str]: + return list(self.__schema[Schema.__facets]) - def keys(self): - return self.__schema.keys() + def fields(self) -> list[str]: + return list(self.__schema[Schema.__fields].keys()) - def is_translatable(self, field): - if "translatable" in self.__schema[field]: - return self.__schema[field]["translatable"] - else: - return True + def types(self) -> dict: + return { + field: a[Schema.__type] + for field, a in self.__schema[Schema.__fields].items() + } - def needs_spec(self, field): - if "spec" in self.__schema[field]: - return self.__schema[field] - else: - return False + def is_unique(self, field: str) -> bool: + return ( + Schema.__yes_vals in self.__schema[field][Schema.__unique].lower() + if Schema.__unique in self.__schema[field].keys() + else True + ) - def get_value(self, meta: dict, field: str, lang: str): - """ - treats receiving the value like a variant, - returns values with their language specific representations - """ - match self.__schema[field]["type"]: - case "str": - return ( - meta[field][lang] - if self.is_translatable(field) - else meta[field]["value"] - ) - case "enum" | "int" | "num" | "multikey": - return meta[field]["value"] - case "multinum": - return ( - meta[field]["value"] - if hasattr(meta[field]["value"], "__iter__") - else [ - meta[field]["value"], - ] - ) # force list! + def needs_spec(self, field: str) -> bool: + return ( + Schema.__yes_vals in self.__schema[field][Schema.__spec].lower() + if Schema.__spec in self.__schema[field].keys() + else False + ) - def to_list_of_dict(self, meta, fields, lang): - """ - generates a list of dict which can easily be converted - to a pandas dataframe - """ - # list comprehension for rows - return [ - { - "field": field, # field name - "lang": lang, # language shortcode - "type": self.__schema[field]["type"], # datatype - "label": self.__schema[field]["label"][lang], # label - "value": self.get_value(meta, field, lang), # actual value - "template": self.__schema[field]["template"][lang] - if "template" in self.__schema[field] - else None, - # getting crazy with nested dict comprehension - "enum_values": { - k: v[lang] for (k, v) in self.__schema[field]["values"].items() - } - if "enum" in self.__schema[field]["type"] - else None, - "key_values": { - k: v[lang] for (k, v) in self.__schema[field]["keys"].items() - } - if "multikey" in self.__schema[field]["type"] - else None, - "spec": meta[field]["spec"][lang] if "spec" in meta[field] else None, - } - for field in fields - ] + # def get_value(self, meta: dict, field: str, lang: str): + # """ + # treats receiving the value like a variant, + # returns values with their language specific representations + # """ + # match self.__schema[field]["type"]: + # case "str": + # return ( + # meta[field][lang] + # if self.is_translatable(field) + # else meta[field]["value"] + # ) + # case "enum" | "int" | "num" | "multikey": + # return meta[field]["value"] + # case "multinum": + # return ( + # meta[field]["value"] + # if hasattr(meta[field]["value"], "__iter__") + # else [ + # meta[field]["value"], + # ] + # ) # force list! - def to_short_dict(self, meta, fields, lang): - """ - generates a short version of dict which can easily be converted - to a pandas dataframe - """ - # dict comprehension for whole meta part - return {field: self.get_value(meta, field, lang) for field in fields} + # def to_list_of_dict(self, meta, fields, lang): + # """ + # generates a list of dict which can easily be converted + # to a pandas dataframe + # """ + # # list comprehension for rows + # return [ + # { + # "field": field, # field name + # "lang": lang, # language shortcode + # "type": self.__schema[field]["type"], # datatype + # "label": self.__schema[field]["label"][lang], # label + # "value": self.get_value(meta, field, lang), # actual value + # "template": self.__schema[field]["template"][lang] + # if "template" in self.__schema[field] + # else None, + # # getting crazy with nested dict comprehension + # "enum_values": { + # k: v[lang] for (k, v) in self.__schema[field]["values"].items() + # } + # if "enum" in self.__schema[field]["type"] + # else None, + # "key_values": { + # k: v[lang] for (k, v) in self.__schema[field]["keys"].items() + # } + # if "multikey" in self.__schema[field]["type"] + # else None, + # "spec": meta[field]["spec"][lang] if "spec" in meta[field] else None, + # } + # for field in fields + # ] - def to_list_of_tuple(self, meta, fields, lang): - """ - generates a list of tuples with a label and value (text) - this is usually consumed by a Markdown generator + # def to_short_dict(self, meta, fields, lang): + # """ + # generates a short version of dict which can easily be converted + # to a pandas dataframe + # """ + # # dict comprehension for whole meta part + # return {field: self.get_value(meta, field, lang) for field in fields} - todo: needs deuglyfication of free standing loop, templates are possible for all - """ - list = [] - for r in self.to_list_of_dict(meta, fields, lang): - match r["type"]: - case "str": - list.append((r["label"], r["value"])) - case "int" | "num": - list.append( - ( - r["label"], - r["template"].format(value=r["value"], spec=r["spec"]) - if r["template"] - else r["value"], - ) - ) - case "enum": - list.append( - ( - r["label"], - r["template"].format( - value=r["enum_values"][r["value"]], spec=r["spec"] - ) - if r["template"] - else r["enum_values"][r["value"]], - ) - ) - case "multikey": - list.append( - ( - r["label"], - ", ".join( - [ - r["template"].format( - key=r["key_values"][k], value=v - ) - for k, v in r["value"].items() - ] - ), - ) - ) - case "multinum": - list.append( - ( - r["label"], - ", ".join( - r["template"].format(value=v) for v in r["value"] - ), - ) - ) + # def to_list_of_tuple(self, meta, fields, lang): + # """ + # generates a list of tuples with a label and value (text) + # this is usually consumed by a Markdown generator - return list + # todo: needs deuglyfication of free standing loop, templates are possible for all + # """ + # list = [] + # for r in self.to_list_of_dict(meta, fields, lang): + # match r["type"]: + # case "str": + # list.append((r["label"], r["value"])) + # case "int" | "num": + # list.append( + # ( + # r["label"], + # r["template"].format(value=r["value"], spec=r["spec"]) + # if r["template"] + # else r["value"], + # ) + # ) + # case "enum": + # list.append( + # ( + # r["label"], + # r["template"].format( + # value=r["enum_values"][r["value"]], spec=r["spec"] + # ) + # if r["template"] + # else r["enum_values"][r["value"]], + # ) + # ) + # case "multikey": + # list.append( + # ( + # r["label"], + # ", ".join( + # [ + # r["template"].format( + # key=r["key_values"][k], value=v + # ) + # for k, v in r["value"].items() + # ] + # ), + # ) + # ) + # case "multinum": + # list.append( + # ( + # r["label"], + # ", ".join( + # r["template"].format(value=v) for v in r["value"] + # ), + # ) + # ) + + # return list diff --git a/test/maacs/v2/3dcc/mod.yaml b/test/maacs/v2/3dcc/mod.yaml index 6f944ea..5d0f618 100644 --- a/test/maacs/v2/3dcc/mod.yaml +++ b/test/maacs/v2/3dcc/mod.yaml @@ -13,8 +13,7 @@ credits: 5 form-of-exam: type: alternative -duration: - value: 1 +duration: 1 author-of-indenture: diff --git a/test/maacs/v2/schema.yaml b/test/maacs/v2/schema.yaml index dbaf221..8c28d7e 100644 --- a/test/maacs/v2/schema.yaml +++ b/test/maacs/v2/schema.yaml @@ -1,231 +1,239 @@ -# fields in curricular description -# leaning on methods in OpenAPI 3.0 +facets: ["de", "en"] -# -# Modulname -# -name: - type: str - label: - de: "Modulname" - en: "name of course" +templates: + name: + de: Modulname {value} + en: name of course {value} -# -# Modulverantwortliche:r -# -instructor: - type: str - label: - de: "Modulverantwortlicher / Modulverantwortliche" - en: "module instructor" +fields: + # + # Kürzel / ID + # + id: + type: str + translatable: false + label: { de: "Kürzel", en: "code" } + # + # Modulname + # + name: + type: str + label: + de: "Modulname" + en: "name of course" -# -# Kürzel / ID -# -id: - type: str - translatable: false - label: { de: "Kürzel", en: "code" } + # + # Modulverantwortliche:r + # + instructor: + type: str + label: + de: "Modulverantwortlicher / Modulverantwortliche" + en: "module instructor" -# -# Qualifikationsziele -# + # + # Qualifikationsziele + # -# 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 -# für Deutsche Hochschulabschlüsse orientieren und Inhaltswiederholungen vermeiden. -# -# Des Weiteren finden Sie im QM-Portal die „Handreichung zur Beschreibung von Lernzielen“ -# als Formulierungshilfe. + # 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 + # für Deutsche Hochschulabschlüsse orientieren und Inhaltswiederholungen vermeiden. + # + # Des Weiteren finden Sie im QM-Portal die „Handreichung zur Beschreibung von Lernzielen“ + # als Formulierungshilfe. -goal: - type: str - label: { de: "Qualifikationsziele", en: "educational goal" } + goal: + type: str + label: { de: "Qualifikationsziele", en: "educational goal" } -# -# Modulinhalte -# + # + # Modulinhalte + # -# Welche fachlichen, methodischen, fachpraktischen und fächerübergreifenden -# Inhalte sollen vermittelt werden? -# -# Es ist ein stichpunktartiges Inhaltsverzeichnis zu erstellen. + # 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 -# + # + # Lehrform + # -# -# 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" } - 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)" + # + # 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" } + 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)" -# -# Voraussetzungen für die Teilnahme -# + # + # Voraussetzungen für die Teilnahme + # -# 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 -# Teilnahme im Vorfeld zu belegen sind. + # 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 + # 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", - } + # + # 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", + } -# -# Lehrbriefautor -# -author-of-indenture: - type: str - label: { de: "Lehrbriefautor", en: "author of indenture" } + # + # Lehrbriefautor + # + 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" } + # + # Verwendung in (Studienprogramm) + # + # used-in: + # type: str + # label: { de: "Verwendung", en: "used in study programs" } -# -# Arbeitsaufwand -# -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", - } - template: - de: "{value}CP, Gewichtung: {value}CP von 120CP " - en: "{value}CP, weight: {value} / 120 " + # + # Arbeitsaufwand + # + 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", + } + 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" } - 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})" + # + # Leistungsnachweis + # + 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", + }, + } + 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" + # + # 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" }, -# } + # + # 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" }, + # } -# -# Dauer des Angebots -# -# duration: -# type: int -# label: -# de: Dauer -# en: duration -# template: -# de: "{value} Semester" -# en: "{value} term(s)" + # + # Dauer des Angebots + # + # 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)", - } - values: - { - "compulsory": { de: "Pflicht", en: "compulsory" }, - "elective": { de: "Wahl/Wahlpflicht", en: "elective" }, - } + # + # Art der Veranstaltung + # + # kind: + # 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" }, + # } -# -# Freiform Bemerkungen -# -remarks: - type: str - label: { de: "Besonderes", en: "remarks" } + # + # Freiform Bemerkungen + # + remarks: + type: str + label: { de: "Besonderes", en: "remarks" }