Compare commits
1 commit
refactor-f
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
0a5db9f8ad |
20 changed files with 670 additions and 758 deletions
25
README.md
25
README.md
|
@ -5,11 +5,12 @@ to generate multi-lingual curricula documentation tables from
|
||||||
structured representations as a flatfile database. Data scheme and
|
structured representations as a flatfile database. Data scheme and
|
||||||
actual values are kept in YAML files in order to version them with git.
|
actual values are kept in YAML files in order to version them with git.
|
||||||
|
|
||||||
## Usage
|
# Usage
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
usage: [-h] [-m META [META ...]] [-l LANG] [-f FIELDS [FIELDS ...]] [-s SCHEMA] [-q QUERY] [-qs QUERY_SORT] [-qc QUERY_COMPOUND] [-qf QUERY_FILTER [QUERY_FILTER ...]]
|
$> python coursebuilder
|
||||||
[-p] [--title TITLE] [-b BOOK] [--level LEVEL] [--table-gen TABLE_GEN] [--template TEMPLATE] [-o OUT] [--legacy] [--leftcol LEFTCOL]
|
usage: [-h] [-m META [META ...]] [-l LANG] [-f FIELDS [FIELDS ...]] [-s SCHEMA] [-p] [-t] [-b BOOK] [--level LEVEL]
|
||||||
|
[--table-gen TABLE_GEN]
|
||||||
|
|
||||||
versatile curricula generator
|
versatile curricula generator
|
||||||
|
|
||||||
|
@ -22,31 +23,19 @@ options:
|
||||||
Fields to be used, the table will be build accordingly
|
Fields to be used, the table will be build accordingly
|
||||||
-s SCHEMA, --schema SCHEMA
|
-s SCHEMA, --schema SCHEMA
|
||||||
using provided schema
|
using provided schema
|
||||||
-q QUERY, --query QUERY
|
|
||||||
compound query to select items
|
|
||||||
-qs QUERY_SORT, --query-sort QUERY_SORT
|
|
||||||
sort query with a min/max over a column like min:credits
|
|
||||||
-qc QUERY_COMPOUND, --query-compound QUERY_COMPOUND
|
|
||||||
create a compound from a column with multiple values/dictionaries in cells
|
|
||||||
-qf QUERY_FILTER [QUERY_FILTER ...], --query-filter QUERY_FILTER [QUERY_FILTER ...]
|
|
||||||
filter final list of columns for output
|
|
||||||
-p, --pagebreak add a pagebreak after each module
|
-p, --pagebreak add a pagebreak after each module
|
||||||
--title TITLE template for title - use curly brackets (i.e. {}) to mark where the title string is inserted
|
-t, --title take first value in list as title
|
||||||
-b BOOK, --book BOOK process a whole curriculum book with sections
|
-b BOOK, --book BOOK process a whole curriculum book with sections
|
||||||
--level LEVEL level of header tags
|
--level LEVEL level of header tags
|
||||||
--table-gen TABLE_GEN
|
--table-gen TABLE_GEN
|
||||||
runs table generator
|
runs table generator
|
||||||
--template TEMPLATE defines a template to be used with fields
|
|
||||||
-o OUT, --out OUT set the output type
|
|
||||||
--legacy use legacy generator mode for compatibility
|
|
||||||
--leftcol LEFTCOL maximum size of left column
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Author
|
# Author
|
||||||
|
|
||||||
© Copyright 2020-2024 Hartmut Seichter
|
© Copyright 2020-2024 Hartmut Seichter
|
||||||
|
|
||||||
## Licence
|
# Licence
|
||||||
|
|
||||||
Coursebuilder is licensed under the terms of the MIT License. For details consult https://opensource.org/license/mit/ or the attached license file
|
Coursebuilder is licensed under the terms of the MIT License. For details consult https://opensource.org/license/mit/ or the attached license file
|
||||||
|
|
||||||
|
|
3
TODO.md
3
TODO.md
|
@ -6,5 +6,4 @@
|
||||||
* [x] add a book mode for mixing input and headers (# Blah -m mod.cg.yaml)
|
* [x] add a book mode for mixing input and headers (# Blah -m mod.cg.yaml)
|
||||||
* [~] table generator
|
* [~] table generator
|
||||||
* [ ] overlay of compulsory with other modes ...
|
* [ ] overlay of compulsory with other modes ...
|
||||||
* [ ] add template based generator
|
* [ ] add template based generator
|
||||||
* [ ] port over to structured YAML ... https://tolgee.io/platform/formats/structured_yaml
|
|
|
@ -1,186 +1,64 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
"""
|
"""
|
||||||
CourseBuilder
|
CourseBuilder
|
||||||
|
|
||||||
Coursebuilder is a preprocessor tool for [pandoc](https://pandoc.org)
|
Coursebuilder is a preprocessor tool for [pandoc](https://pandoc.org)
|
||||||
to generate multi-lingual curricula documentation tables from
|
to generate multi-lingual curricula documentation tables from
|
||||||
structured representations as a flatfile database. Data scheme and
|
structured representations as a flatfile database. Data scheme and
|
||||||
actual values are kept in YAML files in order to version them with git.
|
actual values are kept in YAML files in order to version them with git.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
import os,sys
|
|
||||||
import yaml
|
import yaml
|
||||||
import pandas as pd
|
import string
|
||||||
from string import Template
|
import os,sys
|
||||||
|
|
||||||
from tablegenerator import TableGenerator
|
from tablegenerator import TableGenerator
|
||||||
from markdowngenerator import MarkdownGenerator
|
from markdowngenerator import MarkdownGenerator
|
||||||
from templategenerator import TemplateGenerator
|
from templategenerator import TemplateGenerator
|
||||||
from schema import Schema
|
from metagenerator import MetaGenerator
|
||||||
|
|
||||||
|
|
||||||
class CourseBuilder:
|
class CourseBuilder:
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def generate(args):
|
|
||||||
if args.schema and args.meta:
|
|
||||||
|
|
||||||
# get actual fields
|
|
||||||
actual_fields = None
|
|
||||||
|
|
||||||
# use a file instead of list
|
|
||||||
if args.fields and os.path.isfile(args.fields[0]):
|
|
||||||
with open(args.fields[0]) as ff:
|
|
||||||
actual_fields = yaml.load(ff,Loader=yaml.Loader)['fields']
|
|
||||||
else:
|
|
||||||
# seem we have a list or None
|
|
||||||
actual_fields = args.fields
|
|
||||||
|
|
||||||
# get schema
|
|
||||||
schema = None
|
|
||||||
with open(args.schema) as f:
|
|
||||||
schema = Schema(yaml.load(f,Loader=yaml.Loader))
|
|
||||||
|
|
||||||
# if no fields are given, take all!
|
|
||||||
if actual_fields == None:
|
|
||||||
actual_fields = list(schema.keys())
|
|
||||||
|
|
||||||
|
|
||||||
result_df = []
|
|
||||||
|
|
||||||
# iterate through meta files
|
|
||||||
for m in args.meta:
|
|
||||||
with open(m) as fm:
|
|
||||||
|
|
||||||
if args.legacy:
|
|
||||||
|
|
||||||
MarkdownGenerator.generate_table_legacy(
|
|
||||||
table_items=schema.to_list_of_tuple(
|
|
||||||
meta=yaml.load(fm,Loader=yaml.Loader),
|
|
||||||
fields=actual_fields,
|
|
||||||
lang=args.lang),
|
|
||||||
add_pagebreak=args.pagebreak,
|
|
||||||
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(
|
|
||||||
table_items=schema.to_list_of_tuple(
|
|
||||||
meta=yaml.load(fm,Loader=yaml.Loader),
|
|
||||||
fields=actual_fields,
|
|
||||||
lang=args.lang),
|
|
||||||
add_pagebreak=args.pagebreak,
|
|
||||||
title_template=args.title,
|
|
||||||
first_colwidth=args.leftcol)
|
|
||||||
|
|
||||||
# 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:
|
|
||||||
# print('{}.sum'.format(args.query_compound))
|
|
||||||
df_q.loc[:,'{}.sum'.format(args.query_compound)] = df_q[args.query_compound].apply(lambda x: sum(list(x.values())))
|
|
||||||
print(df_q)
|
|
||||||
|
|
||||||
# --query-sort is parameterized as min:credits - hence direction:column
|
|
||||||
if args.query_sort:
|
|
||||||
qs = args.query_sort.split(':')
|
|
||||||
match qs[0]:
|
|
||||||
case 'min' : df_q = df_q.sort_values(by=qs[1],ascending=True,key=lambda col: min(col) if hasattr(col,'__len()__') else col)
|
|
||||||
case 'max' : df_q = df_q.sort_values(by=qs[1],ascending=False,key=lambda col: max(col) if hasattr(col,'__len()__') else col)
|
|
||||||
|
|
||||||
# filter query
|
|
||||||
if args.query_filter:
|
|
||||||
df_q = df_q.loc[:,args.query_filter]
|
|
||||||
|
|
||||||
# print(df_q.head())
|
|
||||||
|
|
||||||
# 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}]")?
|
|
||||||
# print(mm.format(v=mm))
|
|
||||||
|
|
||||||
# lets get crazy to create a summary table!
|
|
||||||
# df_summary = pd.DataFrame([{
|
|
||||||
# 'sum.credits': df_q['credits'].sum()
|
|
||||||
# }])
|
|
||||||
|
|
||||||
# set labels directly!
|
|
||||||
if args.query_labels:
|
|
||||||
df_q.columns = args.query_labels
|
|
||||||
|
|
||||||
q_as_md = df_q.to_markdown(tablefmt='grid',index=False)
|
|
||||||
|
|
||||||
print(q_as_md)
|
|
||||||
|
|
||||||
# print(df_summary.to_markdown(tablefmt='grid',index=False))
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def run():
|
def run():
|
||||||
|
|
||||||
# arguments
|
# arguments
|
||||||
parser = ArgumentParser(description='versatile curricula generator')
|
parser = ArgumentParser(description='versatile curricula generator')
|
||||||
|
|
||||||
# loading mode for internal database
|
|
||||||
parser.add_argument('-m','--meta',action="extend", nargs="+", type=str,help="course description(s) as YAML file(s)")
|
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('-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('-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('-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")
|
|
||||||
parser.add_argument('-qc','--query-compound',type=str,default=None,help="create a compound from a column with multiple values/dictionaries in cells")
|
|
||||||
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('-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")
|
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('-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('--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('--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')
|
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
|
# get arguments
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.table_gen:
|
if args.table_gen:
|
||||||
|
|
||||||
tg = TableGenerator()
|
tg = TableGenerator()
|
||||||
|
|
||||||
tg.generate_table(args.table_gen)
|
tg.generate_table(args.table_gen)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
# book mode with predefined setting from a book file
|
# book mode with predefined setting from a book file
|
||||||
if args.book and args.schema:
|
if args.book and args.schema:
|
||||||
|
|
||||||
|
generator = MetaGenerator()
|
||||||
|
|
||||||
|
with open(args.schema) as sf:
|
||||||
|
generator.set_schema(yaml.load(sf,Loader=yaml.Loader))
|
||||||
|
|
||||||
with open(args.book) as bf:
|
with open(args.book) as bf:
|
||||||
|
|
||||||
|
@ -190,36 +68,63 @@ class CourseBuilder:
|
||||||
book_path = os.path.abspath(args.book)
|
book_path = os.path.abspath(args.book)
|
||||||
|
|
||||||
for bi in book['book']:
|
for bi in book['book']:
|
||||||
|
|
||||||
if 'fields' in bi:
|
if 'fields' in bi:
|
||||||
actual_fields = bi['fields']
|
actual_fields = bi['fields']
|
||||||
|
|
||||||
if 'sections' in bi:
|
if 'sections' in bi:
|
||||||
for section in bi['sections']:
|
for section in bi['sections']:
|
||||||
|
|
||||||
if 'text' in section:
|
if 'text' in section:
|
||||||
print(section['text'][args.lang])
|
print(section['text'][args.lang])
|
||||||
|
|
||||||
# gernerate section wise parts
|
|
||||||
if 'modules' in section:
|
if 'modules' in section:
|
||||||
|
for m in section['modules']:
|
||||||
|
mod_path = os.path.join(os.path.dirname(book_path),m)
|
||||||
|
|
||||||
# override fields
|
with open(mod_path) as fm:
|
||||||
args.fields = actual_fields
|
try:
|
||||||
|
|
||||||
# expand filenames to be relative to the book
|
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)
|
||||||
args.meta = [os.path.join(os.path.dirname(book_path),mod_path) for mod_path in section['modules']]
|
|
||||||
|
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)
|
||||||
|
|
||||||
CourseBuilder.generate(args=args)
|
|
||||||
|
|
||||||
# verbose command line mode
|
# verbose command line mode
|
||||||
elif args.schema:
|
elif args.schema and args.meta and len(args.fields) > 0:
|
||||||
CourseBuilder.generate(args=args)
|
|
||||||
|
# get actual fields
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
# get schema
|
||||||
|
actual_schema = None
|
||||||
|
with open(args.schema) as f:
|
||||||
|
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)
|
||||||
|
|
||||||
|
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:
|
else:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
|
|
||||||
# run as main
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# recommended setting for pandas
|
|
||||||
pd.options.mode.copy_on_write = True
|
|
||||||
# run
|
|
||||||
CourseBuilder.run()
|
CourseBuilder.run()
|
||||||
|
|
|
@ -1,44 +1,25 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import itertools
|
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:
|
class MarkdownGenerator:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def generate_tablerow() -> str:
|
def generate_tablerow() -> str:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def generate_table(table_items,add_pagebreak = False,title_template = None,first_colwidth = 28):
|
|
||||||
|
|
||||||
import pandas as pd
|
|
||||||
import tabulate
|
|
||||||
|
|
||||||
# get the dataframe
|
|
||||||
df = pd.DataFrame(table_items)
|
|
||||||
|
|
||||||
# use first column for
|
|
||||||
df.columns = df.iloc[0]
|
|
||||||
df = df[1:]
|
|
||||||
|
|
||||||
if title_template != None:
|
|
||||||
print(title_template.format(df.columns[1]),'\n')
|
|
||||||
|
|
||||||
print(df.to_markdown(tablefmt='grid', index=False, maxcolwidths=[first_colwidth,None]))
|
|
||||||
print('\n') # always add a newline after the table
|
|
||||||
|
|
||||||
if add_pagebreak:
|
|
||||||
print('\\pagebreak')
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def generate_table_legacy(table_items,add_pagebreak = False,title_template = None,first_colwidth = 28):
|
def generate(ti,pagebreak = False,title = False,header_level = 1) -> str:
|
||||||
|
|
||||||
import textwrap
|
|
||||||
|
|
||||||
line_length = 128
|
line_length = 128
|
||||||
column_ratio = float(first_colwidth) / 100
|
column_ratio= 0.28
|
||||||
|
|
||||||
h_len = int(line_length * column_ratio)
|
h_len = int(line_length * column_ratio)
|
||||||
d_len = line_length-h_len
|
d_len = line_length-h_len
|
||||||
|
@ -46,8 +27,8 @@ class MarkdownGenerator:
|
||||||
#
|
#
|
||||||
# generate title (currently the first one)
|
# generate title (currently the first one)
|
||||||
#
|
#
|
||||||
if title_template != None:
|
if title:
|
||||||
print(title_template.format(table_items[0][1]),'\n')
|
print('#' * header_level,ti[0][1],'\n')
|
||||||
|
|
||||||
print(''.join(['+',"".ljust(h_len,'-'),'+',"".ljust(d_len,'-'),'+']))
|
print(''.join(['+',"".ljust(h_len,'-'),'+',"".ljust(d_len,'-'),'+']))
|
||||||
|
|
||||||
|
@ -59,7 +40,7 @@ class MarkdownGenerator:
|
||||||
|
|
||||||
# test if this affected by a third item!
|
# test if this affected by a third item!
|
||||||
|
|
||||||
for k,v in table_items:
|
for k,v in ti:
|
||||||
|
|
||||||
#
|
#
|
||||||
if v == None:
|
if v == None:
|
||||||
|
@ -98,5 +79,5 @@ class MarkdownGenerator:
|
||||||
|
|
||||||
|
|
||||||
# to control pagebreaks for pandoc
|
# to control pagebreaks for pandoc
|
||||||
if add_pagebreak:
|
if pagebreak:
|
||||||
print('\n\\newpage')
|
print('\n\\newpage')
|
155
coursebuilder/metagenerator.py
Normal file
155
coursebuilder/metagenerator.py
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
import os,string,sys
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
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 = []
|
||||||
|
|
||||||
|
# iterate over requested fields
|
||||||
|
for field in fields:
|
||||||
|
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,' not resolvable in ',self.__schema,exp)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# book mode
|
||||||
|
# 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:
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
import string
|
|
||||||
|
|
||||||
class Schema:
|
|
||||||
|
|
||||||
def __init__(self,schema) -> None:
|
|
||||||
self.__schema = schema
|
|
||||||
|
|
||||||
def __getitem__(self, field):
|
|
||||||
return self.__schema[field]
|
|
||||||
|
|
||||||
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]:
|
|
||||||
return self.__schema[field]
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_value(self,meta,field,lang):
|
|
||||||
"""
|
|
||||||
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):
|
|
||||||
"""
|
|
||||||
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_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_tuple(self,meta,fields,lang):
|
|
||||||
"""
|
|
||||||
generates a list of tuples with a label and value (text)
|
|
||||||
this is usually consumed by a Markdown generator
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
|
@ -4,12 +4,10 @@ import string
|
||||||
import tempfile
|
import tempfile
|
||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
import pandas as pd
|
|
||||||
import tabulate
|
|
||||||
|
|
||||||
class TableGenerator:
|
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:
|
def __init__(self) -> None:
|
||||||
|
@ -24,7 +22,7 @@ class TableGenerator:
|
||||||
|
|
||||||
for token in data.split(','):
|
for token in data.split(','):
|
||||||
|
|
||||||
t = tuple(token.split(':')[:2])
|
t = tuple(token.split(':')[:2])
|
||||||
row = [t[0]]
|
row = [t[0]]
|
||||||
for k in list(self.__cols_map[lang].keys())[1:]:
|
for k in list(self.__cols_map[lang].keys())[1:]:
|
||||||
if k in t[1]:
|
if k in t[1]:
|
||||||
|
@ -35,10 +33,10 @@ class TableGenerator:
|
||||||
rows.append(' & '.join(row) + '\\\\')
|
rows.append(' & '.join(row) + '\\\\')
|
||||||
|
|
||||||
self.run_template(rows=rows)
|
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())
|
t = string.Template(self.get_latex_template())
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile('w',delete=False,prefix='cb-') as fp:
|
with tempfile.NamedTemporaryFile('w',delete=False,prefix='cb-') as fp:
|
||||||
|
@ -47,7 +45,7 @@ class TableGenerator:
|
||||||
subprocess.run(["latex",fp.name])
|
subprocess.run(["latex",fp.name])
|
||||||
subprocess.run(["dvisvgm",os.path.basename(fp.name) + '.dvi'])
|
subprocess.run(["dvisvgm",os.path.basename(fp.name) + '.dvi'])
|
||||||
# subprocess.run(["mv",os.path.basename(fp.name) + '.svg','.'])
|
# subprocess.run(["mv",os.path.basename(fp.name) + '.svg','.'])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_latex_template(self,lang = 'de') -> str:
|
def get_latex_template(self,lang = 'de') -> str:
|
||||||
|
@ -65,7 +63,7 @@ class TableGenerator:
|
||||||
r'\begin{table}[ht]' +
|
r'\begin{table}[ht]' +
|
||||||
'\\begin{{tabular}} {{ {0} }}'.format(' '.join(layout)) +
|
'\\begin{{tabular}} {{ {0} }}'.format(' '.join(layout)) +
|
||||||
r'\hline'
|
r'\hline'
|
||||||
r' ${th}'
|
r' ${th}'
|
||||||
r'\hline'
|
r'\hline'
|
||||||
r' ${td}' +
|
r' ${td}' +
|
||||||
r'\hline'
|
r'\hline'
|
||||||
|
@ -73,7 +71,7 @@ class TableGenerator:
|
||||||
r'\end{table}'
|
r'\end{table}'
|
||||||
r'\end{document}')
|
r'\end{document}')
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Kompetenz & Kennen & Wertung \\
|
# Kompetenz & Kennen & Wertung \\
|
||||||
|
|
||||||
|
@ -81,4 +79,4 @@ class TableGenerator:
|
||||||
# 2 & Latex & ++ \\
|
# 2 & Latex & ++ \\
|
||||||
# 3 & Writer & +- \\
|
# 3 & Writer & +- \\
|
||||||
#
|
#
|
||||||
# latex image.tex;dvisvgm image.dvi
|
# latex image.tex;dvisvgm image.dvi
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
# 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 following pieces play together here:
|
|
||||||
|
|
||||||
- `schema` files, usually a `schema.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
|
|
||||||
|
|
||||||
# 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
|
|
||||||
- `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
|
|
||||||
|
|
||||||
# mod files (modules)
|
|
||||||
|
|
||||||
Modules describe a course in detail and implement an instance of the schema file. Especially `strings` and `enums` are translatable One of the plan is to use a validator to find inconsistencies automatically, like workloads that are not following the 30h = 1ECTS rule.
|
|
||||||
|
|
||||||
|
|
||||||
# datatypes
|
|
||||||
|
|
||||||
## `str` datatype
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# this would reside in a schema field on top level
|
|
||||||
# a field of name 'id'
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
```
|
|
|
@ -1,7 +0,0 @@
|
||||||
numpy==1.26.4
|
|
||||||
pandas==2.2.2
|
|
||||||
python-dateutil==2.9.0.post0
|
|
||||||
pytz==2024.1
|
|
||||||
six==1.16.0
|
|
||||||
tabulate==0.9.0
|
|
||||||
tzdata==2024.1
|
|
|
@ -1,44 +0,0 @@
|
||||||
# debug make file for testing
|
|
||||||
build_dir := build
|
|
||||||
target_en := ${build_dir}/table.en.pdf
|
|
||||||
target_de := ${build_dir}/table.de.pdf
|
|
||||||
target_de_book := ${build_dir}/curricullum.de.pdf
|
|
||||||
|
|
||||||
targets := ${target_de} ${target_en} ${target_de_book}
|
|
||||||
|
|
||||||
target_flags := --template pandoc-template/eisvogel.latex -V table-use-row-colors:true
|
|
||||||
|
|
||||||
coursebuilder := ../coursebuilder
|
|
||||||
|
|
||||||
all: ${targets}
|
|
||||||
|
|
||||||
${target_en}: mod.cg.yaml
|
|
||||||
@echo "creating English version ..."
|
|
||||||
mkdir -p ${build_dir}
|
|
||||||
python ${coursebuilder} -s schema.yaml -m $^ -l en -f fields.yaml | pandoc ${target_flags} -o ${target_en}
|
|
||||||
|
|
||||||
${target_de}: mod.cg.yaml
|
|
||||||
@echo "creating German version ..."
|
|
||||||
mkdir -p ${build_dir}
|
|
||||||
python ${coursebuilder} -s schema.yaml -m $^ -l de -f fields.yaml | pandoc ${target_flags} -o ${target_de}
|
|
||||||
|
|
||||||
${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:
|
|
||||||
rm -f ${targets}
|
|
||||||
|
|
||||||
debug:
|
|
||||||
python ${coursebuilder} -s schema.yaml -m mod.cg.yaml mod.interactsys.yaml mod.test.yaml -p --title "## {}" -l de -f name credits goal content
|
|
||||||
# | pandoc ${target_flags} -V lang:de -o ${target_de}
|
|
||||||
|
|
||||||
debug-query:
|
|
||||||
python ${coursebuilder} -s schema.yaml -m mod.cg.yaml mod.interactsys.yaml -q "kind=='compulsory'" -qs min:credits -qc form-of-instruction -qf name credits form-of-exam -ql Modulname Kreditpunkte Prüfungsart -qt quatsch
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
debug-query-full:
|
|
||||||
python ${coursebuilder} -s ~/Documents/MaACS/MHB/schema.yaml -b ~/Documents/MaACS/MHB/book.yaml -q "kind=='compulsory_elective'" -qc form-of-instruction -qf name form-of-instruction.sum credits term -ql Modulname SWS Kreditpunkte Semester
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
|
@ -1,106 +0,0 @@
|
||||||
name:
|
|
||||||
de: Test Vorlesung
|
|
||||||
en: Lecture of Test
|
|
||||||
|
|
||||||
instructor:
|
|
||||||
de: Cicero
|
|
||||||
en: Cicero
|
|
||||||
|
|
||||||
id:
|
|
||||||
value: Test
|
|
||||||
|
|
||||||
credits:
|
|
||||||
value: 5
|
|
||||||
|
|
||||||
form-of-exam:
|
|
||||||
value: written
|
|
||||||
|
|
||||||
form-of-instruction:
|
|
||||||
value: { 'lecture': 2, 'exersise': 1 }
|
|
||||||
|
|
||||||
term:
|
|
||||||
value: [1, 3]
|
|
||||||
|
|
||||||
duration:
|
|
||||||
value: 1
|
|
||||||
|
|
||||||
kind:
|
|
||||||
value: compulsory
|
|
||||||
|
|
||||||
goal:
|
|
||||||
de: |
|
|
||||||
**What is it**
|
|
||||||
|
|
||||||
Lorem Ipsum is simply dummy text of the printing and typesetting
|
|
||||||
industry. Lorem Ipsum has been the industry's standard dummy text
|
|
||||||
ever since the 1500s, when an unknown printer took a galley of type
|
|
||||||
and scrambled it to make a type specimen book. It has survived not only
|
|
||||||
five centuries, but also the leap into electronic typesetting, remaining
|
|
||||||
essentially unchanged. It was popularised in the 1960s with the release
|
|
||||||
of Letraset sheets containing Lorem Ipsum passages, and more recently with
|
|
||||||
desktop publishing software like Aldus PageMaker including versions of
|
|
||||||
Lorem Ipsum.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
en: |
|
|
||||||
|
|
||||||
|
|
||||||
content:
|
|
||||||
de: |
|
|
||||||
**Where did it come from**
|
|
||||||
|
|
||||||
Contrary to popular belief, Lorem Ipsum is not simply random text.
|
|
||||||
It has roots in a piece of classical Latin literature from 45 BC,
|
|
||||||
making it over 2000 years old. Richard McClintock, a Latin professor
|
|
||||||
at Hampden-Sydney College in Virginia, looked up one of the more
|
|
||||||
obscure Latin words, consectetur, from a Lorem Ipsum passage, and
|
|
||||||
going through the cites of the word in classical literature,
|
|
||||||
discovered the undoubtable source. Lorem Ipsum comes from sections
|
|
||||||
1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The
|
|
||||||
Extremes of Good and Evil) by Cicero, written in 45 BC. This book
|
|
||||||
is a treatise on the theory of ethics, very popular during the
|
|
||||||
Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor
|
|
||||||
sit amet..", comes from a line in section 1.10.32.
|
|
||||||
|
|
||||||
|
|
||||||
en: |
|
|
||||||
|
|
||||||
|
|
||||||
teaching-material:
|
|
||||||
de: |
|
|
||||||
|
|
||||||
en: |
|
|
||||||
|
|
||||||
|
|
||||||
prerequisites:
|
|
||||||
de: ""
|
|
||||||
en: ""
|
|
||||||
|
|
||||||
author-of-indenture:
|
|
||||||
de: ""
|
|
||||||
en: ""
|
|
||||||
|
|
||||||
used-in:
|
|
||||||
de: "Master Applied Computerscience"
|
|
||||||
en: "Master Applied Computerscience"
|
|
||||||
|
|
||||||
workload:
|
|
||||||
de: "2SWS Vorlesung 1SWS Übung"
|
|
||||||
en: "2SWS lecture 1SWS exersise"
|
|
||||||
|
|
||||||
form-of-exam:
|
|
||||||
value: written
|
|
||||||
spec:
|
|
||||||
de: "120min Klausur"
|
|
||||||
en: "120min exam"
|
|
||||||
|
|
||||||
frequency:
|
|
||||||
value: once_per_year
|
|
||||||
|
|
||||||
kind:
|
|
||||||
value: compulsory
|
|
||||||
|
|
||||||
remarks:
|
|
||||||
de:
|
|
||||||
en:
|
|
228
test/schema.yaml
228
test/schema.yaml
|
@ -1,228 +0,0 @@
|
||||||
# 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" }
|
|
30
test/simple/Makefile
Normal file
30
test/simple/Makefile
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
all: table.en.pdf table.de.pdf
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
table:
|
||||||
|
python ${coursebuilder} -s schema.yaml -b book.yaml -l de -f fields.yaml
|
|
@ -4,9 +4,8 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
book:
|
book:
|
||||||
- fields:
|
- fields:
|
||||||
- name
|
- name
|
||||||
- instructor
|
|
||||||
- id
|
- id
|
||||||
- goal
|
- goal
|
||||||
- content
|
- content
|
||||||
|
@ -34,21 +33,5 @@ book:
|
||||||
en: "## elective courses {.unnumbered}"
|
en: "## elective courses {.unnumbered}"
|
||||||
- modules:
|
- modules:
|
||||||
- mod.interactsys.yaml
|
- mod.interactsys.yaml
|
||||||
- mod.test.yaml
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# tables
|
|
||||||
#
|
|
||||||
query:
|
|
||||||
list-credits-and-workload:
|
|
||||||
- fields:
|
|
||||||
- name
|
|
||||||
- credits
|
|
||||||
- workload
|
|
||||||
|
|
||||||
# just for ideas
|
|
||||||
regulations:
|
|
||||||
- globals:
|
|
||||||
- course_name: Applied Computer Science (M.Sc.)
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
fields:
|
fields:
|
||||||
- name
|
- name
|
||||||
- instructor
|
|
||||||
- id
|
- id
|
||||||
- goal
|
- goal
|
||||||
- content
|
- content
|
|
@ -2,9 +2,6 @@ name:
|
||||||
de: Computergrafik
|
de: Computergrafik
|
||||||
en: Computer Graphics
|
en: Computer Graphics
|
||||||
|
|
||||||
instructor:
|
|
||||||
de: Prof. Hartmut Seichter, PhD
|
|
||||||
en: Prof. Hartmut Seichter, PhD
|
|
||||||
|
|
||||||
id:
|
id:
|
||||||
value: CG
|
value: CG
|
98
test/simple/mod.test.yaml
Normal file
98
test/simple/mod.test.yaml
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
|
||||||
|
name:
|
||||||
|
en: Test Course
|
||||||
|
|
||||||
|
|
||||||
|
test:
|
||||||
|
|
||||||
|
competency-table:
|
||||||
|
de:
|
||||||
|
- "Lineare Algebra": 'ABC'
|
||||||
|
- "Vector Spaces": 'A'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# nested lists seem to work in Markdown only in the US style way
|
||||||
|
#
|
||||||
|
# reference here: https://meta.stackexchange.com/questions/85474/how-to-write-nested-numbered-lists
|
||||||
|
#
|
||||||
|
# note the parser actually corrects 'Tervuren' to 3 in resulting data
|
||||||
|
#
|
||||||
|
|
||||||
|
content:
|
||||||
|
en: |
|
||||||
|
1. Blah
|
||||||
|
|
||||||
|
2. Blub
|
||||||
|
|
||||||
|
1. Blah
|
||||||
|
|
||||||
|
1. Blub
|
||||||
|
|
||||||
|
1. Blah
|
||||||
|
|
||||||
|
1. Blub
|
||||||
|
|
||||||
|
1. Blah
|
||||||
|
|
||||||
|
1. Blub
|
||||||
|
|
||||||
|
1. Blah
|
||||||
|
|
||||||
|
1. Blub
|
||||||
|
|
||||||
|
3. Blah
|
||||||
|
|
||||||
|
4. Blub
|
||||||
|
|
||||||
|
<!-- break -->
|
||||||
|
|
||||||
|
5. Blah
|
||||||
|
|
||||||
|
6. Blah and Blub
|
||||||
|
|
||||||
|
1. Blah
|
||||||
|
|
||||||
|
1. Blub
|
||||||
|
|
||||||
|
7. Blah and Blub
|
||||||
|
|
||||||
|
- Blah
|
||||||
|
|
||||||
|
- Blub
|
||||||
|
|
||||||
|
- Blah
|
||||||
|
|
||||||
|
- Blub
|
||||||
|
|
||||||
|
8. Blub and Blah
|
||||||
|
|
||||||
|
- Blah
|
||||||
|
|
||||||
|
- Blub
|
||||||
|
|
||||||
|
- Blah
|
||||||
|
|
||||||
|
- Blub
|
||||||
|
|
||||||
|
- Blah
|
||||||
|
|
||||||
|
- Blub
|
||||||
|
|
||||||
|
- Blah
|
||||||
|
|
||||||
|
- Blub
|
||||||
|
|
||||||
|
9. Blah, Blub and Blub
|
||||||
|
|
||||||
|
- Blah
|
||||||
|
|
||||||
|
- Blub
|
||||||
|
|
||||||
|
- Blah
|
||||||
|
|
||||||
|
- Blub
|
||||||
|
|
||||||
|
|
||||||
|
|
291
test/simple/schema.yaml
Normal file
291
test/simple/schema.yaml
Normal file
|
@ -0,0 +1,291 @@
|
||||||
|
# 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
|
||||||
|
translatable: false
|
||||||
|
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:
|
||||||
|
label: {
|
||||||
|
de: "Lehrform(en)",
|
||||||
|
en: "form of instruction"
|
||||||
|
}
|
||||||
|
type: multikey
|
||||||
|
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
|
||||||
|
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:
|
||||||
|
label: {
|
||||||
|
de: "Leistungsnachweis",
|
||||||
|
en: "form of examination"
|
||||||
|
}
|
||||||
|
type: enum
|
||||||
|
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:
|
||||||
|
label: {
|
||||||
|
de: "Semester",
|
||||||
|
en: "term"
|
||||||
|
}
|
||||||
|
type: multinum
|
||||||
|
template:
|
||||||
|
de: " ${value}. Semester"
|
||||||
|
en: " ${value}. semester"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Häufigkeit des Angebots
|
||||||
|
#
|
||||||
|
frequency:
|
||||||
|
label: {
|
||||||
|
de: "Häufigkeit des Angebots",
|
||||||
|
en: "frequency of Offer"
|
||||||
|
}
|
||||||
|
type: "enum"
|
||||||
|
values: {
|
||||||
|
'once_per_term' : {
|
||||||
|
de: "jedes Semester",
|
||||||
|
en: "every term"
|
||||||
|
},
|
||||||
|
'once_per_year' : {
|
||||||
|
de: "einmal im Studienjahr",
|
||||||
|
en: "once per study year"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
duration:
|
||||||
|
type: int
|
||||||
|
label:
|
||||||
|
de: Dauer
|
||||||
|
en: duration
|
||||||
|
template:
|
||||||
|
de: "$value Semester"
|
||||||
|
en: "$value term(s)"
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remarks:
|
||||||
|
type: str
|
||||||
|
label: {
|
||||||
|
de: "Besonderes",
|
||||||
|
en: "remarks"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue