some initial testdata again
This commit is contained in:
parent
c4ba0f5e75
commit
2e1490d98f
8 changed files with 448 additions and 96 deletions
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
@ -10,58 +7,83 @@ class Schema:
|
|||
|
||||
def keys(self):
|
||||
return self.__schema.keys()
|
||||
|
||||
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]:
|
||||
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 get_value(self,meta,field,lang):
|
||||
|
||||
def get_value(self, meta: dict, field: str, lang: str):
|
||||
"""
|
||||
treats receiving the value like a variant,
|
||||
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_list_of_dict(self,meta,fields,lang):
|
||||
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):
|
||||
"""
|
||||
generates a list of dict which can easily be converted
|
||||
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]
|
||||
|
||||
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_short_dict(self,meta,fields,lang):
|
||||
def to_short_dict(self, meta, fields, lang):
|
||||
"""
|
||||
generates a short version of dict which can easily be converted
|
||||
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_tuple(self,meta,fields,lang):
|
||||
return {field: self.get_value(meta, field, lang) 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
|
||||
|
@ -69,19 +91,52 @@ class Schema:
|
|||
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'])) )
|
||||
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
|
||||
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
fields:
|
||||
- name
|
||||
- instructor
|
||||
- id
|
||||
- goal
|
||||
- content
|
||||
- form-of-instruction
|
||||
- prerequisites
|
||||
- teaching-material
|
||||
- author-of-indenture
|
||||
- used-in
|
||||
- workload
|
||||
- credits
|
||||
- form-of-exam
|
||||
- term
|
||||
- frequency
|
||||
- duration
|
||||
- kind
|
||||
- remarks
|
||||
field-names:
|
||||
- name
|
||||
- instructor
|
||||
- id
|
||||
- goal
|
||||
- content
|
||||
- form-of-instruction
|
||||
- prerequisites
|
||||
- teaching-material
|
||||
- author-of-indenture
|
||||
- used-in
|
||||
- workload
|
||||
- credits
|
||||
- form-of-exam
|
||||
- term
|
||||
- frequency
|
||||
- duration
|
||||
- kind
|
||||
- remarks
|
||||
|
|
|
@ -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:
|
|
@ -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:
|
|
@ -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:
|
||||
|
|
|
@ -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
231
test/maacs/v2/schema.yaml
Normal 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" }
|
Loading…
Add table
Add a link
Reference in a new issue