some initial testdata again

This commit is contained in:
Hartmut Seichter 2025-03-17 20:47:27 +01:00
parent c4ba0f5e75
commit 2e1490d98f
8 changed files with 448 additions and 96 deletions

View file

@ -2,29 +2,67 @@
# coursebuilder
#
from typing import Any
from argparse import ArgumentParser
from pathlib import Path
import yaml
from coursebuilder.schema import Schema
class LanguageSelector:
context_lang: str
class Course:
def
i18n_name: str = "lang.{}.yaml"
mod_name: str = "mod.yaml"
def __init__(self, *, path: Path) -> None:
with open(path) as f:
# resolve rest of bundle
self.__data = yaml.load(f, Loader=yaml.Loader)
# load i18n overlays
self.__i18n = {
f"{str(p).split('.')[1]}": yaml.load(open(p), Loader=yaml.Loader)
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]
)
def __getattr__(self, name: str, /) -> Any:
return self.__data[name]
def __str__(self):
return f"data:{self.__data}\ni18n:{self.__i18n}"
class StudyCourse:
def __init__(self, data: dict | None, path: Path | None):
self.path: Path | None = path
self.data: dict | None = data
def __init__(self, *, path: Path) -> None:
self.path = path
courses: list[Course] = []
@staticmethod
def load(*, path: str):
with open(path) as f:
data = yaml.load(f, Loader=yaml.Loader)
return StudyCourse(data=data, path=Path(path))
self.__data = yaml.load(f, Loader=yaml.Loader)
self.courses: dict[str, Course] = {
f"{c}": Course(path=self.path.parent / c / Course.mod_name)
for c in self.__data["courses"]
}
def __getattr__(self, name: str, /) -> Any:
return self.__data[name] if self.__data else None
def __str__(self):
return f"path: {self.path}\ndata: {self.data}"
return f"path: {self.path}\ndata: {self.__data}"
def main():
@ -36,16 +74,31 @@ def main():
"-i",
"--input",
type=str,
help="folder with project data",
help="course file with definition of the course",
)
parser.add_argument(
"-s",
"--schema",
type=str,
help="schema to validate against",
)
# get arguments
args = parser.parse_args()
# just input
if args.input:
sc = StudyCourse.load(path=(Path(".") / args.input).absolute())
print(sc)
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())
LanguageSelector.context_lang = "de"
for k in schema.keys():
print(k)
for shortcode, course in sc.courses.items():
print(course[k])
# run as main

View file

@ -1,8 +1,5 @@
import string
class Schema:
def __init__(self,schema) -> None:
def __init__(self, *, schema: dict) -> None:
self.__schema = schema
def __getitem__(self, field):
@ -12,26 +9,39 @@ class Schema:
return self.__schema.keys()
def is_translatable(self, field):
if 'translatable' in self.__schema[field]:
return self.__schema[field]['translatable']
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]:
if "spec" in self.__schema[field]:
return self.__schema[field]
else:
return False
def get_value(self,meta,field,lang):
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!
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_list_of_dict(self, meta, fields, lang):
"""
@ -39,19 +49,31 @@ class Schema:
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,
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
"enum_values": {
k: v[lang] for (k, v) in self.__schema[field]["values"].items()
}
for field in fields]
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_short_dict(self, meta, fields, lang):
"""
@ -70,18 +92,51 @@ class Schema:
"""
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'])) )
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

View file

@ -1,4 +1,4 @@
fields:
field-names:
- name
- instructor
- id

View file

@ -34,8 +34,7 @@ teaching-material: |
prerequisites: |
Formale Voraussetzung bestehen nicht. Für eine erfolgreiche Teilnahme sollte das Modul „Grundlagen der Computergrafik“ im Vorfeld belegt werden.
author-of-indenture:
form-of-exam:
spec: abzugebende Projektarbeit (70%) und mündliche Prüfung (30% ~20min)
workload: "150h Insgesamt bestehend aus 60 Stunden Präsenzzeit, 60 Stunden Selbststudium, 30h Prüfung und Prüfungsvorbereitung"
remarks:

View file

@ -29,5 +29,3 @@ workload: "overall 150h comprising of 60h in-person training, 60h of self-study
form-of-exam:
spec: submitted project (70%) and oral exam (30% ~20min)
remarks:

View file

@ -1,9 +1,9 @@
id: 3DCC
name: 3D Content Creation
instructor: Prof. Hartmut Seichter, PhD
id: 3DCC
form-of-instruction:
- seminar: 2
- exersise: 2
@ -12,7 +12,12 @@ credits: 5
form-of-exam:
type: alternative
spec: abzugebende Projektarbeit (70%) und mündliche Prüfung (30% ~20min)
duration:
value: 1
author-of-indenture:
kind: elective
remarks:

View file

@ -1,4 +1,15 @@
# shortcode
shortcode: maacs
# name
name: Applied Computer Science (Master of Science)
# validation schema
schema: schema.yaml
# languages (oder defines the order of overlay)
languages: [de, en]
# courses
courses:
- 3dcc

231
test/maacs/v2/schema.yaml Normal file
View file

@ -0,0 +1,231 @@
# fields in curricular description
# leaning on methods in OpenAPI 3.0
#
# Modulname
#
name:
type: str
label:
de: "Modulname"
en: "name of course"
#
# Modulverantwortliche:r
#
instructor:
type: str
label:
de: "Modulverantwortlicher / Modulverantwortliche"
en: "module instructor"
#
# Kürzel / ID
#
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
# 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" }
#
# Modulinhalte
#
# 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" }
#
# 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)"
#
# 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.
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",
}
#
# 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" }
#
# 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})"
#
# 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" },
# }
#
# 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" },
}
#
# Freiform Bemerkungen
#
remarks:
type: str
label: { de: "Besonderes", en: "remarks" }