Commit c602fdbf by solashirai Committed by Piotr Mitros

changes to flow of hinting/feedback mostly in place

parent e22d313f
...@@ -8,7 +8,7 @@ import pkg_resources ...@@ -8,7 +8,7 @@ import pkg_resources
import random import random
from xblock.core import XBlock from xblock.core import XBlock
from xblock.fields import Scope, Dict, List from xblock.fields import Scope, Dict, List, Boolean
from xblock.fragment import Fragment from xblock.fragment import Fragment
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -26,13 +26,10 @@ class CrowdsourceHinter(XBlock): ...@@ -26,13 +26,10 @@ class CrowdsourceHinter(XBlock):
# This is a list of incorrect answer submissions made by the student. this list is mostly used for # This is a list of incorrect answer submissions made by the student. this list is mostly used for
# feedback, to find which incorrect answer's hint a student voted on. # feedback, to find which incorrect answer's hint a student voted on.
WrongAnswers = List([], scope=Scope.user_state) WrongAnswers = List([], scope=Scope.user_state)
# This list will keep track of what student answers didn't have a hint to show them. This list is used for
# the feedback stage, where students will be prompted and strongly encouraged to provide a hint for such hintless answers.
NoHintsFor = List([], scope=Scope.user_state)
# A dictionary of default hints. default hints will be shown to students when there are no matches with the # A dictionary of default hints. default hints will be shown to students when there are no matches with the
# student's incorrect answer within the hint_database dictionary (i.e. no students have made hints for the # student's incorrect answer within the hint_database dictionary (i.e. no students have made hints for the
# particular incorrect answer) # particular incorrect answer)
DefaultHints = Dict(default={'default_hint': 0}, scope=Scope.content) DefaultHints = Dict(default={}, scope=Scope.content)
# List of which hints have been shown to the student # List of which hints have been shown to the student
# this list is used to prevent the same hint from showing up to a student (if they submit the same incorrect answers # this list is used to prevent the same hint from showing up to a student (if they submit the same incorrect answers
# multiple times) # multiple times)
...@@ -44,12 +41,11 @@ class CrowdsourceHinter(XBlock): ...@@ -44,12 +41,11 @@ class CrowdsourceHinter(XBlock):
# This is a dictionary of hints that have been flagged. the values represent the incorrect answer submission, and the # This is a dictionary of hints that have been flagged. the values represent the incorrect answer submission, and the
# keys are the hints the corresponding hints. hints with identical text for differing answers will all not show up for the # keys are the hints the corresponding hints. hints with identical text for differing answers will all not show up for the
# student. # student.
Flagged = Dict(default={"This is a hint that should be flagged": "answer2"}, scope=Scope.user_state_summary) Flagged = Dict(default={}, scope=Scope.user_state_summary)
# This string determines whether or not to show only the best (highest rated) hint to a student # This string determines whether or not to show only the best (highest rated) hint to a student
# When set to 'True' only the best hint will be shown to the student. # When set to 'True' only the best hint will be shown to the student.
# Details on operation when set to 'False' are to be finalized. # Details on operation when set to 'False' are to be finalized.
# TODO: make this into a boolean instead of a dict show_best = Boolean(default = True, scope=Scope.user_state_summary)
show_best = Dict(default={'showbest': 'True'}, scope=Scope.user_state_summary)
def student_view(self, context=None): def student_view(self, context=None):
""" """
...@@ -132,9 +128,11 @@ class CrowdsourceHinter(XBlock): ...@@ -132,9 +128,11 @@ class CrowdsourceHinter(XBlock):
eqplace = answer.index("=") + 1 eqplace = answer.index("=") + 1
answer = answer[eqplace:] answer = answer[eqplace:]
remaining_hints = str(self.find_hints(answer)) remaining_hints = str(self.find_hints(answer))
print(answer)
print(remaining_hints)
if remaining_hints != str(0): if remaining_hints != str(0):
best_hint = max(self.hint_database[str(answer)].iteritems(), key=operator.itemgetter(1))[0] best_hint = max(self.hint_database[str(answer)].iteritems(), key=operator.itemgetter(1))[0]
if self.show_best['showbest'] == 'True': if self.show_best:
# if set to show best, only the best hint will be shown. Different hitns will not be shown # if set to show best, only the best hint will be shown. Different hitns will not be shown
# for multiple submissions/hint requests # for multiple submissions/hint requests
if best_hint not in self.Flagged.keys(): if best_hint not in self.Flagged.keys():
...@@ -145,34 +143,27 @@ class CrowdsourceHinter(XBlock): ...@@ -145,34 +143,27 @@ class CrowdsourceHinter(XBlock):
if best_hint not in self.Flagged.keys(): if best_hint not in self.Flagged.keys():
self.Used.append(best_hint) self.Used.append(best_hint)
return {'HintsToUse': best_hint, "StudentAnswer": answer} return {'HintsToUse': best_hint, "StudentAnswer": answer}
else: # choose another random hint for the answer.
# choose another random hint for the answer. temporary_hints_list = []
temporary_hints_list = [] for hint_keys in self.hint_database[str(answer)]:
for hint_keys in self.hint_database[str(answer)]: if hint_keys not in self.Used:
if hint_keys not in self.Used: if hint_keys not in self.Flagged:
if hint_keys not in self.Flagged:
temporary_hints_list.append(str(hint_keys))
not_used = random.choice(temporary_hints_list)
else:
if best_hint not in self.Used:
# choose highest rated hint for the incorrect answer
if best_hint not in self.Flagged.keys():
self.Used.append(best_hint)
return {'HintsToUse': best_hint, "StudentAnswer": answer}
else:
temporary_hints_list = []
for hint_keys in self.DefaultHints:
if hint_keys not in self.Used:
temporary_hints_list.append(str(hint_keys)) temporary_hints_list.append(str(hint_keys))
if len(temporary_hints_list) != 0:
not_used = random.choice(temporary_hints_list) not_used = random.choice(temporary_hints_list)
else: self.Used.append(not_used)
# if there are no more hints left in either the database or defaults return {'HintsToUse': not_used, "StudentAnswer": answer}
self.Used.append(str("There are no hints for" + " " + answer)) else:
self.NoHintsFor.append(answer) temporary_hints_list = []
return {'HintsToUse': "Sorry, there are no more hints for this answer.", "StudentAnswer": answer} for hint_keys in self.DefaultHints:
self.Used.append(not_used) temporary_hints_list.append(str(hint_keys))
return {'HintsToUse': not_used, "StudentAnswer": answer} if len(temporary_hints_list) != 0:
not_used = random.choice(temporary_hints_list)
self.Used.append(not_used)
return {'HintsToUse': not_used, "StudentAnswer": answer}
else:
# if there are no more hints left in either the database or defaults
self.Used.append(str("There are no hints for" + " " + answer))
return {'HintsToUse': "Sorry, there are no hints for this answer.", "StudentAnswer": answer}
def find_hints(self, answer): def find_hints(self, answer):
""" """
...@@ -181,6 +172,8 @@ class CrowdsourceHinter(XBlock): ...@@ -181,6 +172,8 @@ class CrowdsourceHinter(XBlock):
Args: Args:
answer: This is equal to answer from get_hint, the answer the student submitted answer: This is equal to answer from get_hint, the answer the student submitted
Returns 0 if no hints to show exist
""" """
isflagged = [] isflagged = []
isused = 0 isused = 0
...@@ -194,7 +187,8 @@ class CrowdsourceHinter(XBlock): ...@@ -194,7 +187,8 @@ class CrowdsourceHinter(XBlock):
if hint_keys == flagged_keys: if hint_keys == flagged_keys:
isflagged.append(hint_keys) isflagged.append(hint_keys)
if str(hint_keys) in self.Used: if str(hint_keys) in self.Used:
isused += 1 if self.show_best is False:
isused += 1
if (len(self.hint_database[str(answer)]) - len(isflagged) - isused) > 0: if (len(self.hint_database[str(answer)]) - len(isflagged) - isused) > 0:
return str(1) return str(1)
else: else:
...@@ -219,15 +213,15 @@ class CrowdsourceHinter(XBlock): ...@@ -219,15 +213,15 @@ class CrowdsourceHinter(XBlock):
if len(self.WrongAnswers) == 0: if len(self.WrongAnswers) == 0:
return return
else: else:
print(self.Used)
for index in range(0, len(self.Used)): for index in range(0, len(self.Used)):
# each index is a hint that was used, in order of usage # each index is a hint that was used, in order of usage
for answer_keys in self.hint_database: if str(self.Used[index]) in self.hint_database[self.WrongAnswers[index]]:
if str(self.Used[index]) in self.hint_database[str(answer_keys)]: # add new key (hint) to feedback_data with a value (incorrect answer)
# add new key (hint) to feedback_data with a value (incorrect answer) feedback_data[str(self.Used[index])] = str(self.WrongAnswers[index])
feedback_data[str(self.Used[index])] = str(self.WrongAnswers[index]) else:
else: # if the student's answer had no hints (or all the hints were flagged and unavailable) return None
# if the student's answer had no hints (or all the hints were flagged and unavailable) return None feedback_data[None] = str(self.WrongAnswers[index])
feedback_data[None] = str(self.WrongAnswers[index])
self.WrongAnswers=[] self.WrongAnswers=[]
self.Used=[] self.Used=[]
print(feedback_data) print(feedback_data)
...@@ -334,27 +328,9 @@ class CrowdsourceHinter(XBlock): ...@@ -334,27 +328,9 @@ class CrowdsourceHinter(XBlock):
else: else:
temporary_dictionary[str(data_hint)] -= 1 temporary_dictionary[str(data_hint)] -= 1
self.hint_database[str(answer_data)] = temporary_dictionary self.hint_database[str(answer_data)] = temporary_dictionary
print(self.hint_database)
return str(temporary_dictionary[str(data_hint)]) return str(temporary_dictionary[str(data_hint)])
def remove_symbols(self, answer_data):
"""
For removing colons and such from answers to prevent weird things from happening. Not sure if this is properly functional.
Args:
answer_data: This is equal to the data['answer'] in self.rate_hint
Returns:
answer_data: This is equal to the argument answer_data except that symbols have been
replaced by text (hopefully)
"""
answer_data = answer_data.replace('ddeecciimmaallppooiinntt', '.')
answer_data = answer_data.replace('qquueessttiioonnmmaarrkk', '?')
answer_data = answer_data.replace('ccoolloonn', ':')
answer_data = answer_data.replace('sseemmiiccoolloonn', ';')
answer_data = answer_data.replace('eeqquuaallss', '=')
answer_data = answer_data.replace('qquuoottaattiioonnmmaarrkkss', '"')
return answer_data
@XBlock.json_handler @XBlock.json_handler
def moderate_hint(self, data, suffix=''): def moderate_hint(self, data, suffix=''):
""" """
...@@ -422,7 +398,7 @@ class CrowdsourceHinter(XBlock): ...@@ -422,7 +398,7 @@ class CrowdsourceHinter(XBlock):
return [ return [
("CrowdsourceHinter", ("CrowdsourceHinter",
"""<vertical_demo> """<vertical_demo>
<crowdsourcehinter/> <crowdsourcehinter/>
</vertical_demo> </vertical_demo>
"""), """),
] ]
...@@ -14,11 +14,7 @@ ...@@ -14,11 +14,7 @@
.csh_hint_value{ .csh_hint_value{
display: flex; display: flex;
margin-left: 10px; margin-left: 10px;
} flex-direction: column;
.csh_rating_data{
width: 10%;
vertical-align: middle;
} }
.crowdsourcehinter_block .csh_HintsToUse { .crowdsourcehinter_block .csh_HintsToUse {
...@@ -37,8 +33,6 @@ ...@@ -37,8 +33,6 @@
} }
.csh_hint_data{ .csh_hint_data{
justify-content: center;
width: 90%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
......
<script type='x-tmpl/mustache' id='show_hint_feedback'> <script type='x-tmpl/mustache' id='show_hint_feedback'>
<div class='csh_hint_value' value="{{hintvalue}}"> <div class='csh_hint_value' value="{{hintvalue}}">
<div class='csh_hint_data'>
<div class="csh_hint">{{hint}}</div>
</div>
<div class='csh_rating_data'> <div class='csh_rating_data'>
<div role="button" class="csh_rate_hint" data-rate="upvote" data-icon="arrow-u" aria-label="upvote"> <div role="button" class="csh_rate_hint" data-rate="upvote" data-icon="arrow-u" aria-label="upvote">
<b>This hint was helpful</b> <b>This hint was helpful</b>
</div> </div>
//<div class ="csh_rating"> {{rating}} </div>
<div role="button" class="csh_rate_hint" data-rate="downvote" aria-label="downvote"> <div role="button" class="csh_rate_hint" data-rate="downvote" aria-label="downvote">
<b>This hint was not helpful</b> <b>This hint was not helpful</b>
</div> </div>
</div>
<div class='csh_hint_data'>
<div class="csh_hint">{{hint}}</div>
<div role="button" class="csh_rate_hint" data-rate="flag" data-icon="flag" aria-label="flag"> <div role="button" class="csh_rate_hint" data-rate="flag" data-icon="flag" aria-label="flag">
<b>Report this hint</b> <b>Report this hint</b>
</div> </div>
......
...@@ -38,7 +38,7 @@ function CrowdsourceHinter(runtime, element){ ...@@ -38,7 +38,7 @@ function CrowdsourceHinter(runtime, element){
}else{ }else{
$('.csh_correct', element).show(); $('.csh_correct', element).show();
$('.csh_correct', element).text("You're correct! Please help us improve our hints by voting on them, or submit your own hint!"); $('.csh_correct', element).text("You're correct! Please help us improve our hints by voting on them, or submit your own hint!");
$(".csh_HintsToUse", element).text(" "); $(".csh_hint_reveal", element).hide();
//send empty data for ajax call because not having a data field causes error //send empty data for ajax call because not having a data field causes error
$.ajax({ $.ajax({
type: "POST", type: "POST",
...@@ -58,7 +58,7 @@ function CrowdsourceHinter(runtime, element){ ...@@ -58,7 +58,7 @@ function CrowdsourceHinter(runtime, element){
type: "POST", type: "POST",
url: runtime.handlerUrl(element, 'get_feedback'), url: runtime.handlerUrl(element, 'get_feedback'),
data: JSON.stringify({"isStaff":"false"}), data: JSON.stringify({"isStaff":"false"}),
success: getStudentFeedback success: getFeedback
}); });
} }
} }
...@@ -72,11 +72,11 @@ function CrowdsourceHinter(runtime, element){ ...@@ -72,11 +72,11 @@ function CrowdsourceHinter(runtime, element){
$('.csh_HintsToUse', element).text(result.HintsToUse); $('.csh_HintsToUse', element).text(result.HintsToUse);
} }
function showHintFeedback(hint){ function showHintFeedback(hint, student_answer){
//Append answer-specific hints for each student answer during the feedback stage. //Append answer-specific hints for each student answer during the feedback stage.
//This appended div includes upvote/downvote/flagging buttons, the hint, and the hint's rating //This appended div includes upvote/downvote/flagging buttons, the hint, and the hint's rating
$(".csh_student_answer", element).each(function(){ $(".csh_student_answer", element).each(function(){
if ($(this).find("span").text() == result.student_answer){ if ($(this).find("span").text() == student_answer){
var html = ""; var html = "";
$(function(){ $(function(){
var data = { var data = {
...@@ -105,17 +105,13 @@ function CrowdsourceHinter(runtime, element){ ...@@ -105,17 +105,13 @@ function CrowdsourceHinter(runtime, element){
function setStudentAnswers(student_answers){ function setStudentAnswers(student_answers){
//Append divs for each answer the student submitted before correctly answering the question. //Append divs for each answer the student submitted before correctly answering the question.
//showHintFeedback appends new hints into these divs. //showHintFeedback appends new hints into these divs.
for(var i = 0; i < student_answers.length; i++){ var html = "";
var html = ""; var template = $('#show_answer_feedback').html();
$(function(){ var data = {
var template = $('#show_answer_feedback').html(); answer: student_answers
var data = { };
answer: student_answers[i] html = Mustache.render(template, data);
}; $(".csh_feedback", element).append(html);
html = Mustache.render(template, data);
});
$(".csh_feedback", element).append(html);
}
} }
function getFeedback(result){ function getFeedback(result){
...@@ -134,17 +130,18 @@ function CrowdsourceHinter(runtime, element){ ...@@ -134,17 +130,18 @@ function CrowdsourceHinter(runtime, element){
$(".csh_student_answer", element).each(function(){ $(".csh_student_answer", element).each(function(){
if ($(this).find("span").text() == student_answer){ if ($(this).find("span").text() == student_answer){
var html = ""; var html = "";
$(function(){ var template = $('#show_no_hints').html();
var template = $('#show_no_hints').html(); var data = {};
var data = {}; html = Mustache.render(template, data);
html = Mustache.render(template, data); console.log(html);
}); $(this).find("span").append(html);
$(this).append(html);
} }
}); });
} }
//flagged hints have their corresponding answer set to "Flagged" //flagged hints have their corresponding answer set to "Flagged"
showHintFeedback(hint); else{
showHintFeedback(hint, student_answer);
}
}); });
isShowingHintFeedback = true; isShowingHintFeedback = true;
} }
...@@ -188,7 +185,7 @@ function CrowdsourceHinter(runtime, element){ ...@@ -188,7 +185,7 @@ function CrowdsourceHinter(runtime, element){
type: "POST", type: "POST",
url: runtime.handlerUrl(element, 'get_ratings'), url: runtime.handlerUrl(element, 'get_ratings'),
data: JSON.stringify({"student_answer": answerdata, "hint": newhint}), data: JSON.stringify({"student_answer": answerdata, "hint": newhint}),
success: showHintFeedback success: showHintFeedback(newhint, answerdata)
}); });
} }
}); });
......
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