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

Moved some static data into a config file. Added documentation.

parent 682d8385
No related branches found
No related tags found
No related merge requests found
[META]
author = Jan Maximilian Michal
[UPLAODER]
user = root
pass = homer
host = http://localhost:8000/
rtoken = c13456ec3d71dc657e19fb826750f676
#!/usr/local/bin/python3 #!/usr/local/bin/python3
################################################################################ ##########################################################################
# #
# This script contains the main part of hallgrim and is the only script that # This script contains the main part of hallgrim and is the only script that
# needs to be invoked. The steps it takes to generate a task are as follows: # needs to be invoked. The steps it takes to generate a task are as follows:
...@@ -15,12 +15,13 @@ ...@@ -15,12 +15,13 @@
# * a finisher compresses data if needed (needs to be implemented, maybe as # * a finisher compresses data if needed (needs to be implemented, maybe as
# separate subparser). # separate subparser).
# #
################################################################################ ##########################################################################
import importlib import importlib
import argparse import argparse
import os import os
import sys import sys
import configparser
# local import # local import
from hallgrim.IliasXMLCreator import packer from hallgrim.IliasXMLCreator import packer
...@@ -29,18 +30,11 @@ from hallgrim.messages import * ...@@ -29,18 +30,11 @@ from hallgrim.messages import *
from hallgrim.parser import * from hallgrim.parser import *
from hallgrim.uploader import send_script from hallgrim.uploader import send_script
scaffolding = r'''
meta = {{
'author': '{}',
'title': '{}',
'type': '{}',
'points': {},
}}
task = """ decription """ def get_config():
{} config = configparser.ConfigParser()
feedback = """ decription """ config.read('config.ini')
''' return config
def file_to_module(name): def file_to_module(name):
...@@ -93,7 +87,6 @@ def parseme(): ...@@ -93,7 +87,6 @@ def parseme():
"-a", "-a",
"--author", "--author",
help="Name of the scripts author", help="Name of the scripts author",
default='ILIAS Author',
metavar='AUTHOR' metavar='AUTHOR'
) )
parser_new.add_argument( parser_new.add_argument(
...@@ -129,10 +122,6 @@ def parseme(): ...@@ -129,10 +122,6 @@ def parseme():
parser_gen = subparsers.add_parser( parser_gen = subparsers.add_parser(
"upload", help="Subcommand to upload created xml instances.") "upload", help="Subcommand to upload created xml instances.")
parser_gen.add_argument(
'--host',
help='The hostname of the ilias test implementation',
metavar='HOST')
parser_gen.add_argument( parser_gen.add_argument(
'script', 'script',
help='The script that should be uploaded', help='The script that should be uploaded',
...@@ -152,6 +141,16 @@ def parseme(): ...@@ -152,6 +141,16 @@ def parseme():
def delegator(output, script_list, instances): def delegator(output, script_list, instances):
"""
It gets a list of filenames and delegates them to the correct handler.
Every file that does not end with .py will be ignored. Each script
is imported and then passed as module to the handler.
Arguments:
output {filename} -- where to write the finished XML document
script_list {list} -- a list of filenames that contain scripts
instances {int} -- number of instances that should be generated
"""
for script_name in filter(lambda a: a.endswith('.py'), script_list): for script_name in filter(lambda a: a.endswith('.py'), script_list):
script = importlib.import_module(file_to_module(script_name)) script = importlib.import_module(file_to_module(script_name))
handler = { handler = {
...@@ -166,6 +165,18 @@ def delegator(output, script_list, instances): ...@@ -166,6 +165,18 @@ def delegator(output, script_list, instances):
def handle_gap_questions(output, script, instances): def handle_gap_questions(output, script, instances):
""" Handles gap questions of all kinds
A script can contain any mixture of gap, numeric gap and choice gap
questions. The data object that is needed by the XML creating scripts
is generated and the task itself is handled by the parser. The parser
returns the intermediate representation of the task.
Arguments:
output {str} -- where to write the final file
script {module} -- the loaded module that describes the task
instances {int} -- number of instances that should be generated
"""
script_is_valid(script, required=['meta', 'task', 'feedback']) script_is_valid(script, required=['meta', 'task', 'feedback'])
data = { data = {
'type': type_selector(script.meta['type']), 'type': type_selector(script.meta['type']),
...@@ -186,6 +197,16 @@ def handle_gap_questions(output, script, instances): ...@@ -186,6 +197,16 @@ def handle_gap_questions(output, script, instances):
def handle_choice_questions(output, script, instances): def handle_choice_questions(output, script, instances):
"""
Handles multiple and single choice questions. The relevant parts of the
script are fed into a parser that return the correct intermediate
representation for the task. In this case a list of answers.
Arguments:
output {str} -- where to write the finished XML document
script {module} -- the loaded module that describes the task
instances {int} -- number of instances that should be generated
"""
script_is_valid(script, required=['meta', 'task', 'choices', 'feedback']) script_is_valid(script, required=['meta', 'task', 'choices', 'feedback'])
data = { data = {
'type': type_selector(script.meta['type']), 'type': type_selector(script.meta['type']),
...@@ -207,6 +228,25 @@ def handle_choice_questions(output, script, instances): ...@@ -207,6 +228,25 @@ def handle_choice_questions(output, script, instances):
def handle_new_script(name, qtype, author, points): def handle_new_script(name, qtype, author, points):
""" Creates a new script file.
Takes in some meta information from the command line of if not present takes
it from the config.ini or uses default values.
TODO: put the configuration before the parser and use as default values
Arguments:
name {str} -- name of the script, will also become filename
qtype {str} -- question type (choice, gap, alignment)
author {str} -- the author of the script
points {float} -- number of points for the task
"""
from hallgrim.templates import scaffolding
config = get_config()
if not author:
author = config['META']['author']
with open('scripts/' + name + '.py', 'w') as new_script: with open('scripts/' + name + '.py', 'w') as new_script:
choice = '' choice = ''
if qtype in ['multiple choice', 'single choice']: if qtype in ['multiple choice', 'single choice']:
...@@ -216,9 +256,27 @@ def handle_new_script(name, qtype, author, points): ...@@ -216,9 +256,27 @@ def handle_new_script(name, qtype, author, points):
author, name, qtype, points, choice).strip(), file=new_script) author, name, qtype, points, choice).strip(), file=new_script)
info('Generated new script "{}."'.format(new_script.name)) info('Generated new script "{}."'.format(new_script.name))
def handle_upload(script_path, hostname):
r = send_script(script_path) def handle_upload(script_path):
info("Uploaded %s. Status code looks %s." % (script_path, "good" if r else "bad")) """ Passes data to the upload script.
The status code should be 500, since ILIAS always replies with that error
code after an upload is confirmed. If anything else the script will say
the status code was bad.
Arguments:
script_path {str} -- path to the file that should be uploaded
"""
config = get_config()
r = send_script(
script_path,
config['UPLAODER']['host'],
config['UPLAODER']['user'],
config['UPLAODER']['pass'],
config['UPLAODER']['rtoken'],
)
info("Uploaded %s. Status code looks %s." %
(script_path, "good" if r else "bad"))
if __name__ == '__main__': if __name__ == '__main__':
markdown = get_markdown() markdown = get_markdown()
......
...@@ -10,15 +10,14 @@ def xml_print(element, **kwargs): ...@@ -10,15 +10,14 @@ def xml_print(element, **kwargs):
### ###
# Solution 1
# ['text', 'text', 'text']
# [('gap_solution', points), ..., (['one', 'two', 'three'], points)]
# #
# Type of first item in gap tuple determines the gap type. gap might be changed
# to set
# str : str_gap # str : str_gap
# list : choice_gap # list : choice_gap
# tuple : num_gap (x, min, max) # tuple : num_gap (x, min, max)
# #
# Solution 2 # Format of gap_list
# ['text', ('gap_solution', points), ('gap_solution', points), 'text', ...] # ['text', ('gap_solution', points), ('gap_solution', points), 'text', ...]
# #
### ###
......
scaffolding = r'''
meta = {{
'author': '{}',
'title': '{}',
'type': '{}',
'points': {},
}}
task = """ decription """
{}
feedback = """ decription """
'''
\ No newline at end of file
################################################################################ ##########################################################################
# #
# This is the uploader. It simplifies the testing process immensely and makes # This is the uploader. It simplifies the testing process immensely and makes
# autoILIAS finally obsolete, since this system uses a proper Ilias # autoILIAS finally obsolete, since this system uses a proper Ilias
...@@ -13,34 +13,34 @@ ...@@ -13,34 +13,34 @@
# #
# Sadly this script adds some ugly dependencies like requests_toolbelt. # Sadly this script adds some ugly dependencies like requests_toolbelt.
# #
################################################################################ ##########################################################################
import os import os
import requests import requests
from requests_toolbelt import MultipartEncoder from requests_toolbelt import MultipartEncoder
from lxml import html
__all__ = ['send_script'] __all__ = ['send_script']
# static data # static data
host = "http://localhost:8000/" login_url = "ilias/login.php"
login = {"username" : "root", "password" : "homer", "cmd[showLogin]" : "Login"} upload_url = "ilias/ilias.php?ref_id=67&cmd=post&cmdClass=ilobjquestionpoolgui&cmdNode=26:gb&baseClass=ilRepositoryGUI&fallbackCmd=upload&rtoken=%s"
upload_url = host + "ilias/ilias.php?ref_id=65&cmd=post&cmdClass=ilobjquestionpoolgui&cmdNode=26:gb&baseClass=ilRepositoryGUI&fallbackCmd=upload&rtoken=c13456ec3d71dc657e19fb826750f676" import_url = "ilias/ilias.php?ref_id=67&cmd=post&cmdClass=ilobjquestionpoolgui&cmdNode=26:gb&baseClass=ilRepositoryGUI&fallbackCmd=questions&rtoken=%s"
import_url = host + "ilias/ilias.php?ref_id=65&cmd=post&cmdClass=ilobjquestionpoolgui&cmdNode=26:gb&baseClass=ilRepositoryGUI&fallbackCmd=questions&rtoken=c13456ec3d71dc657e19fb826750f676" confirm_url = "ilias/ilias.php?ref_id=67&cmd=post&cmdClass=ilobjquestionpoolgui&cmdNode=26:gb&baseClass=ilRepositoryGUI&rtoken=%s"
confirm_url = host + "ilias/ilias.php?ref_id=65&cmd=post&cmdClass=ilobjquestionpoolgui&cmdNode=26:gb&baseClass=ilRepositoryGUI&rtoken=c13456ec3d71dc657e19fb826750f676"
import_data = { import_data = {
"cmd[importQuestions]" : "Import", "cmd[importQuestions]": "Import",
} }
confirm_data = { confirm_data = {
"cmd[importVerifiedFile]" : "Import", "cmd[importVerifiedFile]": "Import",
"questions_only" : "1", "questions_only": "1",
} }
def send_script(filepath): def send_script(filepath, host, user, password, rtoken):
login = {"username": user, "password": password, "cmd[showLogin]": "Login"}
file = MultipartEncoder(fields={ file = MultipartEncoder(fields={
'xmldoc': ( 'xmldoc': (
os.path.basename(filepath), os.path.basename(filepath),
...@@ -48,13 +48,13 @@ def send_script(filepath): ...@@ -48,13 +48,13 @@ def send_script(filepath):
'text/xml' 'text/xml'
) )
}) })
header = {'Content-Type': file.content_type}
# session create and login # session create and login
session = requests.Session() session = requests.Session()
r = session.post(host + "ilias/login.php", data=login) r = session.post(host + login_url, data=login)
r = session.post(import_url, data=import_data) r = session.post(host + (import_url % rtoken), data=import_data)
r = session.post(upload_url, data=file, headers={'Content-Type': file.content_type}) r = session.post(host + (upload_url % rtoken), data=file, headers=header)
r = session.post(confirm_url, data=confirm_data) r = session.post(host + (confirm_url % rtoken), data=confirm_data)
return r.status_code == 500 return r.status_code == 500
...@@ -21,12 +21,12 @@ choices = """ ...@@ -21,12 +21,12 @@ choices = """
feedback = """ feedback = """
| Aussage | Wert | Begründung | | Aussage | Wert | Begründung |
|---------------------------------------------------------------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |---------------------------------------------------------------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Formalparameter sind lokale Variablen. | TRUE | Die im Methodenkopf definierten Parameter heißen auch Formalparameter und werden innerhalb der Methode als Variable gebraucht. Tatsächliche Parameter sind die im Programmablauf übergebenen Werte oder Referenzen. | | Formalparameter sind lokale Variablen. | TRUE | Die im Methodenkopf definierten Parameter heißen auch Formalparameter und werden innerhalb der Methode als Variable gebraucht. Tatsächliche Parameter sind die im Programmablauf übergebenen Werte oder Referenzen. |
| Die JVM interpretiert Java-Bytecode. | TRUE | Java-Bytecode ist das Instruction-Set der Java-Virtual-Machine | | Die JVM interpretiert Java-Bytecode. | TRUE | Java-Bytecode ist das Instruction-Set der Java-Virtual-Machine |
| (false ? true : false) == (true ? false : true) | TRUE | (false ? true : false) == (true ? false : true) -> false == false -> true | | (false ? true : false) == (true ? false : true) | TRUE | (false ? true : false) == (true ? false : true) -> false == false -> true |
| Das rekursive Grundschema lautet: Rekurriere - Finalisiere (falls trivial) | FALSE | Das rekursive Grundschema lautet: Finalisiere (falls trivial) - Rekurriere | | Das rekursive Grundschema lautet: Rekurriere - Finalisiere (falls trivial) | FALSE | Das rekursive Grundschema lautet: Finalisiere (falls trivial) - Rekurriere |
| Die Java-Laufzeitumgebung beinhaltet Compiler, Klassenbibliotheken und die (JVM) Java Virtual Machine. | FALSE | Wikipedia: "Allgemein besteht die Laufzeitumgebung aus der Java Virtual Machine (Java VM), die für die Ausführung der Java-Anwendungen verantwortlich ist, einer Programmierschnittstelle (API, für Application and Programming Interface) und weiteren Programmbibliotheken." Ein Compiler gehört nicht dazu. | | Die Java-Laufzeitumgebung beinhaltet Compiler, Klassenbibliotheken und die (JVM) Java Virtual Machine. | FALSE | Wikipedia: "Allgemein besteht die Laufzeitumgebung aus der Java Virtual Machine (Java VM), die für die Ausführung der Java-Anwendungen verantwortlich ist, einer Programmierschnittstelle (API, für Application and Programming Interface) und weiteren Programmbibliotheken." Ein Compiler gehört nicht dazu. |
| An Ausdrücke dürfen keine Zuweisungen erfolgen. | FALSE | Da zum Beispiel x[0] = 1 | | An Ausdrücke dürfen keine Zuweisungen erfolgen. | FALSE | Da zum Beispiel x[0] = 1 |
| Methoden mit Signatur int (int) werden mit call-by-reference aufgerufen. | FALSE | Sämtliche Java Methoden werden mit call-by-value aufgerufen. Eine gute Erklärung zu den Unterschieden finden Sie hier. | | Methoden mit Signatur int (int) werden mit call-by-reference aufgerufen. | FALSE | Sämtliche Java Methoden werden mit call-by-value aufgerufen. Eine gute Erklärung zu den Unterschieden finden Sie hier. |
| (false ? false : true) == (false ? true : false) | FALSE | (false ? false : true) == (false ? true : false) -> true == false -> false | | (false ? false : true) == (false ? true : false) | FALSE | (false ? false : true) == (false ? true : false) -> true == false -> false |
""" """
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