Commit e3f12607 by Arthur Barrett

add grading for annotationinput

parent 9d939cba
...@@ -988,27 +988,25 @@ class AnnotationInput(InputTypeBase): ...@@ -988,27 +988,25 @@ class AnnotationInput(InputTypeBase):
self.value = 'null' self.value = 'null'
def _find_options(self): def _find_options(self):
options = [] ''' Returns an array of dicts where each dict represents an option. '''
index = 0 elements = self.xml.findall('./options/option')
for option in self.xml.findall('./options/option'): return [{
options.append({
'id': index, 'id': index,
'description': option.text, 'description': option.text,
'score': option.get('score', 0) 'choice': option.get('choice')
}) } for (index, option) in enumerate(elements) ]
index += 1
return options
def _unpack_value(self): def _unpack(self, json_value):
unpacked_value = json.loads(self.value) ''' Unpacks the json input state into a dict. '''
if type(unpacked_value) != dict: d = json.loads(json_value)
unpacked_value = {} if type(d) != dict:
d = {}
comment_value = unpacked_value.get('comment', '') comment_value = d.get('comment', '')
if not isinstance(comment_value, basestring): if not isinstance(comment_value, basestring):
comment_value = '' comment_value = ''
options_value = unpacked_value.get('options', []) options_value = d.get('options', [])
if not isinstance(options_value, list): if not isinstance(options_value, list):
options_value = [] options_value = []
...@@ -1027,9 +1025,9 @@ class AnnotationInput(InputTypeBase): ...@@ -1027,9 +1025,9 @@ class AnnotationInput(InputTypeBase):
'options': self.options, 'options': self.options,
'return_to_annotation': self.return_to_annotation, 'return_to_annotation': self.return_to_annotation,
'debug': self.debug 'debug': self.debug
} }
unpacked_value = self._unpack_value()
extra_context.update(unpacked_value) extra_context.update(self._unpack(self.value))
return extra_context return extra_context
......
...@@ -1843,6 +1843,105 @@ class ImageResponse(LoncapaResponse): ...@@ -1843,6 +1843,105 @@ class ImageResponse(LoncapaResponse):
dict([(ie.get('id'), ie.get('regions')) for ie in self.ielements])) 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 # TEMPORARY: List of all response subclasses
# FIXME: To be replaced by auto-registration # FIXME: To be replaced by auto-registration
...@@ -1859,4 +1958,5 @@ __all__ = [CodeResponse, ...@@ -1859,4 +1958,5 @@ __all__ = [CodeResponse,
ChoiceResponse, ChoiceResponse,
MultipleChoiceResponse, MultipleChoiceResponse,
TrueFalseResponse, TrueFalseResponse,
JavascriptResponse] JavascriptResponse,
AnnotationResponse]
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
% if debug: % if debug:
<div class="debug-value"> <div class="debug-value">
Rendered with value:<br/> Rendered with value:<br/>
<pre>${value}</pre> <pre>${value|h}</pre>
Current input value:<br/> Current input value:<br/>
<input type="text" class="value" name="input_${id}" id="input_${id}" value="${value|h}" /> <input type="text" class="value" name="input_${id}" id="input_${id}" value="${value|h}" />
</div> </div>
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
<span class="unanswered" style="display:inline-block;" id="status_${id}"></span> <span class="unanswered" style="display:inline-block;" id="status_${id}"></span>
% elif status == 'correct': % elif status == 'correct':
<span class="correct" id="status_${id}"></span> <span class="correct" id="status_${id}"></span>
% elif status == 'partial':
<span class="partially_correct" id="status_${id}">Partially Correct</span>
% elif status == 'incorrect': % elif status == 'incorrect':
<span class="incorrect" id="status_${id}"></span> <span class="incorrect" id="status_${id}"></span>
% elif status == 'incomplete': % elif status == 'incomplete':
......
(function () { (function () {
var debug = true; var debug = false;
var module = { var module = {
debug: debug, debug: debug,
inputSelector: '.annotation-input', inputSelector: '.annotation-input',
tagSelector: '.tag', tagSelector: '.tag',
tagsSelector: '.tags',
commentSelector: 'textarea.comment', commentSelector: 'textarea.comment',
valueSelector: 'input.value', // stash tag selections and comment here as a JSON string... valueSelector: 'input.value', // stash tag selections and comment here as a JSON string...
singleSelect: true,
init: function() { init: function() {
var that = this; var that = this;
...@@ -38,15 +41,30 @@ ...@@ -38,15 +41,30 @@
target_value = $(e.target).data('id'); target_value = $(e.target).data('id');
if(!$(target_el).hasClass('selected')) { 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 { } else {
target_index = current_value.options.indexOf(target_value); if(this.singleSelect) {
if(target_index !== -1) { current_value.options = []
current_value.options.splice(target_index, 1); } 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); 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'); $(target_el).toggleClass('selected');
}, },
findValueEl: function(target_el) { findValueEl: function(target_el) {
......
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