Moved the hinter rendering onto a mako template.
Hinter now displays vote count after voting. Began testing templates.
This commit is contained in:
committed by
Carlos Andrés Rocha
parent
4a406901b3
commit
d57380dd2e
@@ -23,31 +23,31 @@ log = logging.getLogger(__name__)
|
||||
|
||||
class CrowdsourceHinterFields(object):
|
||||
has_children = True
|
||||
hints = Dict(help='''A dictionary mapping answers to lists of [hint, number_of_votes] pairs.
|
||||
''', scope=Scope.content, default= {})
|
||||
hints = Dict(help="""A dictionary mapping answers to lists of [hint, number_of_votes] pairs.
|
||||
""", scope=Scope.content, default= {})
|
||||
|
||||
previous_answers = List(help='''A list of previous answers this student made to this problem.
|
||||
previous_answers = List(help="""A list of previous answers this student made to this problem.
|
||||
Of the form (answer, (hint_id_1, hint_id_2, hint_id_3)) for each problem. hint_id's are
|
||||
None if the hint was not given.''',
|
||||
None if the hint was not given.""",
|
||||
scope=Scope.user_state, default=[])
|
||||
|
||||
user_voted = Boolean(help='Specifies if the user has voted on this problem or not.',
|
||||
scope=Scope.user_state, default=False)
|
||||
|
||||
moderate = String(help='''If True, then all hints must be approved by staff before
|
||||
moderate = String(help="""If True, then all hints must be approved by staff before
|
||||
becoming visible.
|
||||
This field is automatically populated from the xml metadata.''', scope=Scope.content,
|
||||
This field is automatically populated from the xml metadata.""", scope=Scope.content,
|
||||
default='False')
|
||||
|
||||
mod_queue = Dict(help='''Contains hints that have not been approved by the staff yet. Structured
|
||||
identically to the hints dictionary.''', scope=Scope.content, default={})
|
||||
mod_queue = Dict(help="""Contains hints that have not been approved by the staff yet. Structured
|
||||
identically to the hints dictionary.""", scope=Scope.content, default={})
|
||||
|
||||
hint_pk = Integer(help='Used to index hints.', scope=Scope.content, default=0)
|
||||
|
||||
|
||||
class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
|
||||
''' An Xmodule that makes crowdsourced hints.
|
||||
'''
|
||||
""" An Xmodule that makes crowdsourced hints.
|
||||
"""
|
||||
icon_class = 'crowdsource_hinter'
|
||||
|
||||
js = {'coffee': [resource_string(__name__, 'js/src/crowdsource_hinter/display.coffee'),
|
||||
@@ -61,10 +61,10 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
|
||||
|
||||
|
||||
def get_html(self):
|
||||
'''
|
||||
"""
|
||||
Does a regular expression find and replace to change the AJAX url.
|
||||
- Dependent on lon-capa problem.
|
||||
'''
|
||||
"""
|
||||
# Reset the user vote, for debugging only! Remove for prod.
|
||||
self.user_voted = False
|
||||
# You are invited to guess what the lines below do :)
|
||||
@@ -82,10 +82,10 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
|
||||
return out
|
||||
|
||||
def capa_make_answer_hashable(self, answer):
|
||||
'''
|
||||
"""
|
||||
Capa answer format: dict[problem name] -> [list of answers]
|
||||
Output format: ((problem name, (answers)))
|
||||
'''
|
||||
"""
|
||||
out = []
|
||||
for problem, a in answer.items():
|
||||
out.append((problem, tuple(a)))
|
||||
@@ -93,18 +93,18 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
|
||||
|
||||
|
||||
def ans_to_text(self, answer):
|
||||
'''
|
||||
"""
|
||||
Converts capa answer format to a string representation
|
||||
of the answer.
|
||||
-Lon-capa dependent.
|
||||
'''
|
||||
"""
|
||||
return str(float(answer.values()[0]))
|
||||
|
||||
|
||||
def handle_ajax(self, dispatch, get):
|
||||
'''
|
||||
"""
|
||||
This is the landing method for AJAX calls.
|
||||
'''
|
||||
"""
|
||||
if dispatch == 'get_hint':
|
||||
out = self.get_hint(get)
|
||||
if dispatch == 'get_feedback':
|
||||
@@ -122,33 +122,35 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
|
||||
|
||||
|
||||
def get_hint(self, get):
|
||||
'''
|
||||
"""
|
||||
The student got the incorrect answer found in get. Give him a hint.
|
||||
'''
|
||||
"""
|
||||
answer = self.ans_to_text(get)
|
||||
# Look for a hint to give.
|
||||
if (answer not in self.hints) or (len(self.hints[answer]) == 0):
|
||||
# Make a local copy of self.hints - this means we only need to do one json unpacking.
|
||||
local_hints = self.hints
|
||||
if (answer not in local_hints) or (len(local_hints[answer]) == 0):
|
||||
# No hints to give. Return.
|
||||
self.previous_answers += [[answer, [None, None, None]]]
|
||||
return
|
||||
# Get the top hint, plus two random hints.
|
||||
n_hints = len(self.hints[answer])
|
||||
best_hint_index = max(self.hints[answer], key=lambda key: self.hints[answer][key][1])
|
||||
best_hint = self.hints[answer][best_hint_index][0]
|
||||
if len(self.hints[answer]) == 1:
|
||||
n_hints = len(local_hints[answer])
|
||||
best_hint_index = max(local_hints[answer], key=lambda key: local_hints[answer][key][1])
|
||||
best_hint = local_hints[answer][best_hint_index][0]
|
||||
if len(local_hints[answer]) == 1:
|
||||
rand_hint_1 = ''
|
||||
rand_hint_2 = ''
|
||||
self.previous_answers += [[answer, [best_hint_index, None, None]]]
|
||||
elif n_hints == 2:
|
||||
best_hint = self.hints[answer].values()[0][0]
|
||||
best_hint_index = self.hints[answer].keys()[0]
|
||||
rand_hint_1 = self.hints[answer].values()[1][0]
|
||||
hint_index_1 = self.hints[answer].keys()[1]
|
||||
best_hint = local_hints[answer].values()[0][0]
|
||||
best_hint_index = local_hints[answer].keys()[0]
|
||||
rand_hint_1 = local_hints[answer].values()[1][0]
|
||||
hint_index_1 = local_hints[answer].keys()[1]
|
||||
rand_hint_2 = ''
|
||||
self.previous_answers += [[answer, [best_hint_index, hint_index_1, None]]]
|
||||
else:
|
||||
(hint_index_1, rand_hint_1), (hint_index_2, rand_hint_2) =\
|
||||
random.sample(self.hints[answer].items(), 2)
|
||||
random.sample(local_hints[answer].items(), 2)
|
||||
rand_hint_1 = rand_hint_1[0]
|
||||
rand_hint_2 = rand_hint_2[0]
|
||||
self.previous_answers += [(answer, (best_hint_index, hint_index_1, hint_index_2))]
|
||||
@@ -159,9 +161,9 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
|
||||
'answer': answer}
|
||||
|
||||
def get_feedback(self, get):
|
||||
'''
|
||||
"""
|
||||
The student got it correct. Ask him to vote on hints, or submit a hint.
|
||||
'''
|
||||
"""
|
||||
# The student got it right.
|
||||
# Did he submit at least one wrong answer?
|
||||
out = ''
|
||||
@@ -181,11 +183,10 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
|
||||
index_to_hints[i] = []
|
||||
index_to_answer[i] = answer
|
||||
if answer in self.hints:
|
||||
# Add each hint to the html string, with a vote button.
|
||||
for hint_id in hints_offered:
|
||||
if hint_id != None:
|
||||
try:
|
||||
index_to_hints[i].append((self.hints[answer][hint_id][0], hint_id))
|
||||
index_to_hints[i].append((self.hints[answer][str(hint_id)][0], hint_id))
|
||||
except KeyError:
|
||||
# Sometimes, the hint that a user saw will have been deleted by the instructor.
|
||||
continue
|
||||
@@ -194,12 +195,12 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
|
||||
|
||||
|
||||
def tally_vote(self, get):
|
||||
'''
|
||||
"""
|
||||
Tally a user's vote on his favorite hint.
|
||||
get:
|
||||
'answer': ans_no (index in previous_answers)
|
||||
'hint': hint_no
|
||||
'''
|
||||
"""
|
||||
if self.user_voted:
|
||||
return json.dumps({'contents': 'Sorry, but you have already voted!'})
|
||||
ans_no = int(get['answer'])
|
||||
@@ -211,19 +212,25 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
|
||||
self.hints = temp_dict
|
||||
# Don't let the user vote again!
|
||||
self.user_voted = True
|
||||
|
||||
# Return a list of how many votes each hint got.
|
||||
hint_and_votes = []
|
||||
for hint_no in self.previous_answers[ans_no][1]:
|
||||
if hint_no == None:
|
||||
continue
|
||||
hint_and_votes.append(temp_dict[answer][str(hint_no)])
|
||||
|
||||
# Reset self.previous_answers.
|
||||
self.previous_answers = []
|
||||
# In the future, return a list of how many votes each hint got, maybe?
|
||||
return {'message': 'Congrats, you\'ve voted!'}
|
||||
|
||||
return {'hint_and_votes': hint_and_votes}
|
||||
|
||||
def submit_hint(self, get):
|
||||
'''
|
||||
"""
|
||||
Take a hint submission and add it to the database.
|
||||
get:
|
||||
'answer': answer index in previous_answers
|
||||
'hint': text of the new hint that the user is adding
|
||||
'''
|
||||
"""
|
||||
# Do html escaping. Perhaps in the future do profanity filtering, etc. as well.
|
||||
hint = escape(get['hint'])
|
||||
answer = self.previous_answers[int(get['answer'])][0]
|
||||
@@ -251,11 +258,11 @@ class CrowdsourceHinterModule(CrowdsourceHinterFields, XModule):
|
||||
|
||||
|
||||
def delete_hint(self, answer, hint_id):
|
||||
'''
|
||||
"""
|
||||
From the answer, delete the hint with hint_id.
|
||||
Not designed to be accessed via POST request, for now.
|
||||
-LIKELY DEPRECATED.
|
||||
'''
|
||||
"""
|
||||
temp_hints = self.hints
|
||||
del temp_hints[answer][str(hint_id)]
|
||||
self.hints = temp_hints
|
||||
|
||||
@@ -13,7 +13,6 @@ class @Hinter
|
||||
# request.
|
||||
answers = data[0]
|
||||
response = data[1]
|
||||
console.debug(response)
|
||||
if response.search(/class="correct/) == -1
|
||||
# Incorrect. Get hints.
|
||||
$.postWithPrefix "#{@url}/get_hint", answers, (response) =>
|
||||
@@ -29,9 +28,9 @@ class @Hinter
|
||||
bind: =>
|
||||
window.update_schematics()
|
||||
@$('input.vote').click @vote
|
||||
@$('#feedback-select').change @feedback_ui_change
|
||||
@$('input.submit-hint').click @submit_hint
|
||||
@$('.custom-hint').click @clear_default_text
|
||||
@$('#answer-tabs').tabs({active: 0})
|
||||
|
||||
|
||||
vote: (eventObj) =>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from mock import Mock, patch
|
||||
import unittest
|
||||
import copy
|
||||
import random
|
||||
|
||||
import xmodule
|
||||
from xmodule.crowdsource_hinter import CrowdsourceHinterModule
|
||||
@@ -13,12 +14,12 @@ from . import get_test_system
|
||||
import json
|
||||
|
||||
class CHModuleFactory(object):
|
||||
'''
|
||||
"""
|
||||
Helps us make a CrowdsourceHinterModule with the specified internal
|
||||
state.
|
||||
'''
|
||||
"""
|
||||
|
||||
sample_problem_xml = '''
|
||||
sample_problem_xml = """
|
||||
<?xml version="1.0"?>
|
||||
<crowdsource_hinter>
|
||||
<problem display_name="Numerical Input" markdown="A numerical input problem accepts a line of text input from the student, and evaluates the input for correctness based on its numerical value. The answer is correct if it is within a specified numerical tolerance of the expected answer. Enter the number of fingers on a human hand: = 5 [explanation] If you look at your hand, you can count that you have five fingers. [explanation] " rerandomize="never" showanswer="finished">
|
||||
@@ -36,7 +37,7 @@ class CHModuleFactory(object):
|
||||
</solution>
|
||||
</problem>
|
||||
</crowdsource_hinter>
|
||||
'''
|
||||
"""
|
||||
|
||||
num = 0
|
||||
|
||||
@@ -91,102 +92,101 @@ class CHModuleFactory(object):
|
||||
|
||||
descriptor = Mock(weight="1")
|
||||
system = get_test_system()
|
||||
system.render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
module = CrowdsourceHinterModule(system, descriptor, model_data)
|
||||
|
||||
return module
|
||||
|
||||
|
||||
|
||||
|
||||
class CrowdsourceHinterTest(unittest.TestCase):
|
||||
'''
|
||||
"""
|
||||
In the below tests, '24.0' represents a wrong answer, and '42.5' represents
|
||||
a correct answer.
|
||||
'''
|
||||
"""
|
||||
|
||||
def test_gethint_0hint(self):
|
||||
'''
|
||||
"""
|
||||
Someone asks for a hint, when there's no hint to give.
|
||||
- Output should be blank.
|
||||
- New entry should be added to previous_answers
|
||||
'''
|
||||
"""
|
||||
m = CHModuleFactory.create()
|
||||
json_in = {'problem_name': '26.0'}
|
||||
json_out = json.loads(m.get_hint(json_in))['contents']
|
||||
self.assertTrue(json_out == ' ')
|
||||
out = m.get_hint(json_in)
|
||||
self.assertTrue(out == None)
|
||||
self.assertTrue(['26.0', [None, None, None]] in m.previous_answers)
|
||||
|
||||
def test_gethint_1hint(self):
|
||||
'''
|
||||
"""
|
||||
Someone asks for a hint, with exactly one hint in the database.
|
||||
Output should contain that hint.
|
||||
'''
|
||||
"""
|
||||
m = CHModuleFactory.create()
|
||||
json_in = {'problem_name': '25.0'}
|
||||
json_out = json.loads(m.get_hint(json_in))['contents']
|
||||
self.assertTrue('Really popular hint' in json_out)
|
||||
out = m.get_hint(json_in)
|
||||
self.assertTrue(out['best_hint'] == 'Really popular hint')
|
||||
|
||||
|
||||
def test_gethint_manyhints(self):
|
||||
'''
|
||||
"""
|
||||
Someone asks for a hint, with many matching hints in the database.
|
||||
- The top-rated hint should be returned.
|
||||
- Two other random hints should be returned.
|
||||
Currently, the best hint could be returned twice - need to fix this
|
||||
in implementation.
|
||||
'''
|
||||
"""
|
||||
m = CHModuleFactory.create()
|
||||
json_in = {'problem_name': '24.0'}
|
||||
json_out = json.loads(m.get_hint(json_in))['contents']
|
||||
print json_out
|
||||
self.assertTrue('Best hint' in json_out)
|
||||
self.assertTrue(json_out.count('hint') == 3)
|
||||
out = m.get_hint(json_in)
|
||||
self.assertTrue(out['best_hint'] == 'Best hint')
|
||||
self.assertTrue('rand_hint_1' in out)
|
||||
self.assertTrue('rand_hint_2' in out)
|
||||
|
||||
|
||||
def test_getfeedback_0wronganswers(self):
|
||||
'''
|
||||
"""
|
||||
Someone has gotten the problem correct on the first try.
|
||||
Output should be empty.
|
||||
'''
|
||||
"""
|
||||
m = CHModuleFactory.create(previous_answers=[])
|
||||
json_in = {'problem_name': '42.5'}
|
||||
json_out = json.loads(m.get_feedback(json_in))['contents']
|
||||
self.assertTrue(json_out == ' ')
|
||||
out = m.get_feedback(json_in)
|
||||
self.assertTrue(out == None)
|
||||
|
||||
def test_getfeedback_1wronganswer_nohints(self):
|
||||
'''
|
||||
"""
|
||||
Someone has gotten the problem correct, with one previous wrong
|
||||
answer. However, we don't actually have hints for this problem.
|
||||
There should be a dialog to submit a new hint.
|
||||
'''
|
||||
"""
|
||||
m = CHModuleFactory.create(previous_answers=[['26.0',[None, None, None]]])
|
||||
json_in = {'problem_name': '42.5'}
|
||||
json_out = json.loads(m.get_feedback(json_in))['contents']
|
||||
self.assertTrue('textarea' in json_out)
|
||||
self.assertTrue('Vote' not in json_out)
|
||||
out = m.get_feedback(json_in)
|
||||
print out['index_to_answer']
|
||||
self.assertTrue(out['index_to_hints'][0] == [])
|
||||
self.assertTrue(out['index_to_answer'][0] == '26.0')
|
||||
|
||||
|
||||
def test_getfeedback_1wronganswer_withhints(self):
|
||||
'''
|
||||
"""
|
||||
Same as above, except the user did see hints. There should be
|
||||
a voting dialog, with the correct choices, plus a hint submission
|
||||
dialog.
|
||||
'''
|
||||
"""
|
||||
m = CHModuleFactory.create(
|
||||
previous_answers=[
|
||||
['24.0', [0, 3, None]]],
|
||||
)
|
||||
json_in = {'problem_name': '42.5'}
|
||||
json_out = json.loads(m.get_feedback(json_in))['contents']
|
||||
self.assertTrue('Best hint' in json_out)
|
||||
self.assertTrue('Another hint' in json_out)
|
||||
self.assertTrue('third hint' not in json_out)
|
||||
self.assertTrue('textarea' in json_out)
|
||||
|
||||
out = m.get_feedback(json_in)
|
||||
self.assertTrue(len(out['index_to_hints'][0])==2)
|
||||
|
||||
def test_vote_nopermission(self):
|
||||
'''
|
||||
"""
|
||||
A user tries to vote for a hint, but he has already voted!
|
||||
Should not change any vote tallies.
|
||||
'''
|
||||
"""
|
||||
m = CHModuleFactory.create(user_voted=True)
|
||||
json_in = {'answer': 0, 'hint': 1}
|
||||
old_hints = copy.deepcopy(m.hints)
|
||||
@@ -195,21 +195,21 @@ class CrowdsourceHinterTest(unittest.TestCase):
|
||||
|
||||
|
||||
def test_vote_withpermission(self):
|
||||
'''
|
||||
"""
|
||||
A user votes for a hint.
|
||||
'''
|
||||
"""
|
||||
m = CHModuleFactory.create()
|
||||
json_in = {'answer': 0, 'hint': 3}
|
||||
json_out = json.loads(m.tally_vote(json_in))['contents']
|
||||
m.tally_vote(json_in)
|
||||
self.assertTrue(m.hints['24.0']['0'][1] == 40)
|
||||
self.assertTrue(m.hints['24.0']['3'][1] == 31)
|
||||
self.assertTrue(m.hints['24.0']['4'][1] == 20)
|
||||
|
||||
|
||||
def test_submithint_nopermission(self):
|
||||
'''
|
||||
"""
|
||||
A user tries to submit a hint, but he has already voted.
|
||||
'''
|
||||
"""
|
||||
m = CHModuleFactory.create(user_voted=True)
|
||||
json_in = {'answer': 1, 'hint': 'This is a new hint.'}
|
||||
print m.user_voted
|
||||
@@ -219,39 +219,37 @@ class CrowdsourceHinterTest(unittest.TestCase):
|
||||
|
||||
|
||||
def test_submithint_withpermission_new(self):
|
||||
'''
|
||||
"""
|
||||
A user submits a hint to an answer for which no hints
|
||||
exist yet.
|
||||
'''
|
||||
"""
|
||||
m = CHModuleFactory.create()
|
||||
json_in = {'answer': 1, 'hint': 'This is a new hint.'}
|
||||
m.submit_hint(json_in)
|
||||
# Make a hint request.
|
||||
json_in = {'problem name': '29.0'}
|
||||
json_out = json.loads(m.get_hint(json_in))['contents']
|
||||
self.assertTrue('This is a new hint.' in json_out)
|
||||
self.assertTrue('29.0' in m.hints)
|
||||
|
||||
|
||||
def test_submithint_withpermission_existing(self):
|
||||
'''
|
||||
"""
|
||||
A user submits a hint to an answer that has other hints
|
||||
already.
|
||||
'''
|
||||
"""
|
||||
m = CHModuleFactory.create(previous_answers = [['25.0', [1, None, None]]])
|
||||
json_in = {'answer': 0, 'hint': 'This is a new hint.'}
|
||||
m.submit_hint(json_in)
|
||||
# Make a hint request.
|
||||
json_in = {'problem name': '25.0'}
|
||||
json_out = json.loads(m.get_hint(json_in))['contents']
|
||||
self.assertTrue('This is a new hint.' in json_out)
|
||||
out = m.get_hint(json_in)
|
||||
self.assertTrue((out['best_hint'] == 'This is a new hint.')
|
||||
or (out['rand_hint_1'] == 'This is a new hint.'))
|
||||
|
||||
|
||||
def test_submithint_moderate(self):
|
||||
'''
|
||||
"""
|
||||
A user submits a hint, but moderation is on. The hint should
|
||||
show up in the mod_queue, not the public-facing hints
|
||||
dict.
|
||||
'''
|
||||
"""
|
||||
m = CHModuleFactory.create(moderate='True')
|
||||
json_in = {'answer': 1, 'hint': 'This is a new hint.'}
|
||||
m.submit_hint(json_in)
|
||||
@@ -259,9 +257,28 @@ class CrowdsourceHinterTest(unittest.TestCase):
|
||||
self.assertTrue('29.0' in m.mod_queue)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def test_template_gethint(self):
|
||||
"""
|
||||
Test the templates for get_hint.
|
||||
"""
|
||||
m = CHModuleFactory.create()
|
||||
|
||||
def fake_get_hint(get):
|
||||
"""
|
||||
Creates a rendering dictionary, with which we can test
|
||||
the templates.
|
||||
"""
|
||||
return {'best_hint': 'This is the best hint.',
|
||||
'rand_hint_1': 'A random hint',
|
||||
'rand_hint_2': 'Another random hint',
|
||||
'answer': '42.5'}
|
||||
|
||||
m.get_hint = fake_get_hint
|
||||
json_in = {'problem_name': '42.5'}
|
||||
out = json.loads(m.handle_ajax('get_hint', json_in))['contents']
|
||||
self.assertTrue('This is the best hint.' in out)
|
||||
self.assertTrue('A random hint' in out)
|
||||
self.assertTrue('Another random hint' in out)
|
||||
|
||||
|
||||
|
||||
|
||||
93
common/templates/hinter_display.html
Normal file
93
common/templates/hinter_display.html
Normal file
@@ -0,0 +1,93 @@
|
||||
## The hinter module passes in a field called ${op}, which determines which
|
||||
## sub-function to render.
|
||||
|
||||
|
||||
<%def name="get_hint()">
|
||||
% if best_hint != '':
|
||||
<h4> Other students who arrvied at the wrong answer of ${answer} recommend the following hints: </h4>
|
||||
<ul>
|
||||
<li> ${best_hint} </li>
|
||||
% endif
|
||||
% if rand_hint_1 != '':
|
||||
<li> ${rand_hint_1} </li>
|
||||
% endif
|
||||
% if rand_hint_2 != '':
|
||||
<li> ${rand_hint_2} </li>
|
||||
% endif
|
||||
</ul>
|
||||
</%def>
|
||||
|
||||
<%def name="get_feedback()">
|
||||
<i> Participation in the hinting system is strictly optional, and will not influence
|
||||
your grade. </i>
|
||||
<br />
|
||||
Help us improve our hinting system. Start by picking one of your previous incorrect answers from below:
|
||||
<br /><br />
|
||||
|
||||
<div id="answer-tabs">
|
||||
<ul>
|
||||
% for index, answer in index_to_answer.items():
|
||||
<li><a href="#previous-answer-${index}"> ${answer} </a></li>
|
||||
% endfor
|
||||
</ul>
|
||||
|
||||
% for index, answer in index_to_answer.items():
|
||||
<div class = "previous-answer" id="previous-answer-${index}">
|
||||
% if index in index_to_hints and len(index_to_hints[index]) > 0:
|
||||
Which hint was most helpful when you got the wrong answer of ${answer}?
|
||||
<br />
|
||||
% for hint_text, hint_pk in index_to_hints[index]:
|
||||
<input class="vote" data-answer="${index}" data-hintno="${hint_pk}" type="button" value="Vote">
|
||||
${hint_text}
|
||||
<br />
|
||||
% endfor
|
||||
Don't like any of the hints above? You can also submit your own.
|
||||
% else:
|
||||
Write a hint for other students who get the wrong answer of ${answer}.
|
||||
% endif
|
||||
Try to describe what concepts you misunderstood, or what mistake you made. Please don't
|
||||
give away the answer.
|
||||
<textarea cols="50" style="height:100px" class="custom-hint" id="custom-hint-${index}">
|
||||
What would you say to help someone who got this wrong answer?
|
||||
(Don't give away the answer, please.)
|
||||
</textarea>
|
||||
|
||||
<input class="submit-hint" data-answer="${index}" type="button" value="submit">
|
||||
</div>
|
||||
% endfor
|
||||
</div>
|
||||
|
||||
</%def>
|
||||
|
||||
<%def name="show_votes()">
|
||||
Thank you for voting!
|
||||
<br />
|
||||
% for hint, votes in hint_and_votes:
|
||||
<span style="color:green"> ${votes} votes. </span>
|
||||
${hint}
|
||||
<br />
|
||||
% endfor
|
||||
</%def>
|
||||
|
||||
<%def name="simple_message()">
|
||||
${message}
|
||||
</%def>
|
||||
|
||||
% if op == "get_hint":
|
||||
${get_hint()}
|
||||
% endif
|
||||
|
||||
% if op == "get_feedback":
|
||||
${get_feedback()}
|
||||
% endif
|
||||
|
||||
% if op == "submit_hint":
|
||||
${simple_message()}
|
||||
% endif
|
||||
|
||||
% if op == "vote":
|
||||
${show_votes()}
|
||||
% endif
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user