Modular refactor: Better names (grade to get_score, etc.
This commit is contained in:
@@ -33,7 +33,7 @@ from lxml import etree
|
||||
from mitxmako.shortcuts import render_to_string
|
||||
|
||||
class SimpleInput():# XModule
|
||||
''' Type for simple inputs
|
||||
''' Type for simple inputs -- plain HTML with a form element
|
||||
State is a dictionary with optional keys:
|
||||
* Value
|
||||
* ID
|
||||
@@ -42,18 +42,18 @@ class SimpleInput():# XModule
|
||||
feedback from previous attempt)
|
||||
'''
|
||||
|
||||
simple_types = {} ## Maps tags to functions
|
||||
xml_tags = {} ## Maps tags to functions
|
||||
|
||||
@classmethod
|
||||
def get_xml_tags(c):
|
||||
return c.simple_types.keys()
|
||||
return c.xml_tags.keys()
|
||||
|
||||
@classmethod
|
||||
def get_uses(c):
|
||||
return ['capa_input']
|
||||
return ['capa_input', 'capa_transform']
|
||||
|
||||
def get_html(self):
|
||||
return self.simple_types[self.tag](self.xml, self.value, self.status, self.msg)
|
||||
return self.xml_tags[self.tag](self.xml, self.value, self.status, self.msg)
|
||||
|
||||
def __init__(self, system, xml, item_id = None, track_url=None, state=None, use = 'capa_input'):
|
||||
self.xml = xml
|
||||
@@ -80,18 +80,65 @@ class SimpleInput():# XModule
|
||||
if 'status' in state:
|
||||
self.status = state['status']
|
||||
|
||||
def simpleinput(fn, names=None):
|
||||
## TODO
|
||||
# class SimpleTransform():
|
||||
# ''' Type for simple XML to HTML transforms. Examples:
|
||||
# * Math tags, which go from LON-CAPA-style m-tags to MathJAX
|
||||
# '''
|
||||
# xml_tags = {} ## Maps tags to functions
|
||||
|
||||
# @classmethod
|
||||
# def get_xml_tags(c):
|
||||
# return c.xml_tags.keys()
|
||||
|
||||
# @classmethod
|
||||
# def get_uses(c):
|
||||
# return ['capa_transform']
|
||||
|
||||
# def get_html(self):
|
||||
# return self.xml_tags[self.tag](self.xml, self.value, self.status, self.msg)
|
||||
|
||||
# def __init__(self, system, xml, item_id = None, track_url=None, state=None, use = 'capa_input'):
|
||||
# self.xml = xml
|
||||
# self.tag = xml.tag
|
||||
# if not state:
|
||||
# state = {}
|
||||
# if item_id:
|
||||
# self.id = item_id
|
||||
# if xml.get('id'):
|
||||
# self.id = xml.get('id')
|
||||
# if 'id' in state:
|
||||
# self.id = state['id']
|
||||
# self.system = system
|
||||
|
||||
# self.value = ''
|
||||
# if 'value' in state:
|
||||
# self.value = state['value']
|
||||
|
||||
# self.msg = ''
|
||||
# if 'feedback' in state and 'message' in state['feedback']:
|
||||
# self.msg = state['feedback']['message']
|
||||
|
||||
# self.status = 'unanswered'
|
||||
# if 'status' in state:
|
||||
# self.status = state['status']
|
||||
|
||||
|
||||
def register_render_function(fn, names=None, cls=SimpleInput):
|
||||
if names == None:
|
||||
SimpleInput.simple_types[fn.__name__] = fn
|
||||
SimpleInput.xml_tags[fn.__name__] = fn
|
||||
else:
|
||||
raise "Unimplemented/input types"
|
||||
def wrapped():
|
||||
return fn
|
||||
return wrapped
|
||||
|
||||
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
@simpleinput
|
||||
@register_render_function
|
||||
def optioninput(element, value, status, msg=''):
|
||||
'''
|
||||
Select option input type.
|
||||
@@ -125,7 +172,7 @@ def optioninput(element, value, status, msg=''):
|
||||
return etree.XML(html)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
@simpleinput
|
||||
@register_render_function
|
||||
def choicegroup(element, value, status, msg=''):
|
||||
'''
|
||||
Radio button inputs: multiple choice or true/false
|
||||
@@ -148,7 +195,7 @@ def choicegroup(element, value, status, msg=''):
|
||||
html=render_to_string("choicegroup.html", context)
|
||||
return etree.XML(html)
|
||||
|
||||
@simpleinput
|
||||
@register_render_function
|
||||
def textline(element, value, state, msg=""):
|
||||
eid=element.get('id')
|
||||
count = int(eid.split('_')[-2])-1 # HACK
|
||||
@@ -159,7 +206,7 @@ def textline(element, value, state, msg=""):
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
@simpleinput
|
||||
@register_render_function
|
||||
def js_textline(element, value, status, msg=''):
|
||||
'''
|
||||
Plan: We will inspect element to figure out type
|
||||
@@ -185,7 +232,7 @@ def js_textline(element, value, status, msg=''):
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
## TODO: Make a wrapper for <codeinput>
|
||||
@simpleinput
|
||||
@register_render_function
|
||||
def textbox(element, value, status, msg=''):
|
||||
'''
|
||||
The textbox is used for code input. The message is the return HTML string from
|
||||
@@ -201,7 +248,7 @@ def textbox(element, value, status, msg=''):
|
||||
return etree.XML(html)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
@simpleinput
|
||||
@register_render_function
|
||||
def schematic(element, value, status, msg=''):
|
||||
eid = element.get('id')
|
||||
height = element.get('height')
|
||||
@@ -226,7 +273,7 @@ def schematic(element, value, status, msg=''):
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
### TODO: Move out of inputtypes
|
||||
@simpleinput
|
||||
@register_render_function
|
||||
def math(element, value, status, msg=''):
|
||||
'''
|
||||
This is not really an input type. It is a convention from Lon-CAPA, used for
|
||||
@@ -261,7 +308,7 @@ def math(element, value, status, msg=''):
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
@simpleinput
|
||||
@register_render_function
|
||||
def solution(element, value, status, msg=''):
|
||||
'''
|
||||
This is not really an input type. It is just a <span>...</span> which is given an ID,
|
||||
@@ -282,7 +329,7 @@ def solution(element, value, status, msg=''):
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
@simpleinput
|
||||
@register_render_function
|
||||
def imageinput(element, value, status, msg=''):
|
||||
'''
|
||||
Clickable image as an input field. Element should specify the image source, height, and width, eg
|
||||
|
||||
@@ -47,10 +47,10 @@ def compare_with_tolerance(v1, v2, tol):
|
||||
return abs(v1-v2) <= tolerance
|
||||
|
||||
class GenericResponse(object):
|
||||
__metaclass__=abc.ABCMeta
|
||||
__metaclass__=abc.ABCMeta # abc = Abstract Base Class
|
||||
|
||||
@abc.abstractmethod
|
||||
def grade(self, student_answers):
|
||||
def get_score(self, student_answers):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
@@ -61,7 +61,7 @@ class GenericResponse(object):
|
||||
def preprocess_response(self):
|
||||
pass
|
||||
|
||||
#Every response type needs methods "grade" and "get_answers"
|
||||
#Every response type needs methods "get_score" and "get_answers"
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
@@ -95,7 +95,7 @@ class MultipleChoiceResponse(GenericResponse):
|
||||
raise Exception("should have exactly one choice group per multiplechoicceresponse")
|
||||
self.answer_id=self.answer_id[0]
|
||||
|
||||
def grade(self, student_answers):
|
||||
def get_score(self, student_answers):
|
||||
if self.answer_id in student_answers and student_answers[self.answer_id] in self.correct_choices:
|
||||
return {self.answer_id:'correct'}
|
||||
else:
|
||||
@@ -132,7 +132,7 @@ class TrueFalseResponse(MultipleChoiceResponse):
|
||||
else:
|
||||
choice.set("name", "choice_"+choice.get("name"))
|
||||
|
||||
def grade(self, student_answers):
|
||||
def get_score(self, student_answers):
|
||||
correct = set(self.correct_choices)
|
||||
answers = set(student_answers.get(self.answer_id, []))
|
||||
|
||||
@@ -162,7 +162,7 @@ class OptionResponse(GenericResponse):
|
||||
print '[courseware.capa.responsetypes.OR.init] answer_fields=%s' % (self.answer_fields)
|
||||
self.context = context
|
||||
|
||||
def grade(self, student_answers):
|
||||
def get_score(self, student_answers):
|
||||
cmap = {}
|
||||
amap = self.get_answers()
|
||||
for aid in amap:
|
||||
@@ -194,7 +194,7 @@ class NumericalResponse(GenericResponse):
|
||||
except Exception, err:
|
||||
self.answer_id = None
|
||||
|
||||
def grade(self, student_answers):
|
||||
def get_score(self, student_answers):
|
||||
''' Display HTML for a numeric response '''
|
||||
student_answer = student_answers[self.answer_id]
|
||||
try:
|
||||
@@ -300,7 +300,7 @@ def sympy_check2():
|
||||
else:
|
||||
self.code = answer.text
|
||||
|
||||
def grade(self, student_answers):
|
||||
def get_score(self, student_answers):
|
||||
'''
|
||||
student_answers is a dict with everything from request.POST, but with the first part
|
||||
of each key removed (the string before the first "_").
|
||||
@@ -363,7 +363,7 @@ def sympy_check2():
|
||||
print "oops in customresponse (cfn) error %s" % err
|
||||
# print "context = ",self.context
|
||||
print traceback.format_exc()
|
||||
if settings.DEBUG: print "[courseware.capa.responsetypes.customresponse.grade] ret = ",ret
|
||||
if settings.DEBUG: print "[courseware.capa.responsetypes.customresponse.get_score] ret = ",ret
|
||||
if type(ret)==dict:
|
||||
correct[0] = 'correct' if ret['ok'] else 'incorrect'
|
||||
msg = ret['msg']
|
||||
@@ -428,7 +428,7 @@ class ExternalResponse(GenericResponse):
|
||||
|
||||
self.tests = xml.get('answer')
|
||||
|
||||
def grade(self, student_answers):
|
||||
def get_score(self, student_answers):
|
||||
submission = [student_answers[k] for k in sorted(self.answer_ids)]
|
||||
self.context.update({'submission':submission})
|
||||
|
||||
@@ -504,7 +504,7 @@ class FormulaResponse(GenericResponse):
|
||||
self.case_sensitive = False
|
||||
|
||||
|
||||
def grade(self, student_answers):
|
||||
def get_score(self, student_answers):
|
||||
variables=self.samples.split('@')[0].split(',')
|
||||
numsamples=int(self.samples.split('@')[1].split('#')[1])
|
||||
sranges=zip(*map(lambda x:map(float, x.split(",")),
|
||||
@@ -566,7 +566,7 @@ class SchematicResponse(GenericResponse):
|
||||
else:
|
||||
self.code = answer.text
|
||||
|
||||
def grade(self, student_answers):
|
||||
def get_score(self, student_answers):
|
||||
from capa_problem import global_context
|
||||
submission = [json.loads(student_answers[k]) for k in sorted(self.answer_ids)]
|
||||
self.context.update({'submission':submission})
|
||||
@@ -605,7 +605,7 @@ class ImageResponse(GenericResponse):
|
||||
self.ielements = xml.findall('imageinput')
|
||||
self.answer_ids = [ie.get('id') for ie in self.ielements]
|
||||
|
||||
def grade(self, student_answers):
|
||||
def get_score(self, student_answers):
|
||||
correct_map = {}
|
||||
expectedset = self.get_answers()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user