Commit b64fe5c5 by Felix Sun

Finished prototype of hint moderation view. Began re-writing tests of the…

Finished prototype of hint moderation view.  Began re-writing tests of the crowdsource hinter module.  (Old tests no longer cover all the code, now that moderation has been added.)
parent 100f6bf1
...@@ -26,6 +26,8 @@ from django.core.urlresolvers import reverse ...@@ -26,6 +26,8 @@ from django.core.urlresolvers import reverse
from courseware.courses import get_course_with_access from courseware.courses import get_course_with_access
from courseware.models import XModuleContentField from courseware.models import XModuleContentField
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
@ensure_csrf_cookie @ensure_csrf_cookie
...@@ -48,6 +50,10 @@ def hint_manager(request, course_id): ...@@ -48,6 +50,10 @@ def hint_manager(request, course_id):
pass pass
if request.POST['op'] == 'change votes': if request.POST['op'] == 'change votes':
change_votes(request, course_id, field) change_votes(request, course_id, field)
if request.POST['op'] == 'add hint':
add_hint(request, course_id, field)
if request.POST['op'] == 'approve':
approve(request, course_id, field)
rendered_html = render_to_string('courseware/hint_manager_inner.html', get_hints(request, course_id, field)) rendered_html = render_to_string('courseware/hint_manager_inner.html', get_hints(request, course_id, field))
return HttpResponse(json.dumps({'success': True, 'contents': rendered_html})) return HttpResponse(json.dumps({'success': True, 'contents': rendered_html}))
...@@ -59,7 +65,6 @@ def get_hints(request, course_id, field): ...@@ -59,7 +65,6 @@ def get_hints(request, course_id, field):
# DON'T TRUST field attributes that come from ajax. Use an if statement # DON'T TRUST field attributes that come from ajax. Use an if statement
# to make sure the field is valid before plugging into functions. # to make sure the field is valid before plugging into functions.
out = ''
if field == 'mod_queue': if field == 'mod_queue':
other_field = 'hints' other_field = 'hints'
field_label = 'Hints Awaiting Moderation' field_label = 'Hints Awaiting Moderation'
...@@ -71,32 +76,40 @@ def get_hints(request, course_id, field): ...@@ -71,32 +76,40 @@ def get_hints(request, course_id, field):
chopped_id = '/'.join(course_id.split('/')[:-1]) chopped_id = '/'.join(course_id.split('/')[:-1])
chopped_id = re.escape(chopped_id) chopped_id = re.escape(chopped_id)
all_hints = XModuleContentField.objects.filter(field_name=field, definition_id__regex=chopped_id) all_hints = XModuleContentField.objects.filter(field_name=field, definition_id__regex=chopped_id)
big_out_dict = {}
name_dict = {}
for problem in all_hints: for problem in all_hints:
out += '<h2> Problem: ' + problem.definition_id + '</h2>' loc = Location(problem.definition_id)
for answer, hint_dict in json.loads(problem.value).items(): try:
out += '<h4> Answer: ' + answer + '</h4>' descriptor = modulestore().get_items(loc)[0]
for pk, hint in hint_dict.items(): except IndexError:
out += '<p data-problem="'\ # Sometimes, the problem is no longer in the course. Just
+ problem.definition_id + '" data-pk="' + str(pk) + '" data-answer="'\ # don't include said problem.
+ answer + '">' continue
out += '<input class="hint-select" type="checkbox"/>' + hint[0] + \ name_dict[problem.definition_id] = descriptor.get_children()[0].display_name
'<br /> Votes: <input type="text" class="votes" value="' + str(hint[1]) + '"></input>' # Answer list contains (answer, dict_of_hints) tuples.
out += '</p>'
out += '''<h4> Add a hint to this problem </h4> def answer_sorter(thing):
Answer (exact formatting): '''
<input type="text" id="new-hint-answer-''' + problem.definition_id \ thing is a tuple, where thing[0] contains an answer, and thing[1] contains
+ '"/> <br /> Hint: <br /><textarea cols="50" style="height:200px" id="new-hint-' + problem.definition_id \ a dict of hints. This function returns an index based on thing[0], which
+ '"></textarea> <br /> <button class="submit-new-hint" data-problem="' + problem.definition_id \ is used as a key to sort the list of things.
+ '"> Submit </button><br />' '''
try:
return float(thing[0])
out += '<button id="hint-delete"> Delete selected </button> <button id="update-votes"> Update votes </button>' except ValueError:
render_dict = {'out': out, # Put all non-numerical answers first.
'field': field, return float('-inf')
answer_list = sorted(json.loads(problem.value).items(), key=answer_sorter)
big_out_dict[problem.definition_id] = answer_list
render_dict = {'field': field,
'other_field': other_field, 'other_field': other_field,
'field_label': field_label, 'field_label': field_label,
'other_field_label': other_field_label, 'other_field_label': other_field_label,
'all_hints': all_hints} 'all_hints': big_out_dict,
'id_to_name': name_dict}
return render_dict return render_dict
def delete_hints(request, course_id, field): def delete_hints(request, course_id, field):
...@@ -132,6 +145,80 @@ def change_votes(request, course_id, field): ...@@ -132,6 +145,80 @@ def change_votes(request, course_id, field):
this_problem.value = json.dumps(problem_dict) this_problem.value = json.dumps(problem_dict)
this_problem.save() this_problem.save()
def add_hint(request, course_id, field):
'''
Add a new hint. POST:
op
field
problem - The problem id
answer - The answer to which a hint will be added
hint - The text of the hint
'''
problem_id = request.POST['problem']
answer = request.POST['answer']
hint_text = request.POST['hint']
this_problem = XModuleContentField.objects.get(field_name=field, definition_id=problem_id)
hint_pk_entry = XModuleContentField.objects.get(field_name='hint_pk', definition_id=problem_id)
this_pk = int(hint_pk_entry.value)
hint_pk_entry.value = this_pk + 1
hint_pk_entry.save()
problem_dict = json.loads(this_problem.value)
if answer not in problem_dict:
problem_dict[answer] = {}
problem_dict[answer][this_pk] = [hint_text, 1]
this_problem.value = json.dumps(problem_dict)
this_problem.save()
def approve(request, course_id, field):
'''
Approve a list of hints, moving them from the mod_queue to the real
hint list. POST:
op, field
(some number) -> [problem, answer, pk]
'''
for key in request.POST:
if key == 'op' or key == 'field':
continue
problem_id, answer, pk = request.POST.getlist(key)
# Can be optimized - sort the delete list by problem_id, and load each problem
# from the database only once.
problem_in_mod = XModuleContentField.objects.get(field_name=field, definition_id=problem_id)
problem_dict = json.loads(problem_in_mod.value)
hint_to_move = problem_dict[answer][pk]
del problem_dict[answer][pk]
problem_in_mod.value = json.dumps(problem_dict)
problem_in_mod.save()
problem_in_hints = XModuleContentField.objects.get(field_name='hints', definition_id=problem_id)
problem_dict = json.loads(problem_in_hints.value)
if answer not in problem_dict:
problem_dict[answer] = {}
problem_dict[answer][pk] = hint_to_move
problem_in_hints.value = json.dumps(problem_dict)
problem_in_hints.save()
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
data_dict[i] = [$(this).parent().attr("data-problem"), data_dict[i] = [$(this).parent().attr("data-problem"),
$(this).parent().attr("data-answer"), $(this).parent().attr("data-answer"),
$(this).parent().attr("data-pk")]; $(this).parent().attr("data-pk")];
i += 1
} }
}); });
$.ajax(window.location.pathname, { $.ajax(window.location.pathname, {
...@@ -64,6 +65,40 @@ ...@@ -64,6 +65,40 @@
}); });
}); });
$(".submit-new-hint").click(function(){
problem_name = $(this).data("problem");
hint_text = $(".submit-hint-text").filter('*[data-problem="'+problem_name+'"]').val();
hint_answer = $(".submit-hint-answer").filter('*[data-problem="'+problem_name+'"]').val();
data_dict = {'op': 'add hint',
'field': field,
'problem': problem_name,
'answer': hint_answer,
'hint': hint_text};
$.ajax(window.location.pathname, {
type: "POST",
data: data_dict,
success: update_contents
});
});
$("#approve").click(function(){
var data_dict = {'op': 'approve',
'field': field}
var i = 1
$(".hint-select").each(function(){
if ($(this).is(":checked")) {
data_dict[i] = [$(this).parent().attr("data-problem"),
$(this).parent().attr("data-answer"),
$(this).parent().attr("data-pk")];
i += 1
}
});
$.ajax(window.location.pathname, {
type: "POST",
data: data_dict,
success: update_contents
});
});
} }
$(document).ready(setup); $(document).ready(setup);
......
<%block name="main"> <%block name="main">
<div id="field-label" style="display:none"> ${field} </div> <div id="field-label" style="display:none">${field}</div>
<h1> ${field_label} </h1> <h1> ${field_label} </h1>
Switch to <a id="switch-fields" other-field="${other_field}"> ${other_field_label} </a> Switch to <a id="switch-fields" other-field="${other_field}">${other_field_label}</a>
% for problem in all_hints: % for definition_id in all_hints:
<h2> Problem: ${problem.definition_id} </h2> <h2> Problem: ${id_to_name[definition_id]} </h2>
<% % for answer, hint_dict in all_hints[definition_id]:
import json % if len(hint_dict) > 0:
loaded_json = json.loads(problem.value).items() <h4> Answer: ${answer} </h4><div style="background-color:#EEEEEE">
%> % endif
% for answer, hint_dict in loaded_json:
<h4> Answer: ${answer} </h4>
% for pk, hint in hint_dict.items(): % for pk, hint in hint_dict.items():
<p data-problem="${problem.definition_id}" data-pk="${pk}" data-answer="${answer}"> <p data-problem="${definition_id}" data-pk="${pk}" data-answer="${answer}">
<input class="hint-select" type="checkbox"/> ${hint[0]} <input class="hint-select" type="checkbox"/> ${hint[0]}
<br /> <br />
Votes: <input type="text" class="votes" value="${str(hint[1])}"></input> Votes: <input type="text" class="votes" value="${str(hint[1])}" style="font-size:12px; height:20px; width:50px"></input>
<br /><br />
</p> </p>
% endfor % endfor
% if len(hint_dict) > 0:
</div><br />
% endif
% endfor % endfor
<h4> Add a hint to this problem </h4> <h4> Add a hint to this problem </h4>
Answer (exact formatting): <h4> Answer: </h4>
<input type="text" id="new-hint-answer-${problem.definition_id}"/> <input type="text" class="submit-hint-answer" data-problem="${definition_id}"/>
(Be sure to format your answer in the same way as the other answers you see here.)
<br /> <br />
Hint: <br /> Hint: <br />
<textarea cols="50" style="height:200px" id="new-hint-${problem.definition_id}"></textarea> <textarea cols="50" style="height:200px" class="submit-hint-text" data-problem="${definition_id}"></textarea>
<br /> <br />
<button class="submit-new-hint" data-problem="${problem.definition_id}"> Submit </button> <button class="submit-new-hint" data-problem="${definition_id}"> Submit </button>
<br /> <br />
% endfor % endfor
<button id="hint-delete"> Delete selected </button> <button id="update-votes"> Update votes </button> <button id="hint-delete"> Delete selected </button> <button id="update-votes"> Update votes </button>
% if field == 'mod_queue':
<button id="approve"> Approve selected </button>
% endif
</%block> </%block>
\ No newline at end of file
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