Skip to content
Snippets Groups Projects
Commit ee8c3875 authored by Jan Maximilian Michal's avatar Jan Maximilian Michal
Browse files

updated readme, added comments, allow multiple scripts on command line

parent fb9cddf4
No related branches found
No related tags found
No related merge requests found
......@@ -6,7 +6,7 @@ Invoke the script with `python3 grim.py -h` in order to see usage. Currently
many features are not yet implemented. Single and Multiple Chice questions
can be generated also in parametrized form.
Example scripts can be found in `scripts/`.
Example scripts can be found in `scripts/examples/`.
A first version of the tool that relied on autoilias is still in the repository
(see `generator.py`). It will be removed as soon as the necessary features are
......@@ -15,16 +15,17 @@ implemented.
### Dependencies
`pip install mistune`
`pip install pygments`
### TODO
* Add a good description / documentation.
* Add more functionality (gap, alignment, etc.)
* Add more functionality (finalize gap, alignment)
* Make parsers more robust.
* reverse ILIAS authentication mechanism for automated upload.
* Create whole test object with questions for direct import. Create two
versions (one for internal use and one for the test.)
* move moints from meta to parser
* add zip support
### Notes
......@@ -40,5 +41,5 @@ data and assumes unknown properties)
### LaTeX Support
Hallgrim supports the native latex approach by ILIAS. To typeset a formula just
out in backets like this `[[\\sum_{i=1}^n i = \\frac{n(n+1)}{2}]]`. Special
out in backets like this `[[\\suam_{i=1}^n i = \\frac{n(n+1)}{2}]]`. Special
carecters (mostly `\`) have to be escaped unless you use raw strings (`r'a raw string'`).
#!/usr/local/bin/python3
################################################################################
#
# This script contains the main part of hallgrim and ist the only script that
# needs to ne invoked. The steps it takes to generate a task are as follows:
#
# * parse the commandline arguments with argparse
# * for each script determine the type and validate correct syntax
# * delegate the script to a handler for the specific type
# * the handler parses the script into the intermediate represenatation (mostly
# arrays)
# * the handler passes the needed information to the script generator, which
# will ptint the final xml file.
# * a finisher compresses data if needed (needs to be implemented, maybe as s
# eperate subparser).
#
################################################################################
import importlib
import argparse
import os
......@@ -10,6 +27,19 @@ from hallgrim.IliasXMLCreator import packer
from hallgrim.messages import *
from hallgrim.parser import *
scaffolding = r'''
meta = {{
'author': '{}',
'title': '{}',
'type': '{}',
'points': {},
}}
task = """ decription """
{}
feedback = """ decription """
'''
def file_to_module(name):
return name.rstrip('.py').replace('/', '.')
......@@ -69,6 +99,7 @@ def parseme():
"--points",
help='Points given for correct answer (different behavior for different questions)',
type=float,
default=0.0,
metavar='POINTS',
)
......@@ -83,6 +114,7 @@ def parseme():
parser_gen.add_argument(
'input',
help='Script to execute',
nargs='+',
type=file_exists,
metavar='FILE')
parser_gen.add_argument(
......@@ -103,15 +135,16 @@ def parseme():
parser.print_help()
def delegator(output, script_name, instances):
script = importlib.import_module(file_to_module(script_name))
handler = {
'gap': handle_gap_questions,
'single choice': handle_choice_questions,
'multiple choice': handle_choice_questions
}[script.meta['type']]
def delegator(output, script_list, instances):
for script_name in filter(lambda a: a.endswith('.py'), script_list):
script = importlib.import_module(file_to_module(script_name))
handler = {
'gap': handle_gap_questions,
'single choice': handle_choice_questions,
'multiple choice': handle_choice_questions
}[script.meta['type']]
handler(output, script, instances)
handler(output, script, instances)
def handle_gap_questions(output, script, instances):
......@@ -151,29 +184,18 @@ def handle_choice_questions(output, script, instances):
output = os.path.join(
'output', script.meta['title']) + '.xml' if not output else output
packer.convert_and_print(data, output, instances)
info('Processed "{}" and wrote xml to "{}".'.format(
script.__name__, output))
info('Processed "{}" and'.format(script.__name__))
info('wrote xml "{}"'.format(output), notag=True)
def handle_new_script(name, qtype, author, points):
with open('scripts/' + name + '.py', 'x') as new_script:
with open('scripts/' + name + '.py', 'w') as new_script:
choice = ''
if qtype in ['multi', 'single']:
choice = '\nchoices = """\n[X] A\n[ ] B\n[ ] C\n[X] D\n"""\n'
scaffolding = '''
meta = {{
'author': '{}',
'title': '{}',
'type': '{}',
'points': {}, # per correct choice
}}
task = """ decription """
{}
feedback = """ decription """
'''.format(author, name, qtype, points, choice).strip()
print(scaffolding, file=new_script)
print(scaffolding.format(
author, name, qtype, points, choice).strip(), file=new_script)
info('Generated new script "{}."'.format(new_script.name))
if __name__ == '__main__':
......
import re
try:
from mistune import Renderer, InlineLexer, Markdown, escape
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import HtmlFormatter
except ImportError as err:
print("Please install mistune to make use of markdown parsing.")
print("\t pip install mistune")
no_copy = "-webkit-touch-callout: none; \
-webkit-user-select: none; \
-khtml-user- select: none; \
-moz-user-select: none;\
-ms-user-select: none;\
user-select: none;"
def box(content, color):
return '<div style="background-color: #ffedc9; border: 1px solid {}; \
padding: 10px; font-size: smaller;">{}</div>'.format(color, content)
def yellow_box(content):
return box(content, '#FFB12E')
def blue_box(content):
return box(content, '#9999ff')
def markdown(value):
renderer = HighlightRenderer()
markdown = Markdown(renderer=renderer)
return markdown(value)
class LaTeXRenderer(Renderer):
def latex(self, formula):
return '<span class="latex">{}</span>'.format(formula)
def block_code(self, code, lang):
if not lang:
return '\n<pre><code>%s</code></pre>\n' % \
escape(code)
lexer = get_lexer_by_name(lang, stripall=True)
formatter = HtmlFormatter(noclasses=True, cssstyles=no_copy)
return highlight(code, lexer, formatter)
class LaTeXInlineLexer(InlineLexer):
""" Classes are inspired by the lexer example in the mistune readme """
def enable_latex(self):
# add latex rules
self.rules.latex = re.compile(
r'\[\[' # [[
r'([^\]]+)' # formula
r'\]\](?!\])' # ]]
)
self.default_rules.insert(3, 'latex')
def output_latex(self, m):
formula = m.group(1).replace('\n', ' ')
return self.renderer.latex(formula)
def get_custom_markdown():
renderer = LaTeXRenderer()
inline = LaTeXInlineLexer(renderer)
# enable the feature
inline.enable_latex()
return Markdown(renderer, inline=inline)
markdown = get_custom_markdown()
......@@ -6,8 +6,8 @@ def warn(msg):
def debug(msg):
print('[DEBUG]', msg)
def info(msg):
print('[INFO]', msg)
def info(msg, notag=False):
print('[INFO]' if not notag else ' ', msg)
def error(msg):
print('[ERROR]', msg)
......
import re
try:
from mistune import Renderer, InlineLexer, Markdown, escape
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import HtmlFormatter
except ImportError as err:
print("Please install mistune to make use of markdown parsing.")
print("\t pip install mistune")
## TODO get lexer elsewhere
no_copy = "-webkit-touch-callout: none; \
-webkit-user-select: none; \
-khtml-user- select: none; \
-moz-user-select: none;\
-ms-user-select: none;\
user-select: none;"
def box(content, color):
return '<div style="background-color: #ffedc9; border: 1px solid {}; \
padding: 10px; font-size: smaller;">{}</div>'.format(color, content)
def yellow_box(content):
return box(content, '#FFB12E')
def blue_box(content):
return box(content, '#9999ff')
def markdown(value):
renderer = HighlightRenderer()
markdown = Markdown(renderer=renderer)
return markdown(value)
class LaTeXRenderer(Renderer):
def latex(self, formula):
return '<span class="latex">{}</span>'.format(formula)
def block_code(self, code, lang):
if not lang:
return '\n<pre><code>%s</code></pre>\n' % \
escape(code)
lexer = get_lexer_by_name(lang, stripall=True)
formatter = HtmlFormatter(noclasses=True, cssstyles=no_copy)
return highlight(code, lexer, formatter)
class LaTeXInlineLexer(InlineLexer):
""" Classes are inspired by the lexer example in the mistune readme """
def enable_latex(self):
# add latex rules
self.rules.latex = re.compile(
r'\[\[' # [[
r'([^\]]+)' # formula
r'\]\](?!\])' # ]]
)
self.default_rules.insert(3, 'latex')
def output_latex(self, m):
formula = m.group(1).replace('\n', ' ')
return self.renderer.latex(formula)
def get_custom_markdown():
renderer = LaTeXRenderer()
inline = LaTeXInlineLexer(renderer)
# enable the feature
inline.enable_latex()
return Markdown(renderer, inline=inline)
markdown = get_custom_markdown()
from hallgrim.custom_markdown import markdown
def choice_parser(raw_choices, points):
""" Parse the multiple choice answers and form an array that has the
......@@ -94,13 +13,13 @@ def choice_parser(raw_choices, points):
lines = raw_choices.strip().split('\n')
elif type(raw_choices) is list:
lines = raw_choices
regex = re.compile('\[(\d|X| )\]\s+([\w\W]+)', re.MULTILINE)
regex = re.compile('\[(([0-9]*[.])?[0-9]+|X| )\]\s+([\w\W]+)', re.MULTILINE)
parse = [re.match(regex, line).groups() for line in lines]
final = [(
markdown(text),
True if mark != ' ' else False,
float(mark) if mark not in ' X' else points)
for mark, text in parse]
for mark, _, text in parse]
return final
def gap_parser(task):
......
File moved
File moved
meta = {
'author': 'Jan Maximilian Michal',
'title': 'Zeilen sind anders, Spalten auch (I1-ID: lhu27691fzg0)',
'type': 'gap',
}
gap_1 = """[select]
[1] int n_ze = m.length;
[ ] int n_ze = m[0].length;
[ ] int n_ze = m.length();
[ ] int n_ze = m[0].length();
[/select]"""
gap_2 = """[select]
[ ] int n_sp = m.length();
[ ] int n_sp = m[0].length();
[ ] int n_sp = m.length;
[1] int n_sp = m[0].length;
[/select]"""
gap_3 = """[select]
[ ] for (int i = 1; i <= n_ze; i++)
[1] for (int i = 0; i < n_ze; i++)
[ ] for (int i = 1; i <= n_sp; i++)
[ ] for (int i = 0; i < n_sp; i++)
[/select]"""
gap_4 = """[select]
[ ] ms[i] = ms[i] + m[j][i]*s[i];
[ ] ms[i] = ms[i] + m[i][j]*s[i];
[ ] ms[i] = ms[i] + m[j][i]*s[j];
[1] ms[i] = ms[i] + m[i][j]*s[j];
[/select]"""
task = """ Folgendes Codefragment soll die Multiplikation eines Zeilenvektors mit einer Matrix sowie einer Matrix mit einem Spaltenvektor realisieren. Wählen Sie die fehlenden Zeilen unten entsprechend aus:
```java
01: int[][] m = {{ 0, 1, 2, 3}, // Matrix
02: { 4, 5, 6, 7},
03: {8, 9, 10, 11}};
04: int[] z = { 12, 13, 14}; // Zeile
05: int[] s = {15, 16, 17, 18}; // Spalte
06:
07: /* CODEZEILE AUSWÄHLEN */ // Zeilenanzahl der Matrix
08: /* CODEZEILE AUSWÄHLEN */ // Spaltenanzahl der Matrix
09:
10: /* 1: Zeile mal Matrix */011: int[] zm = new int[n_sp];
12: for (int j = 0; j < n_sp; j++) {
13: zm[j] = 0;
14: /* CODEZEILE AUSWÄHLEN */
15: zm[j] = zm[j] + z[i]*m[i][j];
16: }
17: /* 2: Matrix mal Spalte */
18: int[] ms = new int[n_ze];
19: for (int i = 0; i < n_ze; i++) {
20: ms[i] = 0;
21: for (int j = 0; j < n_sp; j++)
22: /* CODEZEILE AUSWÄHLEN */
23:
```
**Zeile 7:**
%s
**Zeile 8:**
%s
**Zeile 14:**
%s
**Zeile 22:**
%s
""" % (gap_1, gap_2, gap_3, gap_4)
feedback = """
Der vollständige Code:
```java
int[][] m = {{ 0, 1, 2, 3}, // Matrix
{ 4, 5, 6, 7},
{8, 9, 10, 11}};
int[] z = { 12, 13, 14}; // Zeile
int[] s = {15, 16, 17, 18}; // Spalte
int n_ze = m.length; // Zeilenanzahl der Matrix
int n_sp = m[0].length; // Spaltenanzahl der Matrix
/* 1: Zeile mal Matrix */011: int[] zm = new int[n_sp];
for (int j = 0; j < n_sp; j++) {
zm[j] = 0;
for (int i = 0; i < n_ze; i++)
zm[j] = zm[j] + z[i]*m[i][j];
}
/* 2: Matrix mal Spalte */
int[] ms = new int[n_ze];
for (int i = 0; i < n_ze; i++) {
ms[i] = 0;
for (int j = 0; j < n_sp; j++)
ms[i] = ms[i] + m[i][j]*s[j];
```
"""
\ No newline at end of file
File moved
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment