Commit 9dbd4c5b by Ned Batchelder

Merge pull request #2197 from edx/ned/add-responsetypes-registry

Make a ResponseType registry.
parents a505fa95 3942876e
...@@ -25,18 +25,14 @@ from copy import deepcopy ...@@ -25,18 +25,14 @@ from copy import deepcopy
from capa.correctmap import CorrectMap from capa.correctmap import CorrectMap
import capa.inputtypes as inputtypes import capa.inputtypes as inputtypes
import capa.customrender as customrender import capa.customrender as customrender
import capa.responsetypes as responsetypes
from capa.util import contextualize_text, convert_files_to_filenames from capa.util import contextualize_text, convert_files_to_filenames
import capa.xqueue_interface as xqueue_interface import capa.xqueue_interface as xqueue_interface
# to be replaced with auto-registering
import capa.responsetypes as responsetypes
from capa.safe_exec import safe_exec from capa.safe_exec import safe_exec
from pytz import UTC from pytz import UTC
# dict of tagname, Response Class -- this should come from auto-registering
response_tag_dict = dict([(x.response_tag, x) for x in responsetypes.__all__])
# extra things displayed after "show answers" is pressed # extra things displayed after "show answers" is pressed
solution_tags = ['solution'] solution_tags = ['solution']
...@@ -652,7 +648,7 @@ class LoncapaProblem(object): ...@@ -652,7 +648,7 @@ class LoncapaProblem(object):
''' '''
response_id = 1 response_id = 1
self.responders = {} self.responders = {}
for response in tree.xpath('//' + "|//".join(response_tag_dict)): for response in tree.xpath('//' + "|//".join(responsetypes.registry.registered_tags())):
response_id_str = self.problem_id + "_" + str(response_id) response_id_str = self.problem_id + "_" + str(response_id)
# create and save ID for this response # create and save ID for this response
response.set('id', response_id_str) response.set('id', response_id_str)
...@@ -673,8 +669,8 @@ class LoncapaProblem(object): ...@@ -673,8 +669,8 @@ class LoncapaProblem(object):
answer_id = answer_id + 1 answer_id = answer_id + 1
# instantiate capa Response # instantiate capa Response
responder = response_tag_dict[response.tag](response, inputfields, responsetype_cls = responsetypes.registry.get_class_for_tag(response.tag)
self.context, self.system) responder = responsetype_cls(response, inputfields, self.context, self.system)
# save in list in self # save in list in self
self.responders[response] = responder self.responders[response] = responder
......
...@@ -289,6 +289,7 @@ class InputTypeBase(object): ...@@ -289,6 +289,7 @@ class InputTypeBase(object):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class OptionInput(InputTypeBase): class OptionInput(InputTypeBase):
""" """
Input type for selecting and Select option input type. Input type for selecting and Select option input type.
...@@ -333,14 +334,13 @@ class OptionInput(InputTypeBase): ...@@ -333,14 +334,13 @@ class OptionInput(InputTypeBase):
return [Attribute('options', transform=cls.parse_options), return [Attribute('options', transform=cls.parse_options),
Attribute('inline', False)] Attribute('inline', False)]
registry.register(OptionInput)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# TODO: consolidate choicegroup, radiogroup, checkboxgroup after discussion of # TODO: consolidate choicegroup, radiogroup, checkboxgroup after discussion of
# desired semantics. # desired semantics.
@registry.register
class ChoiceGroup(InputTypeBase): class ChoiceGroup(InputTypeBase):
""" """
Radio button or checkbox inputs: multiple choice or true/false Radio button or checkbox inputs: multiple choice or true/false
...@@ -415,12 +415,10 @@ class ChoiceGroup(InputTypeBase): ...@@ -415,12 +415,10 @@ class ChoiceGroup(InputTypeBase):
return choices return choices
registry.register(ChoiceGroup)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class JavascriptInput(InputTypeBase): class JavascriptInput(InputTypeBase):
""" """
Hidden field for javascript to communicate via; also loads the required Hidden field for javascript to communicate via; also loads the required
...@@ -451,13 +449,11 @@ class JavascriptInput(InputTypeBase): ...@@ -451,13 +449,11 @@ class JavascriptInput(InputTypeBase):
if self.value == "": if self.value == "":
self.value = 'null' self.value = 'null'
registry.register(JavascriptInput)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class JSInput(InputTypeBase): class JSInput(InputTypeBase):
""" """
Inputtype for general javascript inputs. Intended to be used with Inputtype for general javascript inputs. Intended to be used with
...@@ -480,7 +476,7 @@ class JSInput(InputTypeBase): ...@@ -480,7 +476,7 @@ class JSInput(InputTypeBase):
height="500" height="500"
width="400"/> width="400"/>
See the documentation in docs/data/source/course_data_formats/jsinput.rst See the documentation in docs/data/source/course_data_formats/jsinput.rst
for more information. for more information.
""" """
...@@ -517,11 +513,10 @@ class JSInput(InputTypeBase): ...@@ -517,11 +513,10 @@ class JSInput(InputTypeBase):
return context return context
registry.register(JSInput)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class TextLine(InputTypeBase): class TextLine(InputTypeBase):
""" """
A text line input. Can do math preview if "math"="1" is specified. A text line input. Can do math preview if "math"="1" is specified.
...@@ -587,11 +582,10 @@ class TextLine(InputTypeBase): ...@@ -587,11 +582,10 @@ class TextLine(InputTypeBase):
return {'do_math': self.do_math, return {'do_math': self.do_math,
'preprocessor': self.preprocessor, } 'preprocessor': self.preprocessor, }
registry.register(TextLine)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class FileSubmission(InputTypeBase): class FileSubmission(InputTypeBase):
""" """
Upload some files (e.g. for programming assignments) Upload some files (e.g. for programming assignments)
...@@ -636,11 +630,10 @@ class FileSubmission(InputTypeBase): ...@@ -636,11 +630,10 @@ class FileSubmission(InputTypeBase):
def _extra_context(self): def _extra_context(self):
return {'queue_len': self.queue_len, } return {'queue_len': self.queue_len, }
registry.register(FileSubmission)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class CodeInput(InputTypeBase): class CodeInput(InputTypeBase):
""" """
A text area input for code--uses codemirror, does syntax highlighting, special tab handling, A text area input for code--uses codemirror, does syntax highlighting, special tab handling,
...@@ -700,12 +693,11 @@ class CodeInput(InputTypeBase): ...@@ -700,12 +693,11 @@ class CodeInput(InputTypeBase):
"""Defined queue_len, add it """ """Defined queue_len, add it """
return {'queue_len': self.queue_len, } return {'queue_len': self.queue_len, }
registry.register(CodeInput)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class MatlabInput(CodeInput): class MatlabInput(CodeInput):
''' '''
InputType for handling Matlab code input InputType for handling Matlab code input
...@@ -866,11 +858,9 @@ class MatlabInput(CodeInput): ...@@ -866,11 +858,9 @@ class MatlabInput(CodeInput):
return {'success': error == 0, 'message': msg} return {'success': error == 0, 'message': msg}
registry.register(MatlabInput)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class Schematic(InputTypeBase): class Schematic(InputTypeBase):
""" """
InputType for the schematic editor InputType for the schematic editor
...@@ -893,11 +883,10 @@ class Schematic(InputTypeBase): ...@@ -893,11 +883,10 @@ class Schematic(InputTypeBase):
Attribute('submit_analyses', None), ] Attribute('submit_analyses', None), ]
registry.register(Schematic)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class ImageInput(InputTypeBase): class ImageInput(InputTypeBase):
""" """
Clickable image as an input field. Element should specify the image source, height, Clickable image as an input field. Element should specify the image source, height,
...@@ -939,11 +928,10 @@ class ImageInput(InputTypeBase): ...@@ -939,11 +928,10 @@ class ImageInput(InputTypeBase):
return {'gx': self.gx, return {'gx': self.gx,
'gy': self.gy} 'gy': self.gy}
registry.register(ImageInput)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class Crystallography(InputTypeBase): class Crystallography(InputTypeBase):
""" """
An input for crystallography -- user selects 3 points on the axes, and we get a plane. An input for crystallography -- user selects 3 points on the axes, and we get a plane.
...@@ -963,11 +951,10 @@ class Crystallography(InputTypeBase): ...@@ -963,11 +951,10 @@ class Crystallography(InputTypeBase):
Attribute('width'), Attribute('width'),
] ]
registry.register(Crystallography)
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
@registry.register
class VseprInput(InputTypeBase): class VseprInput(InputTypeBase):
""" """
Input for molecular geometry--show possible structures, let student Input for molecular geometry--show possible structures, let student
...@@ -988,11 +975,10 @@ class VseprInput(InputTypeBase): ...@@ -988,11 +975,10 @@ class VseprInput(InputTypeBase):
Attribute('geometries'), Attribute('geometries'),
] ]
registry.register(VseprInput)
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@registry.register
class ChemicalEquationInput(InputTypeBase): class ChemicalEquationInput(InputTypeBase):
""" """
An input type for entering chemical equations. Supports live preview. An input type for entering chemical equations. Supports live preview.
...@@ -1064,11 +1050,10 @@ class ChemicalEquationInput(InputTypeBase): ...@@ -1064,11 +1050,10 @@ class ChemicalEquationInput(InputTypeBase):
return result return result
registry.register(ChemicalEquationInput)
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@registry.register
class FormulaEquationInput(InputTypeBase): class FormulaEquationInput(InputTypeBase):
""" """
An input type for entering formula equations. Supports live preview. An input type for entering formula equations. Supports live preview.
...@@ -1160,11 +1145,10 @@ class FormulaEquationInput(InputTypeBase): ...@@ -1160,11 +1145,10 @@ class FormulaEquationInput(InputTypeBase):
return result return result
registry.register(FormulaEquationInput)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class DragAndDropInput(InputTypeBase): class DragAndDropInput(InputTypeBase):
""" """
Input for drag and drop problems. Allows student to drag and drop images and Input for drag and drop problems. Allows student to drag and drop images and
...@@ -1259,11 +1243,10 @@ class DragAndDropInput(InputTypeBase): ...@@ -1259,11 +1243,10 @@ class DragAndDropInput(InputTypeBase):
self.loaded_attributes['drag_and_drop_json'] = json.dumps(to_js) self.loaded_attributes['drag_and_drop_json'] = json.dumps(to_js)
self.to_render.add('drag_and_drop_json') self.to_render.add('drag_and_drop_json')
registry.register(DragAndDropInput)
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@registry.register
class EditAMoleculeInput(InputTypeBase): class EditAMoleculeInput(InputTypeBase):
""" """
An input type for edit-a-molecule. Integrates with the molecule editor java applet. An input type for edit-a-molecule. Integrates with the molecule editor java applet.
...@@ -1296,11 +1279,10 @@ class EditAMoleculeInput(InputTypeBase): ...@@ -1296,11 +1279,10 @@ class EditAMoleculeInput(InputTypeBase):
return context return context
registry.register(EditAMoleculeInput)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class DesignProtein2dInput(InputTypeBase): class DesignProtein2dInput(InputTypeBase):
""" """
An input type for design of a protein in 2D. Integrates with the Protex java applet. An input type for design of a protein in 2D. Integrates with the Protex java applet.
...@@ -1333,11 +1315,10 @@ class DesignProtein2dInput(InputTypeBase): ...@@ -1333,11 +1315,10 @@ class DesignProtein2dInput(InputTypeBase):
return context return context
registry.register(DesignProtein2dInput)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class EditAGeneInput(InputTypeBase): class EditAGeneInput(InputTypeBase):
""" """
An input type for editing a gene. An input type for editing a gene.
...@@ -1370,11 +1351,10 @@ class EditAGeneInput(InputTypeBase): ...@@ -1370,11 +1351,10 @@ class EditAGeneInput(InputTypeBase):
return context return context
registry.register(EditAGeneInput)
#--------------------------------------------------------------------- #---------------------------------------------------------------------
@registry.register
class AnnotationInput(InputTypeBase): class AnnotationInput(InputTypeBase):
""" """
Input type for annotations: students can enter some notes or other text Input type for annotations: students can enter some notes or other text
...@@ -1481,9 +1461,8 @@ class AnnotationInput(InputTypeBase): ...@@ -1481,9 +1461,8 @@ class AnnotationInput(InputTypeBase):
return extra_context return extra_context
registry.register(AnnotationInput)
@registry.register
class ChoiceTextGroup(InputTypeBase): class ChoiceTextGroup(InputTypeBase):
""" """
Groups of radiobutton/checkboxes with text inputs. Groups of radiobutton/checkboxes with text inputs.
...@@ -1686,5 +1665,3 @@ class ChoiceTextGroup(InputTypeBase): ...@@ -1686,5 +1665,3 @@ class ChoiceTextGroup(InputTypeBase):
# Add the tuple for the current choice to the list of choices # Add the tuple for the current choice to the list of choices
choices.append((choice.get("name"), components)) choices.append((choice.get("name"), components))
return choices return choices
registry.register(ChoiceTextGroup)
"""A registry for finding classes based on tags in the class."""
class TagRegistry(object): class TagRegistry(object):
""" """
A registry mapping tags to handlers. A registry mapping tags to handlers.
...@@ -35,6 +37,9 @@ class TagRegistry(object): ...@@ -35,6 +37,9 @@ class TagRegistry(object):
for t in cls.tags: for t in cls.tags:
self._mapping[t] = cls self._mapping[t] = cls
# Returning the cls means we can use this as a decorator.
return cls
def registered_tags(self): def registered_tags(self):
""" """
Get a list of all the tags that have been registered. Get a list of all the tags that have been registered.
......
...@@ -33,6 +33,7 @@ from shapely.geometry import Point, MultiPoint ...@@ -33,6 +33,7 @@ from shapely.geometry import Point, MultiPoint
# specific library imports # specific library imports
from calc import evaluator, UndefinedVariable from calc import evaluator, UndefinedVariable
from . import correctmap from . import correctmap
from .registry import TagRegistry
from datetime import datetime from datetime import datetime
from pytz import UTC from pytz import UTC
from .util import (compare_with_tolerance, contextualize_text, convert_files_to_filenames, from .util import (compare_with_tolerance, contextualize_text, convert_files_to_filenames,
...@@ -45,6 +46,7 @@ import capa.safe_exec as safe_exec ...@@ -45,6 +46,7 @@ import capa.safe_exec as safe_exec
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
registry = TagRegistry()
CorrectMap = correctmap.CorrectMap # pylint: disable=C0103 CorrectMap = correctmap.CorrectMap # pylint: disable=C0103
CORRECTMAP_PY = None CORRECTMAP_PY = None
...@@ -92,7 +94,7 @@ class LoncapaResponse(object): ...@@ -92,7 +94,7 @@ class LoncapaResponse(object):
Each subclass must also define the following attributes: Each subclass must also define the following attributes:
- response_tag : xhtml tag identifying this response (used in auto-registering) - tags : xhtml tags identifying this response (used in auto-registering)
In addition, these methods are optional: In addition, these methods are optional:
...@@ -120,7 +122,7 @@ class LoncapaResponse(object): ...@@ -120,7 +122,7 @@ class LoncapaResponse(object):
""" """
__metaclass__ = abc.ABCMeta # abc = Abstract Base Class __metaclass__ = abc.ABCMeta # abc = Abstract Base Class
response_tag = None tags = None
hint_tag = None hint_tag = None
max_inputfields = None max_inputfields = None
...@@ -405,13 +407,14 @@ class LoncapaResponse(object): ...@@ -405,13 +407,14 @@ class LoncapaResponse(object):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class JavascriptResponse(LoncapaResponse): class JavascriptResponse(LoncapaResponse):
""" """
This response type is used when the student's answer is graded via This response type is used when the student's answer is graded via
Javascript using Node.js. Javascript using Node.js.
""" """
response_tag = 'javascriptresponse' tags = ['javascriptresponse']
max_inputfields = 1 max_inputfields = 1
allowed_inputfields = ['javascriptinput'] allowed_inputfields = ['javascriptinput']
...@@ -605,6 +608,7 @@ class JavascriptResponse(LoncapaResponse): ...@@ -605,6 +608,7 @@ class JavascriptResponse(LoncapaResponse):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class ChoiceResponse(LoncapaResponse): class ChoiceResponse(LoncapaResponse):
""" """
This response type is used when the student chooses from a discrete set of This response type is used when the student chooses from a discrete set of
...@@ -653,7 +657,7 @@ class ChoiceResponse(LoncapaResponse): ...@@ -653,7 +657,7 @@ class ChoiceResponse(LoncapaResponse):
""" """
response_tag = 'choiceresponse' tags = ['choiceresponse']
max_inputfields = 1 max_inputfields = 1
allowed_inputfields = ['checkboxgroup', 'radiogroup'] allowed_inputfields = ['checkboxgroup', 'radiogroup']
correct_choices = None correct_choices = None
...@@ -702,10 +706,11 @@ class ChoiceResponse(LoncapaResponse): ...@@ -702,10 +706,11 @@ class ChoiceResponse(LoncapaResponse):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class MultipleChoiceResponse(LoncapaResponse): class MultipleChoiceResponse(LoncapaResponse):
# TODO: handle direction and randomize # TODO: handle direction and randomize
response_tag = 'multiplechoiceresponse' tags = ['multiplechoiceresponse']
max_inputfields = 1 max_inputfields = 1
allowed_inputfields = ['choicegroup'] allowed_inputfields = ['choicegroup']
correct_choices = None correct_choices = None
...@@ -759,9 +764,10 @@ class MultipleChoiceResponse(LoncapaResponse): ...@@ -759,9 +764,10 @@ class MultipleChoiceResponse(LoncapaResponse):
return {self.answer_id: self.correct_choices} return {self.answer_id: self.correct_choices}
@registry.register
class TrueFalseResponse(MultipleChoiceResponse): class TrueFalseResponse(MultipleChoiceResponse):
response_tag = 'truefalseresponse' tags = ['truefalseresponse']
def mc_setup_response(self): def mc_setup_response(self):
i = 0 i = 0
...@@ -786,12 +792,13 @@ class TrueFalseResponse(MultipleChoiceResponse): ...@@ -786,12 +792,13 @@ class TrueFalseResponse(MultipleChoiceResponse):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class OptionResponse(LoncapaResponse): class OptionResponse(LoncapaResponse):
''' '''
TODO: handle direction and randomize TODO: handle direction and randomize
''' '''
response_tag = 'optionresponse' tags = ['optionresponse']
hint_tag = 'optionhint' hint_tag = 'optionhint'
allowed_inputfields = ['optioninput'] allowed_inputfields = ['optioninput']
answer_fields = None answer_fields = None
...@@ -819,13 +826,14 @@ class OptionResponse(LoncapaResponse): ...@@ -819,13 +826,14 @@ class OptionResponse(LoncapaResponse):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class NumericalResponse(LoncapaResponse): class NumericalResponse(LoncapaResponse):
''' '''
This response type expects a number or formulaic expression that evaluates This response type expects a number or formulaic expression that evaluates
to a number (e.g. `4+5/2^2`), and accepts with a tolerance. to a number (e.g. `4+5/2^2`), and accepts with a tolerance.
''' '''
response_tag = 'numericalresponse' tags = ['numericalresponse']
hint_tag = 'numericalhint' hint_tag = 'numericalhint'
allowed_inputfields = ['textline', 'formulaequationinput'] allowed_inputfields = ['textline', 'formulaequationinput']
required_attributes = ['answer'] required_attributes = ['answer']
...@@ -946,6 +954,7 @@ class NumericalResponse(LoncapaResponse): ...@@ -946,6 +954,7 @@ class NumericalResponse(LoncapaResponse):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class StringResponse(LoncapaResponse): class StringResponse(LoncapaResponse):
''' '''
This response type allows one or more answers. This response type allows one or more answers.
...@@ -978,7 +987,7 @@ class StringResponse(LoncapaResponse): ...@@ -978,7 +987,7 @@ class StringResponse(LoncapaResponse):
</hintgroup> </hintgroup>
</stringresponse> </stringresponse>
''' '''
response_tag = 'stringresponse' tags = ['stringresponse']
hint_tag = 'stringhint' hint_tag = 'stringhint'
allowed_inputfields = ['textline'] allowed_inputfields = ['textline']
required_attributes = ['answer'] required_attributes = ['answer']
...@@ -1080,13 +1089,14 @@ class StringResponse(LoncapaResponse): ...@@ -1080,13 +1089,14 @@ class StringResponse(LoncapaResponse):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class CustomResponse(LoncapaResponse): class CustomResponse(LoncapaResponse):
''' '''
Custom response. The python code to be run should be in <answer>...</answer> Custom response. The python code to be run should be in <answer>...</answer>
or in a <script>...</script> or in a <script>...</script>
''' '''
response_tag = 'customresponse' tags = ['customresponse']
allowed_inputfields = ['textline', 'textbox', 'crystallography', allowed_inputfields = ['textline', 'textbox', 'crystallography',
'chemicalequationinput', 'vsepr_input', 'chemicalequationinput', 'vsepr_input',
...@@ -1408,12 +1418,13 @@ class CustomResponse(LoncapaResponse): ...@@ -1408,12 +1418,13 @@ class CustomResponse(LoncapaResponse):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class SymbolicResponse(CustomResponse): class SymbolicResponse(CustomResponse):
""" """
Symbolic math response checking, using symmath library. Symbolic math response checking, using symmath library.
""" """
response_tag = 'symbolicresponse' tags = ['symbolicresponse']
max_inputfields = 1 max_inputfields = 1
def setup_response(self): def setup_response(self):
...@@ -1456,6 +1467,7 @@ class SymbolicResponse(CustomResponse): ...@@ -1456,6 +1467,7 @@ class SymbolicResponse(CustomResponse):
ScoreMessage = namedtuple('ScoreMessage', ['valid', 'correct', 'points', 'msg']) # pylint: disable=invalid-name ScoreMessage = namedtuple('ScoreMessage', ['valid', 'correct', 'points', 'msg']) # pylint: disable=invalid-name
@registry.register
class CodeResponse(LoncapaResponse): class CodeResponse(LoncapaResponse):
""" """
Grade student code using an external queueing server, called 'xqueue' Grade student code using an external queueing server, called 'xqueue'
...@@ -1472,7 +1484,7 @@ class CodeResponse(LoncapaResponse): ...@@ -1472,7 +1484,7 @@ class CodeResponse(LoncapaResponse):
(i.e. and not for getting reference answers) (i.e. and not for getting reference answers)
""" """
response_tag = 'coderesponse' tags = ['coderesponse']
allowed_inputfields = ['textbox', 'filesubmission', 'matlabinput'] allowed_inputfields = ['textbox', 'filesubmission', 'matlabinput']
max_inputfields = 1 max_inputfields = 1
payload = None payload = None
...@@ -1705,6 +1717,7 @@ class CodeResponse(LoncapaResponse): ...@@ -1705,6 +1717,7 @@ class CodeResponse(LoncapaResponse):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class ExternalResponse(LoncapaResponse): class ExternalResponse(LoncapaResponse):
""" """
Grade the students input using an external server. Grade the students input using an external server.
...@@ -1713,7 +1726,7 @@ class ExternalResponse(LoncapaResponse): ...@@ -1713,7 +1726,7 @@ class ExternalResponse(LoncapaResponse):
""" """
response_tag = 'externalresponse' tags = ['externalresponse']
allowed_inputfields = ['textline', 'textbox'] allowed_inputfields = ['textline', 'textbox']
awdmap = { awdmap = {
'EXACT_ANS': 'correct', # TODO: handle other loncapa responses 'EXACT_ANS': 'correct', # TODO: handle other loncapa responses
...@@ -1864,12 +1877,13 @@ class ExternalResponse(LoncapaResponse): ...@@ -1864,12 +1877,13 @@ class ExternalResponse(LoncapaResponse):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class FormulaResponse(LoncapaResponse): class FormulaResponse(LoncapaResponse):
""" """
Checking of symbolic math response using numerical sampling. Checking of symbolic math response using numerical sampling.
""" """
response_tag = 'formularesponse' tags = ['formularesponse']
hint_tag = 'formulahint' hint_tag = 'formulahint'
allowed_inputfields = ['textline', 'formulaequationinput'] allowed_inputfields = ['textline', 'formulaequationinput']
required_attributes = ['answer', 'samples'] required_attributes = ['answer', 'samples']
...@@ -2068,11 +2082,12 @@ class FormulaResponse(LoncapaResponse): ...@@ -2068,11 +2082,12 @@ class FormulaResponse(LoncapaResponse):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class SchematicResponse(LoncapaResponse): class SchematicResponse(LoncapaResponse):
""" """
Circuit schematic response type. Circuit schematic response type.
""" """
response_tag = 'schematicresponse' tags = ['schematicresponse']
allowed_inputfields = ['schematic'] allowed_inputfields = ['schematic']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
...@@ -2118,6 +2133,7 @@ class SchematicResponse(LoncapaResponse): ...@@ -2118,6 +2133,7 @@ class SchematicResponse(LoncapaResponse):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class ImageResponse(LoncapaResponse): class ImageResponse(LoncapaResponse):
""" """
Handle student response for image input: the input is a click on an image, Handle student response for image input: the input is a click on an image,
...@@ -2145,7 +2161,7 @@ class ImageResponse(LoncapaResponse): ...@@ -2145,7 +2161,7 @@ class ImageResponse(LoncapaResponse):
True, if click is inside any region or rectangle. Otherwise False. True, if click is inside any region or rectangle. Otherwise False.
""" """
response_tag = 'imageresponse' tags = ['imageresponse']
allowed_inputfields = ['imageinput'] allowed_inputfields = ['imageinput']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
...@@ -2248,6 +2264,7 @@ class ImageResponse(LoncapaResponse): ...@@ -2248,6 +2264,7 @@ class ImageResponse(LoncapaResponse):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@registry.register
class AnnotationResponse(LoncapaResponse): class AnnotationResponse(LoncapaResponse):
""" """
Checking of annotation responses. Checking of annotation responses.
...@@ -2255,7 +2272,7 @@ class AnnotationResponse(LoncapaResponse): ...@@ -2255,7 +2272,7 @@ class AnnotationResponse(LoncapaResponse):
The response contains both a comment (student commentary) and an option (student tag). The response contains both a comment (student commentary) and an option (student tag).
Only the tag is currently graded. Answers may be incorrect, partially correct, or correct. Only the tag is currently graded. Answers may be incorrect, partially correct, or correct.
""" """
response_tag = 'annotationresponse' tags = ['annotationresponse']
allowed_inputfields = ['annotationinput'] allowed_inputfields = ['annotationinput']
max_inputfields = 1 max_inputfields = 1
default_scoring = {'incorrect': 0, 'partially-correct': 1, 'correct': 2} default_scoring = {'incorrect': 0, 'partially-correct': 1, 'correct': 2}
...@@ -2371,6 +2388,7 @@ class AnnotationResponse(LoncapaResponse): ...@@ -2371,6 +2388,7 @@ class AnnotationResponse(LoncapaResponse):
return None return None
@registry.register
class ChoiceTextResponse(LoncapaResponse): class ChoiceTextResponse(LoncapaResponse):
""" """
Allows for multiple choice responses with text inputs Allows for multiple choice responses with text inputs
...@@ -2378,7 +2396,7 @@ class ChoiceTextResponse(LoncapaResponse): ...@@ -2378,7 +2396,7 @@ class ChoiceTextResponse(LoncapaResponse):
ChoiceResponse. ChoiceResponse.
""" """
response_tag = 'choicetextresponse' tags = ['choicetextresponse']
max_inputfields = 1 max_inputfields = 1
allowed_inputfields = ['choicetextgroup', allowed_inputfields = ['choicetextgroup',
'checkboxtextgroup', 'checkboxtextgroup',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment