Commit d95a5e46 by Piotr Mitros

Modular refactor: Better names (grade to get_score, etc.

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