add grading for annotationinput
This commit is contained in:
@@ -988,27 +988,25 @@ class AnnotationInput(InputTypeBase):
|
||||
self.value = 'null'
|
||||
|
||||
def _find_options(self):
|
||||
options = []
|
||||
index = 0
|
||||
for option in self.xml.findall('./options/option'):
|
||||
options.append({
|
||||
''' Returns an array of dicts where each dict represents an option. '''
|
||||
elements = self.xml.findall('./options/option')
|
||||
return [{
|
||||
'id': index,
|
||||
'description': option.text,
|
||||
'score': option.get('score', 0)
|
||||
})
|
||||
index += 1
|
||||
return options
|
||||
'choice': option.get('choice')
|
||||
} for (index, option) in enumerate(elements) ]
|
||||
|
||||
def _unpack_value(self):
|
||||
unpacked_value = json.loads(self.value)
|
||||
if type(unpacked_value) != dict:
|
||||
unpacked_value = {}
|
||||
def _unpack(self, json_value):
|
||||
''' Unpacks the json input state into a dict. '''
|
||||
d = json.loads(json_value)
|
||||
if type(d) != dict:
|
||||
d = {}
|
||||
|
||||
comment_value = unpacked_value.get('comment', '')
|
||||
comment_value = d.get('comment', '')
|
||||
if not isinstance(comment_value, basestring):
|
||||
comment_value = ''
|
||||
|
||||
options_value = unpacked_value.get('options', [])
|
||||
options_value = d.get('options', [])
|
||||
if not isinstance(options_value, list):
|
||||
options_value = []
|
||||
|
||||
@@ -1027,9 +1025,9 @@ class AnnotationInput(InputTypeBase):
|
||||
'options': self.options,
|
||||
'return_to_annotation': self.return_to_annotation,
|
||||
'debug': self.debug
|
||||
}
|
||||
unpacked_value = self._unpack_value()
|
||||
extra_context.update(unpacked_value)
|
||||
}
|
||||
|
||||
extra_context.update(self._unpack(self.value))
|
||||
|
||||
return extra_context
|
||||
|
||||
|
||||
@@ -1843,6 +1843,105 @@ class ImageResponse(LoncapaResponse):
|
||||
dict([(ie.get('id'), ie.get('regions')) for ie in self.ielements]))
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
class AnnotationResponse(LoncapaResponse):
|
||||
|
||||
response_tag = 'annotationresponse'
|
||||
allowed_inputfields = ['annotationinput']
|
||||
max_inputfields = 1
|
||||
default_scoring = { 'incorrect': 0, 'partial': 1, 'correct': 2 }
|
||||
|
||||
def setup_response(self):
|
||||
xml = self.xml
|
||||
self.points_map = self._get_points_map()
|
||||
self.answer_map = self._get_answer_map()
|
||||
|
||||
def _get_points_map(self):
|
||||
''' Returns a dict of option->scoring for each input. '''
|
||||
scoring = self.default_scoring
|
||||
choices = dict(zip(scoring.keys(), scoring.keys()))
|
||||
points_map = {}
|
||||
|
||||
for inputfield in self.inputfields:
|
||||
option_map = dict([(option['id'], {
|
||||
'correctness': choices.get(option['choice']),
|
||||
'points': scoring.get(option['choice'])
|
||||
}) for option in self._find_options(inputfield) ])
|
||||
|
||||
points_map[inputfield.get('id')] = option_map
|
||||
|
||||
return points_map
|
||||
|
||||
def _get_answer_map(self):
|
||||
''' Returns a dict of answers for each input.'''
|
||||
answer_map = {}
|
||||
for inputfield in self.inputfields:
|
||||
correct_option = self._find_option_with_choice(inputfield, 'correct')
|
||||
answer_map[inputfield.get('id')] = correct_option['description']
|
||||
return answer_map
|
||||
|
||||
def _find_options(self, inputfield):
|
||||
''' Returns an array of dicts where each dict represents an option. '''
|
||||
elements = inputfield.findall('./options/option')
|
||||
return [{
|
||||
'id': index,
|
||||
'description': option.text,
|
||||
'choice': option.get('choice')
|
||||
} for (index, option) in enumerate(elements) ]
|
||||
|
||||
def _find_option_with_choice(self, inputfield, choice):
|
||||
''' Returns the option with the given choice value, otherwise None. '''
|
||||
for option in self._find_options(inputfield):
|
||||
if option['choice'] == choice:
|
||||
return option
|
||||
|
||||
def _unpack(self, json_value):
|
||||
''' Unpacks a student response value submitted as JSON.'''
|
||||
d = json.loads(json_value)
|
||||
if type(d) != dict:
|
||||
d = {}
|
||||
|
||||
comment_value = d.get('comment', '')
|
||||
if not isinstance(d, basestring):
|
||||
comment_value = ''
|
||||
|
||||
options_value = d.get('options', [])
|
||||
if not isinstance(options_value, list):
|
||||
options_value = []
|
||||
|
||||
return {
|
||||
'options_value': options_value,
|
||||
'comment_value': comment_value
|
||||
}
|
||||
|
||||
def _get_submitted_option(self, student_answer):
|
||||
''' Return the single option that was selected, otherwise None.'''
|
||||
value = self._unpack(student_answer)
|
||||
options = value['options_value']
|
||||
if len(options) == 1:
|
||||
return options[0]
|
||||
return None
|
||||
|
||||
def get_score(self, student_answers):
|
||||
''' Returns a CorrectMap for the student answer, which may include
|
||||
partially correct answers.'''
|
||||
student_answer = student_answers[self.answer_id]
|
||||
student_option = self._get_submitted_option(student_answer)
|
||||
|
||||
scoring = self.points_map[self.answer_id]
|
||||
is_valid = student_option is not None and student_option in scoring.keys()
|
||||
|
||||
(correctness, points) = ('incorrect', None)
|
||||
if is_valid:
|
||||
correctness = scoring[student_option]['correctness']
|
||||
points = scoring[student_option]['points']
|
||||
|
||||
return CorrectMap(self.answer_id, correctness=correctness, npoints=points)
|
||||
|
||||
def get_answers(self):
|
||||
return self.answer_map
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# TEMPORARY: List of all response subclasses
|
||||
# FIXME: To be replaced by auto-registration
|
||||
|
||||
@@ -1859,4 +1958,5 @@ __all__ = [CodeResponse,
|
||||
ChoiceResponse,
|
||||
MultipleChoiceResponse,
|
||||
TrueFalseResponse,
|
||||
JavascriptResponse]
|
||||
JavascriptResponse,
|
||||
AnnotationResponse]
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
% if debug:
|
||||
<div class="debug-value">
|
||||
Rendered with value:<br/>
|
||||
<pre>${value}</pre>
|
||||
<pre>${value|h}</pre>
|
||||
Current input value:<br/>
|
||||
<input type="text" class="value" name="input_${id}" id="input_${id}" value="${value|h}" />
|
||||
</div>
|
||||
@@ -38,6 +38,8 @@
|
||||
<span class="unanswered" style="display:inline-block;" id="status_${id}"></span>
|
||||
% elif status == 'correct':
|
||||
<span class="correct" id="status_${id}"></span>
|
||||
% elif status == 'partial':
|
||||
<span class="partially_correct" id="status_${id}">Partially Correct</span>
|
||||
% elif status == 'incorrect':
|
||||
<span class="incorrect" id="status_${id}"></span>
|
||||
% elif status == 'incomplete':
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
(function () {
|
||||
var debug = true;
|
||||
var debug = false;
|
||||
|
||||
var module = {
|
||||
debug: debug,
|
||||
inputSelector: '.annotation-input',
|
||||
tagSelector: '.tag',
|
||||
tagsSelector: '.tags',
|
||||
commentSelector: 'textarea.comment',
|
||||
valueSelector: 'input.value', // stash tag selections and comment here as a JSON string...
|
||||
|
||||
singleSelect: true,
|
||||
|
||||
init: function() {
|
||||
var that = this;
|
||||
|
||||
@@ -38,15 +41,30 @@
|
||||
target_value = $(e.target).data('id');
|
||||
|
||||
if(!$(target_el).hasClass('selected')) {
|
||||
current_value.options.push(target_value);
|
||||
if(this.singleSelect) {
|
||||
current_value.options = [target_value]
|
||||
} else {
|
||||
current_value.options.push(target_value);
|
||||
}
|
||||
} else {
|
||||
target_index = current_value.options.indexOf(target_value);
|
||||
if(target_index !== -1) {
|
||||
current_value.options.splice(target_index, 1);
|
||||
if(this.singleSelect) {
|
||||
current_value.options = []
|
||||
} else {
|
||||
target_index = current_value.options.indexOf(target_value);
|
||||
if(target_index !== -1) {
|
||||
current_value.options.splice(target_index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.storeValue(value_el, current_value);
|
||||
|
||||
if(this.singleSelect) {
|
||||
$(target_el).closest(this.tagsSelector)
|
||||
.find(this.tagSelector)
|
||||
.not(target_el)
|
||||
.removeClass('selected')
|
||||
}
|
||||
$(target_el).toggleClass('selected');
|
||||
},
|
||||
findValueEl: function(target_el) {
|
||||
|
||||
Reference in New Issue
Block a user