Commit e6067d88 by Felix Sun

Addressed PR comments.

Fixed coffeescript event logging error.

Fixed crowdsourced hinter dependence on self.field.append() not working.
parent 69fbe77d
...@@ -923,8 +923,8 @@ class NumericalResponse(LoncapaResponse): ...@@ -923,8 +923,8 @@ class NumericalResponse(LoncapaResponse):
with this problem's tolerance. with this problem's tolerance.
""" """
return compare_with_tolerance( return compare_with_tolerance(
evaluator(dict(), dict(), a), evaluator({}, {}, a),
evaluator(dict(), dict(), b), evaluator({}, {}, b),
self.tolerance self.tolerance
) )
......
...@@ -7,6 +7,7 @@ Currently experimental - not for instructor use, yet. ...@@ -7,6 +7,7 @@ Currently experimental - not for instructor use, yet.
import logging import logging
import json import json
import random import random
import copy
from pkg_resources import resource_string from pkg_resources import resource_string
...@@ -82,17 +83,17 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule): ...@@ -82,17 +83,17 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
log.exception('Unable to find a capa problem child.') log.exception('Unable to find a capa problem child.')
return return
self.is_formula = (type(responder) == FormulaResponse) self.is_formula = isinstance(self, FormulaResponse)
if self.is_formula: if self.is_formula:
self.answer_to_str = self.formula_answer_to_str self.answer_to_str = self.formula_answer_to_str
else: else:
self.answer_to_str = self.numerical_answer_to_str self.answer_to_str = self.numerical_answer_to_str
# compare_answer is expected to return whether its two inputs are close enough # compare_answer is expected to return whether its two inputs are close enough
# to be equal, or raise a StudentInputError if one of the inputs is malformatted. # to be equal, or raise a StudentInputError if one of the inputs is malformatted.
try: if hasattr(responder, 'compare_answer') and hasattr(responder, 'validate_answer'):
self.compare_answer = responder.compare_answer self.compare_answer = responder.compare_answer
self.validate_answer = responder.validate_answer self.validate_answer = responder.validate_answer
except AttributeError: else:
# This response type is not supported! # This response type is not supported!
log.exception('Response type not supported for hinting: ' + str(responder)) log.exception('Response type not supported for hinting: ' + str(responder))
...@@ -199,43 +200,43 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule): ...@@ -199,43 +200,43 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
if answer not in self.user_submissions: if answer not in self.user_submissions:
self.user_submissions += [answer] self.user_submissions += [answer]
# Next, find all of the hints that could possibly go with this answer.
# Make a local copy of self.hints - this means we only need to do one json unpacking.
# (This is because xblocks storage makes the following command a deep copy.)
local_hints = self.hints
# For all answers similar enough to our own, accumulate all hints together. # For all answers similar enough to our own, accumulate all hints together.
# Also track the original answer of each hint. # Also track the original answer of each hint.
matching_answers = self.get_matching_answers(answer) matching_answers = self.get_matching_answers(answer)
matching_hints = {} matching_hints = {}
for matching_answer in matching_answers: for matching_answer in matching_answers:
temp_dict = local_hints[matching_answer] temp_dict = copy.deepcopy(self.hints[matching_answer])
for key, value in temp_dict.items(): for key, value in temp_dict.items():
# Each value now has hint, votes, matching_answer. # Each value now has hint, votes, matching_answer.
temp_dict[key] = value + [matching_answer] temp_dict[key] = value + [matching_answer]
matching_hints.update(local_hints[matching_answer]) matching_hints.update(temp_dict)
# matching_hints now maps pk's to lists of [hint, votes, matching_answer] # matching_hints now maps pk's to lists of [hint, votes, matching_answer]
# Finally, randomly choose a subset of matching_hints to actually show. # Finally, randomly choose a subset of matching_hints to actually show.
if len(matching_hints) == 0: if not matching_hints:
# No hints to give. Return. # No hints to give. Return.
return return
# Get the top hint, plus two random hints. # Get the top hint, plus two random hints.
n_hints = len(matching_hints) n_hints = len(matching_hints)
hints = [] hints = []
best_hint_index = max(matching_hints, key=lambda key: matching_hints[key][1]) # max(dict) returns the maximum key in dict.
# The key function takes each pk, and returns the number of votes for the
# hint with that pk.
best_hint_index = max(matching_hints, key=lambda pk: matching_hints[pk][1])
hints.append(matching_hints[best_hint_index][0]) hints.append(matching_hints[best_hint_index][0])
best_hint_answer = matching_hints[best_hint_index][2] best_hint_answer = matching_hints[best_hint_index][2]
# The brackets surrounding the index are for backwards compatability purposes. # The brackets surrounding the index are for backwards compatability purposes.
# (It used to be that each answer was paired with multiple hints in a list.) # (It used to be that each answer was paired with multiple hints in a list.)
self.previous_answers += [[best_hint_answer, [best_hint_index]]] self.previous_answers += [[best_hint_answer, [best_hint_index]]]
for i in xrange(min(2, n_hints-1)): for i in xrange(min(2, n_hints - 1)):
# Keep making random hints until we hit a target, or run out. # Keep making random hints until we hit a target, or run out.
go_on = False while True:
while not go_on: # random.choice randomly chooses an element from its input list.
# (We then unpack the item, in this case data for a hint.)
(hint_index, (rand_hint, votes, hint_answer)) =\ (hint_index, (rand_hint, votes, hint_answer)) =\
random.choice(matching_hints.items()) random.choice(matching_hints.items())
if not rand_hint in hints: if rand_hint not in hints:
go_on = True break
hints.append(rand_hint) hints.append(rand_hint)
self.previous_answers += [[hint_answer, [hint_index]]] self.previous_answers += [[hint_answer, [hint_index]]]
return {'hints': hints, return {'hints': hints,
...@@ -297,7 +298,7 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule): ...@@ -297,7 +298,7 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
ans = data['answer'] ans = data['answer']
if not self.validate_answer(ans): if not self.validate_answer(ans):
# Uh oh. Invalid answer. # Uh oh. Invalid answer.
log.exception('Failure in hinter tally_vote: Unable to parse answer: ' + ans) log.exception('Failure in hinter tally_vote: Unable to parse answer: {ans}'.format(ans=ans))
return {'error': 'Failure in voting!'} return {'error': 'Failure in voting!'}
hint_pk = str(data['hint']) hint_pk = str(data['hint'])
# We use temp_dict because we need to do a direct write for the database to update. # We use temp_dict because we need to do a direct write for the database to update.
...@@ -305,8 +306,8 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule): ...@@ -305,8 +306,8 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
try: try:
temp_dict[ans][hint_pk][1] += 1 temp_dict[ans][hint_pk][1] += 1
except KeyError: except KeyError:
log.exception('Failure in hinter tally_vote: User voted for non-existant hint: Answer=' + log.exception('''Failure in hinter tally_vote: User voted for non-existant hint:
ans + ' pk=' + hint_pk) Answer={ans} pk={hint_pk}'''.format(ans=ans, hint_pk=hint_pk))
return {'error': 'Failure in voting!'} return {'error': 'Failure in voting!'}
self.hints = temp_dict self.hints = temp_dict
# Don't let the user vote again! # Don't let the user vote again!
...@@ -317,13 +318,13 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule): ...@@ -317,13 +318,13 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
hint_and_votes = [] hint_and_votes = []
for answer, vote_pk in pk_list: for answer, vote_pk in pk_list:
if not self.validate_answer(answer): if not self.validate_answer(answer):
log.exception('In hinter tally_vote, couldn\'t parse ' + answer) log.exception('In hinter tally_vote, couldn\'t parse {ans}'.format(ans=answer))
continue continue
try: try:
hint_and_votes.append(temp_dict[answer][str(vote_pk)]) hint_and_votes.append(temp_dict[answer][str(vote_pk)])
except KeyError: except KeyError:
log.exception('In hinter tally_vote, couldn\'t find: ' log.exception('In hinter tally_vote, couldn\'t find: {ans}, {vote_pk}'.format(
+ answer + ', ' + str(vote_pk)) ans=answer, vote_pk=str(vote_pk)))
hint_and_votes.sort(key=lambda pair: pair[1], reverse=True) hint_and_votes.sort(key=lambda pair: pair[1], reverse=True)
# Reset self.previous_answers and user_submissions. # Reset self.previous_answers and user_submissions.
...@@ -345,7 +346,8 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule): ...@@ -345,7 +346,8 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
hint = escape(data['hint']) hint = escape(data['hint'])
answer = data['answer'] answer = data['answer']
if not self.validate_answer(answer): if not self.validate_answer(answer):
log.exception('Failure in hinter submit_hint: Unable to parse answer: ' + answer) log.exception('Failure in hinter submit_hint: Unable to parse answer: {ans}'.format(
ans=answer))
return {'error': 'Could not submit answer'} return {'error': 'Could not submit answer'}
# Only allow a student to vote or submit a hint once. # Only allow a student to vote or submit a hint once.
if self.user_voted: if self.user_voted:
......
...@@ -247,6 +247,7 @@ class @Problem ...@@ -247,6 +247,7 @@ class @Problem
@updateProgress response @updateProgress response
else else
@gentle_alert response.success @gentle_alert response.success
Logger.log 'problem_graded', [@answers, response.contents], @url
if not abort_submission if not abort_submission
$.ajaxWithPrefix("#{@url}/problem_check", settings) $.ajaxWithPrefix("#{@url}/problem_check", settings)
......
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