factory.py
3.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# QFactory is a class that can generate question instances, e.g. by shuffling
# options, running a script to generate the question, etc.
#
# To generate an instance of a question we use the method generate() where
# the argument is the reference of the question we wish to produce.
#
# Example:
#
# # read question from file
# qdict = tools.load_yaml(filename)
# qfactory = QFactory(qdict)
# question = qfactory.generate()
#
# # experiment answering one question and correct it
# question.updateAnswer('42') # insert answer
# grade = question.correct() # correct answer
# An instance of an actual question is an object that inherits from Question()
#
# Question - base class inherited by other classes
# QuestionInformation - not a question, just a box with content
# QuestionRadio - single choice from a list of options
# QuestionCheckbox - multiple choice, equivalent to multiple true/false
# QuestionText - line of text compared to a list of acceptable answers
# QuestionTextRegex - line of text matched against a regular expression
# QuestionTextArea - corrected by an external program
# QuestionNumericInterval - line of text parsed as a float
# base
from os import path
import logging
# project
from tools import run_script
from questions import QuestionInformation, QuestionRadio, QuestionCheckbox, QuestionText, QuestionTextRegex, QuestionTextArea, QuestionNumericInterval
# setup logger for this module
logger = logging.getLogger(__name__)
# Topic Factory
# Generates a list
# class RunningTopic(object):
# def __init__(self, graph, topic):
# ===========================================================================
# Question Factory
# ===========================================================================
class QFactory(object):
# Depending on the type of question, a different question class will be
# instantiated. All these classes derive from the base class `Question`.
_types = {
'radio' : QuestionRadio,
'checkbox' : QuestionCheckbox,
'text' : QuestionText,
'text-regex': QuestionTextRegex,
'numeric-interval': QuestionNumericInterval,
'textarea' : QuestionTextArea,
# -- informative panels --
'information': QuestionInformation, 'info': QuestionInformation,
'warning' : QuestionInformation, 'warn': QuestionInformation,
'alert' : QuestionInformation,
'success' : QuestionInformation,
}
def __init__(self, question_dict={}):
self.question = question_dict
# -----------------------------------------------------------------------
# Given a ref returns an instance of a descendent of Question(),
# i.e. a question object (radio, checkbox, ...).
# -----------------------------------------------------------------------
def generate(self):
logger.debug(f'Generating "{self.question["ref"]}"...')
# Shallow copy so that script generated questions will not replace
# the original generators
q = self.question.copy()
# If question is of generator type, an external program will be run
# which will print a valid question in yaml format to stdout. This
# output is then yaml parsed into a dictionary `q`.
if q['type'] == 'generator':
logger.debug(f' \\_ Running "{q["script"]}".')
q.setdefault('arg', '') # optional arguments will be sent to stdin
script = path.join(q['path'], q['script'])
out = run_script(script=script, stdin=q['arg'])
q.update(out)
# Finally we create an instance of Question()
try:
qinstance = self._types[q['type']](q) # instance with correct class
except KeyError as e:
logger.error(f'Unknown type "{q["type"]}" in question "{q["ref"]}"')
raise e
else:
return qinstance