# 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, 'warning' : 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, ...). # ----------------------------------------------------------------------- # async def generate_async(self): # loop = asyncio.get_event_loop() # return await loop.run_in_executor(None, self.generate) 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