Commit 4f0d011d by Sola

Merge branch 'master' of https://github.com/solashirai/crowdsourcedhinter into soladev

Conflicts:
	README.md~
	crowdxblock/crowdxblock.py~
parents dd682c51 10195599
This is the repository for the Crowd Sourced Hinter XBlock. The Crowd Sourced Hinter serves to provide students with hints when they incorrectly answer a problem within a course (currently tested for text and numerical input type questions).
This XBlock is still under construction. Functionalities to set default hints, properly moderate flagged hints, and improvements to user interface are to be done soon.
backlog: https://docs.google.com/a/edx.org/document/d/1lOBLZWohwRmnfgwz53gc4b4GdfP4EER3UNohdYfcEJU/edit#
An example of a student recieving a hint
![CrowdSourcedHinter Hint Screenshot](crowdsourcedhinter_hint.png)
An example of a hint giving feedback
![CrowdSourcedHinter Student Feedback Screenshot](crowdsourcedhinter_feedback.png)
To bring the crowd sourced hinter into a demo course:
First, follow https://github.com/edx/edx-platform/blob/master/docs/en_us/developers/source/xblocks.rst#testing for general xblock creation.
The name of the module to set in the advanced settings tab is "crowdxblock" (this will likely be changed in the near future to something like "crowd_sourced_hinter").
After creating a new unit, add the crowdsourcedhinter XBlock into a course just like any other XBlock. The name of the crowd sourced hinter may not show up in studio for some unknown reason, but an empty space where its name should be will be clickable (problem to be identified/fixed...).
Testing the functionality of the crowd sourced hinter works best when switching between different users and answering the same problem.
What It Does:
The two key features of the crowd sourced hinter are the abilities to show students hints and to have the students themselves create hints to be shown to future students.
When a student incorrectly answers a text input type problem, the crowd sourced hinter will look through its database to search for a hint that has been stored for that exact incorrect answer input (i.e. when the database is large enough, two different incorrect answers would not receive the same hint). If hints exist for a student's incorrect answer, this hint is shown to the student. The student then may have the opportunity to input their answer again, which may prompt another hint to be displayed.
After a student re-submits an answer correctly, they can rate hints as well as submit new hints. Rating hints works by upvoting, downvoting, or flagging the hints (flagged hints are not shown to students). Students can submit new hints for each incorrect answer that has been made, and this hint will be stored only for that specific incorrect answer.
# pylint: disable=line-too-long
# pylint: disable=unused-argument
import ast
import logging
import operator
import pkg_resources
import random
from xblock.core import XBlock
from xblock.fields import Scope, Dict, List
from xblock.fragment import Fragment
log = logging.getLogger(__name__)
class CrowdXBlock(XBlock):
"""
This is the Crowd Sourced Hinter XBlock. This Xblock seeks to provide students with hints
that specifically address their mistake. Additionally, the hints that this Xblock shows
are created by the students themselves. This doc string will probably be edited later.
"""
hint_database = Dict(default={: {}}, scope=Scope.user_state_summary)
# database of hints. hints are stored as such: {"incorrect_answer": {"hint": rating}}. each key (incorrect answer)
# has a corresponding dictionary (in which hints are keys and the hints' ratings are the values).
HintsToUse = Dict({}, scope=Scope.user_state)
# this is a dictionary of hints that will be used to determine what hints to show a student.
# flagged hints are not included in this dictionary of hints
WrongAnswers = List([], scope=Scope.user_state)
# 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.
DefaultHints = Dict(default={}, scope=Scope.content)
# 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
# particular incorrect answer)
Used = List([], scope=Scope.user_state)
# list of which hints from the HintsToUse dictionary 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
# multiple times)
Voted = List(default=[], scope=Scope.user_state)
# this list is used to prevent students from voting multiple times on the same hint during the feedback stage.
# i believe this will also prevent students from voting again on a particular hint if they were to return to
# a particular problem later
Flagged = Dict(default={}, scope=Scope.user_state_summary)
# this is a dictionary of hints that have been flagged. the keys represent the incorrect answer submission, and the
# values are the hints the corresponding hints. even if a hint is flagged, if the hint shows up for a different
# incorrect answer, i believe that the hint will still be able to show for a student
def student_view(self, context=None):
"""
This function defines which files to access when a student views this xblock.
"""
html = self.resource_string("static/html/crowdxblock.html")
frag = Fragment(html.format(self=self))
frag.add_css(self.resource_string("static/css/crowdxblock.css"))
frag.add_javascript(self.resource_string("static/js/src/crowdxblock.js"))
frag.initialize_js('CrowdXBlock')
return frag
def studio_view(self, context=None):
"""
This function defines which files to access when an instructor views this xblock through the
studio view (and click "edit"). The only difference from student_view is the html script.
"""
html = self.resource_string("static/html/crowdxblockstudio.html")
frag = Fragment(html.format(self=self))
frag.add_css(self.resource_string("static/css/crowdxblock.css"))
frag.add_javascript(self.resource_string("static/js/src/crowdxblock.js"))
frag.initialize_js('CrowdXBlock')
return frag
def resource_string(self, path):
"""
This function is used to get the path of static resources.
"""
data = pkg_resources.resource_string(__name__, path)
return data.decode("utf8")
@XBlock.json_handler
def clear_temp(self, data, suffix=''):
"""
this clears all temprorary lists/dictionaries. This may not be relevant any longer
but is intended to prevent hints from messing up when changing between units within
a section
"""
remove_list = []
# a list is used to systematically remove all key/values from a dictionary.
# there may be a much cleaner way to do this but I could not find one very easily.
# This whole function will probably be removed if scopes are corrected later.
for used_hints in self.Used:
remove_list.append(used_hints)
for items in remove_list:
self.Used.remove(items)
remove_list = []
for wrong_answers in self.WrongAnswers:
remove_list.append(wrong_answers)
for items in remove_list:
self.WrongAnswers.remove(items)
self.HintsToUse.clear()
@XBlock.json_handler
def get_hint(self, data, suffix=''):
"""
Returns hints to students. Hints are placed into the HintsToUse dictionary if it is found that they
are not flagged. Hints with the highest rating are shown to students unless the student has already
submitted the same incorrect answer previously.
Args:
data['submittedanswer']: The string of text that the student submits for a problem.
returns:
'HintsToUse': the highest rated hint for an incorrect answer
or another random hint for an incorrect answer
or 'Sorry, there are no more hints for this answer.' if no more hints exist
"""
answer = str(data["submittedanswer"])
answer = answer.lower() # for analyzing the student input string I make it lower case.
found_equal_sign = 0
hints_used = 0
# the string returned by the event problem_graded is very messy and is different
# for each problem, but after all of the numbers/letters there is an equal sign, after which the
# student's input is shown. I use the function below to remove everything before the first equal
# sign and only take the student's actual input.
if "=" in answer:
if found_equal_sign == 0:
found_equal_sign = 1
eqplace = answer.index("=") + 1
answer = answer[eqplace:]
self.find_hints(answer)
# add hints to the self.HintsToUse dictionary. Will likely be replaced
# soon by simply looking within the self.hint_database for hints.
if str(answer) not in self.hint_database:
# add incorrect answer to hint_database if no precedent exists
self.hint_database[str(answer)] = {}
self.HintsToUse.clear()
self.HintsToUse.update(self.DefaultHints)
if max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0] not in self.Used:
# choose highest rated hint for the incorrect answer
if max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0] not in self.Flagged.keys():
self.Used.append(max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0])
return {'HintsToUse': max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0]}
else:
# choose another random hint for the answer.
not_used = random.choice(self.HintsToUse.keys())
for used_hints in self.Used:
if used_hints in self.HintsToUse.keys():
hints_used += 1
if str(len(self.HintsToUse)) > str(hints_used):
while not_used in self.Used:
# loop through hints to ensure no hint is shown twice
while not_used in self.Flagged.keys():
not_used = random.choice(self.HintsToUse.keys())
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 more hints for this answer."}
self.Used.append(not_used)
return {'HintsToUse': not_used}
def find_hints(self, answer):
"""
This function is used to find all appropriate hints that would be provided for
an incorrect answer. Flagged hints are not added into the HintsToUse dictionary.
Args:
answer: This is equal to answer from get_hint, the answer the student submitted
"""
hints_exist = 0
isflagged = []
self.WrongAnswers.append(str(answer)) # add the student's input to the temporary list, for later use
for answer_keys in self.hint_database:
# look through answer keys to find a match with the student's answer, and add
# the hints that exist for said answer into the HintsToUse dict.
hints = str(self.hint_database[str(answer_keys)])
if str(answer_keys) == str(answer):
self.HintsToUse.clear()
self.HintsToUse.update(ast.literal_eval(hints))
for hint_keys in self.HintsToUse:
for flagged_keys in self.Flagged:
if str(hint_keys) == str(flagged_keys):
isflagged.append(hint_keys)
for flagged_keys in isflagged:
# remove flagged keys from the HintsToUse
del self.HintsToUse[flagged_keys]
for answer_keys in self.HintsToUse:
if answer_keys not in self.Used:
hints_exist = 1
if hints_exist == 0:
self.HintsToUse.update(self.DefaultHints)
@XBlock.json_handler
def get_feedback(self, data, suffix=''):
"""
This function is used to facilitate student feedback to hints. Specifically this function
is used to send necessary data to JS about incorrect answer submissions and hints.
Returns:
feedback_data: This dicitonary contains all incorrect answers that a student submitted
for the question, all the hints the student recieved, as well as two
more random hints that exist for an incorrect answer in the hint_database
"""
feedback_data = {}
# feedback_data is a dictionary of hints (or lack thereof) used for a
# specific answer, as well as 2 other random hints that exist for each answer
# that were not used. The keys are the used hints, the values are the
# corresponding incorrect answer
number_of_hints = 0
if len(self.WrongAnswers) == 0:
return
else:
for index in range(0, len(self.Used)):
# 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[str(answer_keys)]:
# add new key (hint) to feedback_data with a value (incorrect answer)
feedback_data[str(self.Used[index])] = str(self.WrongAnswers[index])
for answer_keys in self.hint_database:
if str(answer_keys) == str(self.WrongAnswers[index]):
# for answes in self.hint_database, if the len of the answer's corresponding
# hints is not zero...
if str(len(self.hint_database[str(answer_keys)])) != str(0):
number_of_hints = 0
hint_key_shuffled = self.hint_database[str(answer_keys)].keys()
# shuffle keys so that random hints won't repeat. probably can be done better.
random.shuffle(hint_key_shuffled)
for random_hint_key in hint_key_shuffled:
if str(random_hint_key) not in self.Flagged.keys():
if number_of_hints < 3:
number_of_hints += 1
# add random unused hint to feedback_data's keys
# with the value as the incorrect answer
feedback_data[str(random_hint_key)] = str(self.WrongAnswers[index])
self.WrongAnswers.append(str(self.WrongAnswers[index]))
self.Used.append(str(random_hint_key))
else:
self.no_hints(index)
feedback_data[str("There are no hints for" + " " + str(self.WrongAnswers[index]))] = str(self.WrongAnswers[index])
return feedback_data
def no_hints(self, index):
"""
This function is used when no hints exist for an answer. The feedback_data within
get_feedback is set to "there are no hints for" + " " + str(self.WrongAnswers[index])
"""
self.WrongAnswers.append(str(self.WrongAnswers[index]))
self.Used.append(str("There are no hints for" + " " + str(self.WrongAnswers[index])))
@XBlock.json_handler
def rate_hint(self, data, suffix=''):
"""
Used to facilitate hint rating by students. Ratings are -1, 1, or 0. -1 is downvote, 1 is upvote, and 0 is
when a student flags a hint. 'zzeerroo' is returned to JS when a hint's rating is 0 because whenever 0 was
simply returned, JS would interpret it as null.
Hint ratings in hint_database are updated and the resulting hint rating (or flagged status) is returned to JS.
Args:
data['answer']: The incorrect answer that corresponds to the hint that is being voted on
data['value']: The hint that is being voted on
data['student_rating']: The rating chosen by the student. The value is -1, 1, or 0.
Returns:
"rating": The rating of the hint. 'zzeerroo' is returned if the hint's rating is 0.
If the hint has already been voted on, 'You have already voted on this hint!'
will be returned to JS.
"""
original_data = data['answer'] # original strings are saved to return later
answer_data = data['answer']
# answer_data is manipulated to remove symbols to prevent errors that
# might arise due to certain symbols. I don't think I have this fully working but am not sure.
data_rating = data['student_rating']
data_value = data['value']
answer_data = self.remove_symbols(answer_data)
if str(data['student_rating']) == str(0):
# if student flagged hint
self.hint_flagged(data['value'], answer_data)
return {"rating": 'thiswasflagged', 'origdata': original_data}
if str(answer_data) not in self.Voted:
self.Voted.append(str(answer_data)) # add data to Voted to prevent multiple votes
rating = self.change_rating(data_value, int(data_rating), answer_data) # change hint rating
if str(rating) == str(0):
# if the rating is "0", return "zzeerroo" instead. "0" showed up as "null" in JS
return {"rating": str('zzeerroo'), 'origdata': original_data}
else:
return {"rating": str(rating), 'origdata': original_data}
else:
return {"rating": str('You have already voted on this hint!'), 'origdata': original_data}
def hint_flagged(self, data_value, answer_data):
"""
This is used to add a hint to the self.flagged dictionary. When a hint is returned with the rating
of 0, it is considered to be flagged.
Args:
data_value: This is equal to the data['value'] in self.rate_hint
answer_data: This is equal to the data['answer'] in self.rate_hint
"""
for answer_keys in self.hint_database:
if answer_keys == data_value:
for hint_keys in self.hint_database[str(answer_keys)]:
if str(hint_keys) == answer_data:
self.Flagged[str(hint_keys)] = str(answer_keys)
def change_rating(self, data_value, data_rating, answer_data):
"""
This function is used to change the rating of a hint when it is voted on.
Initiated by rate_hint. The temporary_dictionary is manipulated to be used
in self.rate_hint
Args:
data_value: This is equal to the data['value'] in self.rate_hint
data_rating: This is equal to the data['student_rating'] in self.rate_hint
answer_data: This is equal to the data['answer'] in self.rate_hint
Returns:
The rating associated with the hint is returned. This rating is identical
to what would be found under self.hint_database[answer_string[hint_string]]
"""
for hint_keys in self.Used:
if str(hint_keys) == str(answer_data):
# use index of hint used to find the hint within self.hint_database
answer = self.Used.index(str(answer_data))
for answer_keys in self.hint_database:
temporary_dictionary = str(self.hint_database[str(answer_keys)])
temporary_dictionary = (ast.literal_eval(temporary_dictionary))
# if I remember correctly, changing the rating values in self.hint_database
# didn't work correctly for some reason, so instead I manipulated this
# temporary dictionary and then set self.hint_database to equal it.
# probably due to scope error (which also is affecting the studio interactions)
if str(answer_keys) == str(self.WrongAnswers[answer]):
temporary_dictionary[self.Used[int(answer)]] += int(data_rating)
self.hint_database[str(answer_keys)] = temporary_dictionary
return str(temporary_dictionary[self.Used[int(answer)]])
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
def moderate_hint(self, data, suffix=''):
"""
UNDER CONSTRUCTION, intended to be used for instructors to remove hints from the database after hints
have been flagged.
"""
flagged_hints = {}
flagged_hints = self.Flagged
if data['rating'] == "dismiss":
flagged_hints.pop(data['answer_wrong'], None)
else:
flagged_hints.pop(data['answer_wrong'], None)
for answer_keys in self.hint_database:
if str(answer_keys) == data['answ']:
for hint_keys in self.hint_database[str(answer_keys)]:
if str(hint_keys) == data['hint']:
temporary_dict = str(self.hint_database[str(answer_keys)])
temporary_dict = (ast.literal_eval(temporary_dict))
temporary_dict.pop(hint_keys, None)
self.hint_database[str(answer_keys)] = temporary_dict
@XBlock.json_handler
def give_hint(self, data, suffix=''):
"""
This function adds a new hint submitted by the student into the hint_database.
Args:
data['submission']: This is the text of the new hint that the student has submitted.
data['answer']: This is the incorrect answer for which the student is submitting a new hint.
"""
submission = data['submission'].replace('ddeecciimmaallppooiinntt', '.')
hint_id = data['answer'].replace('ddeecciimmaallppooiinntt', '.')
for answer_keys in self.hint_database:
if str(answer_keys) == self.WrongAnswers[self.Used.index(hint_id)]:
# find the answer for which a hint is being submitted
if str(submission) not in self.hint_database[str(answer_keys)]:
temporary_dictionary = str(self.hint_database[str(answer_keys)])
temporary_dictionary = (ast.literal_eval(temporary_dictionary))
temporary_dictionary.update({submission: 0})
# once again, manipulating temporary_dictionary and setting
# self.hint_database equal to it due to being unable to directly
# edit self.hint_databse. Most likely scope error
self.hint_database[str(answer_keys)] = temporary_dictionary
return
else:
# if the hint exists already, simply upvote the previously entered hint
hint_index = self.Used.index(submission)
for default_hints in self.DefaultHints:
if default_hints == self.Used[int(hint_index)]:
self.DefaultHints[str(default_hints)] += int(1)
return
for answer_keys in self.hint_database:
temporary_dictionary = str(self.hint_database[str(answer_keys)])
temporary_dictionary = (ast.literal_eval(temporary_dictionary))
if str(answer_keys) == str(self.WrongAnswers[int(hint_index)]):
temporary_dictionary[self.Used[int(hint_index)]] += int(data['rating'])
self.hint_database[str(answer_keys)] = temporary_dictionary
@XBlock.json_handler
def studiodata(self, data, suffix=''):
"""
This function serves to return the dictionary of flagged hints to JS. This is intended for use in
the studio_view, which is under construction at the moment
"""
return self.Flagged
@staticmethod
def workbench_scenarios():
"""A canned scenario for display in the workbench."""
return [
("CrowdXBlock",
"""<vertical_demo>
<crowdxblock/>
</vertical_demo>
"""),
]
/* CSS for CrowdXBlock */
.crowdxblock_block .count {
font-weight: bold;
}
/*.crowdxblock_block {
cursor: pointer;
background: rgb(0,0,200);
}*/
.crowdxblock_block .crowdsource-wrapper {
box-shadow: inset 0 1px 2px 1px rgba(0,0,0,0.1);
border-radius: 2px;
display: none;
margin-top: 20px;
padding: (15px);
background: rgb(50, 50, 235);
}
.crowdxblock_block .hint-inner-container {
padding-left: 15px;
padding-right: 15px;
font-size: 16px;
}
.crowdxblock_studio .studio_textarea {
width: 400px;
}
.crowdxblock_block .hintsarea {
border-style: solid;
border-width: thin;
border-color: rgb(200,200,200);
}
/*.crowdxblock_block .hintbutton {
height: 10px;
}*/
.crowdxblock_block .vote {
padding-top: 0px !important;
padding-bottom: 0px !important;
}
/*.crowdxblock_block .hintbutton {
}*/
/*.crowdxblock_block .hintsarea {
width: 600px;
padding: 10px;
border: 5px solid gray;
margin: 0px;
}*/
<div class="crowdxblock_block"> <!--most stuff just for testing purposes-->
<p> <span class='HintsToUse'>{self.HintsToUse}</span>
</p>
</div>
<div class="crowdxblock_block">
<section class="correct"></section>
<p>
<span class='Thankyou'></span>
</p>
<div class="hintansarea">
</div>
<p>
</p>
<section class="action">
<input type="hidden" name="problem_id" value="Numerical Input">
<section class="problem">
</div></section></span>
</section>
<section class="crowdsource-wrapper">
</section>
</section>
</section>
</section>
<div class="crowdxblock_block"> <!--most stuff just for testing purposes-->
<p> <span class='HintsToUse'>{self.HintsToUse}</span>
</p>
</div>
<div class="crowdxblock_block">
<section class="correct"></section>
<p>
<span class='Thankyou'></span>
</p>
<div class="hintansarea">
</div>
<p>
</p>
<section class="action">
<input type="hidden" name="problem_id" value="Numerical Input">
<section class="problem">
</div></section></span>
</section>
<section class="crowdsource-wrapper">
</section>
</section>
</section>
</section>
<div class="crowdxblock_block">
<p id="hint_purge">
<li id="vert-0" data-id="i4x://Me/19.002/crowdsource_hinter/crowdsource_hinter_def7a1142dd0">
<section class="xblock xblock-student_view xmodule_display xmodule_CrowdsourceHinterModule" data-type="Hinter" id="hinter-root">
<section class="xblock xblock-student_view xmodule_display xmodule_CapaModule" data-type="Problem" id="problem">
<section id="problem_i4x-Me-19_002-problem-Numerical_Input" class="problems-wrapper" data-problem-id="i4x://Me/19.002/problem/Numerical_Input" data-url="/courses/Me/19.002/Test/modx/i4x://Me/19.002/problem/Numerical_Input" data-progress_status="done" data-progress_detail="1/1">
<h2 class="problem-header">
Numerical Input
</h2>
<section class="problem-progress">(1/1 point)</section>
<section class="problem">
<div><p>The answer is 2*x^2*y + 5
</p><span><br><span> Answer =</span>
<section id="inputtype_i4x-Me-19_002-problem-Numerical_Input_2_1" class="text-input-dynamath capa_inputtype ">
<div class="correct " id="status_i4x-Me-19_002-problem-Numerical_Input_2_1">
<input type="text" name="input_i4x-Me-19_002-problem-Numerical_Input_2_1" id="input_i4x-Me-19_002-problem-Numerical_Input_2_1" aria-describedby="answer_i4x-Me-19_002-problem-Numerical_Input_2_1" value="2*x^2*y +5" class="math" size="40">
</div></section></span>
<input type="file" />
<section class="solution-span"><span id="solution_i4x-Me-19_002-problem-Numerical_Input_solution_1"></span></section></div>
<section class="action">
<input type="hidden" name="problem_id" value="Numerical Input">
<input class="check Check" type="button" value="Check">
<button class="show"><span class="show-label">Show Answer(s)</span> <span class="sr">(for question(s) above - adjacent to each field)</span></button>
</section>
</section>
</section>
</section>
<div id="i4x_Me_19_002_problem_Numerical_Input_setup"></div>
<section class="crowdsource-wrapper" data-url="/courses/Me/19.002/Test/modx/i4x://Me/19.002/crowdsource_hinter/crowdsource_hinter_def7a1142dd0" data-child-id="i4x://Me/19.002/problem/Numerical_Input" style="display: none;"> </section>
</section>
</li>
</p>
</div>
var repeating = 0;
var repeatcounter = 0;
var canhint = 0;
var issubmitting = 0;
var issubmittinghint = 0;
function CrowdXBlock(runtime, element){
var WrongAnswer = [];
var HintUsed = [];
var HintShown = [];
$("#answer").hide();
$(".problem").hide();
$("#feedback").hide();
vare = String;
varet = String;
vard = String;
$(".HintsToUse", element).text("Hints are enabled for this problem!");
clearvariables();
repeatcounter += 1;
console.debug(repeatcounter);
Logger.listen('seq_next', null, clearingvariables);
Logger.listen('seq_goto', null, clearingvariables);
function clearingvariables(event_type, data, element){
clearvariables(data);
}
function clearvariables(data){
HintUsed = [];
WrongAnswer = [];
repeating = 0;
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'clear_temp'),
data: JSON.stringify({"hello": "world"}),
});
}
Logger.listen('problem_graded', null, dostuff);
function dostuff(event_type, data, element){
repeating += 1;
if(repeating != repeatcounter){
console.debug(repeating);
}else{
$("#studentsubmit").val('');
vare = element;
varet = event_type;
vard = data;
senddata(varet, vard, vare);
}
}
$(document).on('click', '.check.Check', function(){
repeating = 0;
});
function senddata(varet, vard, vare){
if (vard[1].search(/class="correct/) === -1){
$.ajax({ //that probably will be changed once i use response.search or something?
type: "POST", //if/when that is changed, remove checkreply and uncomment the else statement below
url: runtime.handlerUrl(element, 'get_hint'),
data: JSON.stringify({"submittedanswer": vard[0]}), //return student's incorrect answer here
//from vard[1] check id (long thing) and get class (correct or incorrect)
success: seehint
});
}else{
$('.correct', element).show();
$('.correct', element).text("You're correct! Please help us improve our hints by voting on them, or submit your own hint!");
$(".HintsToUse", element).text(" ");
console.debug("this should also only show up once...");
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'get_feedback'),
data: JSON.stringify({"hello": "world"}),
success: getfeedback
});}
}
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'studiodata'),
data: JSON.stringify({"hello": "world"}),
success: studiodata
});
function studiodata(result){
$(".xblock-editor").append("confirm_working");
if($(".xblock-editor").length != 0){
$.each(result, function(index, value) {
console.debug(index);
$('.xblock-editor').append("<p id=\"" + value + "\"> The hint<b>" + " " + index + " " + "</b>was flagged for the submission<b>" + " " + value + "</b></p>");
$('#'+value).prepend("<input data-value=\"" + value + "\" id=\"" + index + "\" style=\"height:35px;padding-top: 3px;\" type=\"button\" class=\"flagbutton\" data-rate=\"dismiss\" value=\"Dismiss Hint\"><input data-value=\"" + value + "\" id=\"" + index + "\" style=\"height:35px; padding-top: 3px;\" type=\"button\" class=\"flagbutton\" data-rate=\"purge\" value=\"Purge Hint\">");
});
}
}
$(document).on('click', '.flagbutton', function(){
answer_wrong = $(this).attr('id');
hint = $(this).attr('data-value');
rating = $(this).attr('data-rate');
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'moderate_hint'),
data: JSON.stringify({"answer_wrong":answer_wrong, "hint": hint, "rating":rating}),
});
});
function seehint(result){//use html to show these results somewhere i guess
console.debug('seehint');
HintUsed.push(result.HintsToUse);
$('.HintsToUse', element).text("Hint:" + " " + result.HintsToUse); //text(result.self.hints?)
console.debug('hint:' + ' ' + result.HintsToUse);
}
function getfeedback(result){
$("#answer").show();
$(".problem").show();
$("#feedback").show();
$.each(result, function(index, value) {
valueid = value.replace(/\./g, 'ddeecciimmaallppooiinntt');
indexid = index.replace(/\./g, 'ddeecciimmaallppooiinntt');
valueid = valueid.replace(/\:/g, 'ccoolloonn');
indexid = indexid.replace(/\:/g, 'ccoolloonn');
valueid = valueid.replace(/\;/g, 'sseemmiiccoolloonn');
indexid = indexid.replace(/\;/g, 'sseemmiiccoolloonn');
valueid = valueid.replace(/\=/g, 'eeqquuaallss');
indexid = indexid.replace(/\=/g, 'eeqquuaallss');
if($("#submit"+valueid).length == 0){
$('.hintansarea').append("<p id=\"submit" + valueid + "\" class=\"hintsarea\"> </p>");
$('#submit'+valueid).append("<p> </p><b>Incorrect Answer: \b" + " " + value + "<p> <input id=\"submitbuttonfor" + indexid + "\" style=\"float: right; float: top;\" type=\"button\" class=\"submitbutton\" value=\"Submit a hint\"> <p id=\"hintstoshow" + valueid + "\"> <b><u>Hints in the Data Base:</u>\b </p></div>");
}
if(indexid.slice(0,22) != "There are no hints for"){
if($.inArray(index, HintUsed) == -1){
if($.inArray(index, HintShown) == -1){
console.log('yis.'); //style=\"float: left;\"
$('#hintstoshow'+valueid).append("<p \" id =\"thisparagraph" + indexid + "\">" + "<span style=\"display: inline-block; \"><input data-value=\"" + valueid + "\" id=\"" + indexid + "\" style=\"width:20px; height:35px;padding-top: 3px;\" type=\"button\" class=\"hintbutton\" data-rate=\"1\" data-icon=\"arrow-u\" value=\"^\"><input data-value=\"" + valueid + "\" id=\"" + indexid + "\" style=\"width:20px; height:35px; padding-top: 3px;\" type=\"button\" class=\"hintbutton\" data-rate=\"-1\" value=\"v\"><input data-value=\"" + valueid + "\" id=\"" + indexid + "\" style=\"width:20px; height:35px;padding-top: 3px;\" type=\"button\" class=\"hintbutton\" data-rate=\"0\" value=\"!\"></span>" + index + "</p>");
HintShown.push(index);}
}else{
if($.inArray(index, HintShown) == -1){
console.log('YIESSSSS');
$('#hintstoshow'+valueid).prepend("<p \" id =\"thisparagraph" + indexid + "\">" + "<span style=\"display: inline-block;\"><input data-value=\"" + valueid + "\" id=\"" + indexid + "\" type=\"button\" style=\"padding-top: 3px;width:20px; height:35px;\" class=\"hintbutton\" data-rate=\"1\" value=\"^\"><input data-value=\"" + valueid + "\" id=\"" + indexid + "\" style=\"padding-top: 3px;width:20px; height:35px;\" type=\"button\" class=\"hintbutton\" data-rate=\"-1\" value=\"v\"><input data-value=\"" + valueid + "\" style=\"padding-top: 3px;width:20px; height:35px;\" id=\"" + indexid + "\" type=\"button\" class=\"hintbutton\" data-rate=\"0\" value=\"!\"></span><font color=\"blue\">" + index + "</font></p>");
HintShown.push(index);
}}}else{
$('#hintstoshow'+valueid).empty();
console.log('index id is:' + indexid);
$('#hintstoshow'+valueid).append("<p style = \"color: blue;\" id=\"hintstoshow" + valueid + "\"data-value=\"" + valueid + "\"> <b>No hints exist in the database. (You received a default hint)</p> <p id=\"" + indexid + "\"data-value=\"" + valueid + "\" </p>");
}
});
}
$(document).on('click', '.submitbutton', function(){ //upvote
issubmittinghint = 0;
issubmitting += 1;
if(issubmitting == repeatcounter){
id = this.id;
id = id.slice(15);
value = document.getElementById(id).getAttribute('data-value');
$('.submitbutton').show();
$('.math').remove();
$('#submit').remove();
$(this).hide();
$('#hintstoshow' + value).prepend("<p><input type=\"text\" name=\"studentinput\" id=\"" + id + "\" class=\"math\" size=\"40\"><input id=\"submit\" type=\"button\" data-is=\"" + id + "\" class=\"button\" value=\"Submit Hint\"> </p>");
}})
$(document).on('click', '#submit', function(){
issubmittinghint += 1;
if(issubmittinghint == repeatcounter){
if($('.math').val() != null){
var valueid = String;
issubmitting = 0;
$('#submit').each(function(){
valueid = $(this).attr('data-is');
});
$('.submitbutton').show();
console.log('valueidworks' + valueid);
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'give_hint'),
data: JSON.stringify({"submission": $('.math').val(), "id": valueid}), //give hin for first incorrect answer
success: finish
});
$("#answer").val('');
data_value = document.getElementById(valueid).getAttribute('data-value');
data_value = String('hintstoshow' + data_value);
$(this).remove();
$('.math').remove();
document.getElementById("submitbuttonfor" + valueid).remove();
$('#submitbuttonfor' + valueid).remove();
$('#'+valueid).remove();
value = document.getElementById(id).getAttribute('data-value');
$('#hintstoshow' + value).prepend("<p> Thankyou! </p>");
$('#submit'+valueid).prepend('Thankyou for your hint!');
}}})
$(document).on('click', '.hintbutton', function(){ //upvote
canhint = 0;
id = this.id;
$(this).hide();
$('.hintbutton').each(function(){
if($(this).attr('id') == String(id)){
$(this).hide();}
});
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'rate_hint'),
data: JSON.stringify({"student_rating": $(this).attr('data-rate'), "answer": $(this).attr('id'), "value": $(this).attr('data-value')}),
success: finish
});})
function finish(result){
if(canhint == 0){
canhint = 1;
$('.Thankyou', element).text("Thankyou for your help!");
idtouse = String('thisparagraph' + result.origdata);
hint_rating = result.rating;
if(result.rating == "zzeerroo"){
hint_rating = 0;
}if(result.rating == "thiswasflagged"){
hint_rating = 999;
}
//idtouse = idtouse.replace('ddeecciimmaallppooiinntt', /\./g);
//idtouse = idtouse.replace('ccoolloonn', /\:/g);
//idtouse = idtouse.replace('sseemmiiccoolloonn', /\;/g);
//idtouse = idtouse.replace('eeqquuaallss', /\=/g);
$('p').each(function(){
if($(this).attr('id') == idtouse){
if(hint_rating != "You have already voted on this hint!" && hint_rating != 999){
$(this).prepend("<div><p style=\"float: left;\"><b> This hint's rating is:" + " " + " " + hint_rating + "</p></div>");
}if (hint_rating == "You have already voted on this hint!"){
$(this).prepend("<div><p style=\"float: left;\"><b> You have already voted on this hint.</p></div>");
}if (hint_rating == 999){
$(this).prepend("<div><p style=\"float: left;\"><b><font color=\"red\"> This hint has been flagged for moderation.</font></p></div>");}
}
});}
}
}
var returnpls = 0;
var repeatcounter = 0;
var canhint = 0;
var issubmitting = 0;
var issubmittinghint = 0;
var gotstudio = 0;
function CrowdXBlock(runtime, element){
var WrongAnswer = [];
var HintUsed = [];
var HintShown = [];
$("#answer").hide();
$(".problem").hide();
$("#feedback").hide();
vare = String;
varet = String;
vard = String;
$(".HintsToUse", element).text("Hints are enabled for this problem!");
console.debug('!!!!!!!!!!!!!!!!!!!!!!');
clearstufftoo();
repeatcounter += 1;
console.debug(repeatcounter);
Logger.listen('seq_next', null, clearstuff);
Logger.listen('seq_goto', null, clearstuff);
function clearstuff(event_type, data, element){
console.debug('next workt');
clearstufftoo(data);
}
function clearstufftoo(data){
HintUsed = [];
WrongAnswer = [];
returnpls = 0;
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'clear_stuff'),
data: JSON.stringify({"hello": "world"}),
});
console.log('cleartstuff');
console.debug(HintUsed)
}
Logger.listen('problem_graded', null, dostuff);
function dostuff(event_type, data, element){
returnpls += 1;
if(returnpls != repeatcounter){
console.debug(returnpls);
}else{
console.debug(returnpls);
console.debug(HintUsed);
$("#studentsubmit").val('');
vare = element;
varet = event_type;
vard = data;
console.debug("HOW MANY TIMES IS THIS HAPPENING?");
senddata(varet, vard, vare);
}
}
$(document).on('click', '.check.Check', function(){
returnpls = 0;
console.debug('clickt');
});
function senddata(varet, vard, vare){
if (vard[1].search(/class="correct/) === -1){
console.log('ITISNT');
console.debug('hmmm senddata is here');
$.ajax({ //that probably will be changed once i use response.search or something?
type: "POST", //if/when that is changed, remove checkreply and uncomment the else statement below
url: runtime.handlerUrl(element, 'get_hint'),
data: JSON.stringify({"submittedanswer": vard[0]}), //return student's incorrect answer here
//from vard[1] check id (long thing) and get class (correct or incorrect)
success: seehint
});
}else{console.debug("yay");
$('.correct', element).show();
$('.correct', element).text("You're correct! Please help us improve our hints by voting on them, or submit your own hint!");
$(".HintsToUse", element).text(" ");
console.debug("this should also only show up once...");
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'get_feedback'),
data: JSON.stringify({"hello": "world"}),
success: getfeedback
});}
}
function seehint(result){//use html to show these results somewhere i guess
console.debug('seehint');
HintUsed.push(result.HintsToUse);
$('.HintsToUse', element).text("Hint:" + " " + result.HintsToUse); //text(result.self.hints?)
console.debug('hint:' + ' ' + result.HintsToUse);
}
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'get_feedback'),
data: JSON.stringify({"hello": "world"}),
success: getfeedback
});
console.debug('aw');
$(".hinter_purge").append("yay");
function getfeedback(result){
$("#answer").show();
$(".problem").show();
$("#feedback").show();
$.each(result, function(index, value) {
valueid = value.replace(/\./g, 'ddeecciimmaallppooiinntt');
indexid = index.replace(/\./g, 'ddeecciimmaallppooiinntt');
valueid = valueid.replace(/\:/g, 'ccoolloonn');
indexid = indexid.replace(/\:/g, 'ccoolloonn');
valueid = valueid.replace(/\;/g, 'sseemmiiccoolloonn');
indexid = indexid.replace(/\;/g, 'sseemmiiccoolloonn');
valueid = valueid.replace(/\=/g, 'eeqquuaallss');
indexid = indexid.replace(/\=/g, 'eeqquuaallss');
if($("#submit"+valueid).length == 0){
$('.hintansarea').append("<p id=\"submit" + valueid + "\" class=\"hintsarea\"> </p>");
$('#submit'+valueid).append("<p> </p><b>Incorrect Answer: \b" + " " + value + "<p> <input id=\"submitbuttonfor" + indexid + "\" style=\"float: right; float: top;\" type=\"button\" class=\"submitbutton\" value=\"Submit a hint\"> <p id=\"hintstoshow" + valueid + "\"> <b><u>Hints in the Data Base:</u>\b </p></div>");
}
if(indexid.slice(0,22) != "There are no hints for"){
if($.inArray(index, HintUsed) == -1){
if($.inArray(index, HintShown) == -1){
console.log('yis.'); //style=\"float: left;\"
$('#hintstoshow'+valueid).append("<p \" id =\"thisparagraph" + indexid + "\">" + "<span style=\"display: inline-block; \"><input data-value=\"" + valueid + "\" id=\"" + indexid + "\" style=\"width:20px; height:35px;padding-top: 3px;\" type=\"button\" class=\"hintbutton\" data-rate=\"1\" data-icon=\"arrow-u\" value=\"^\"><input data-value=\"" + valueid + "\" id=\"" + indexid + "\" style=\"width:20px; height:35px; padding-top: 3px;\" type=\"button\" class=\"hintbutton\" data-rate=\"-1\" value=\"v\"><input data-value=\"" + valueid + "\" id=\"" + indexid + "\" style=\"width:20px; height:35px;padding-top: 3px;\" type=\"button\" class=\"hintbutton\" data-rate=\"0\" value=\"!\"></span>" + index + "</p>");
HintShown.push(index);}
}else{
if($.inArray(index, HintShown) == -1){
console.log('YIESSSSS');
$('#hintstoshow'+valueid).prepend("<p \" id =\"thisparagraph" + indexid + "\">" + "<span style=\"display: inline-block;\"><input data-value=\"" + valueid + "\" id=\"" + indexid + "\" type=\"button\" style=\"padding-top: 3px;width:20px; height:35px;\" class=\"hintbutton\" data-rate=\"1\" value=\"^\"><input data-value=\"" + valueid + "\" id=\"" + indexid + "\" style=\"padding-top: 3px;width:20px; height:35px;\" type=\"button\" class=\"hintbutton\" data-rate=\"-1\" value=\"v\"><input data-value=\"" + valueid + "\" style=\"padding-top: 3px;width:20px; height:35px;\" id=\"" + indexid + "\" type=\"button\" class=\"hintbutton\" data-rate=\"0\" value=\"!\"></span><font color=\"blue\">" + index + "</font></p>");
HintShown.push(index);
}}}else{
$('#hintstoshow'+valueid).empty();
console.log('index id is:' + indexid);
$('#hintstoshow'+valueid).append("<p style = \"color: blue;\" id=\"hintstoshow" + valueid + "\"data-value=\"" + valueid + "\"> <b>No hints exist in the database. (You received a default hint)</p> <p id=\"" + indexid + "\"data-value=\"" + valueid + "\" </p>");
}
});
}
//"<p \" id =\"thisparagraph" + indexid + "\">" + "<span style=\"display: inline-block; \"><input data-value=\"" + valueid + "\" id=\"" + indexid + "\" style=\"width:20px; height:20px;padding-top: 3px;\" type=\"button\" class=\"hintbutton\" data-rate=\"1\" data-icon=\"arrow-u\" value=\"^\"><input data-value=\"" + valueid + "\" id=\"" + indexid + "\" style=\"width:20px; height:20px; padding-top: 3px;\" type=\"button\" class=\"hintbutton\" data-rate=\"-1\" value=\"v\"><input data-value=\"" + valueid + "\" id=\"" + indexid + "\" style=\"width:20px; height:20px;padding-top: 3px;\" type=\"button\" class=\"hintbutton\" data-rate=\"0\" value=\"!\"></span><span class=\"thetextareahere\">" + index + "</span></p>"
/*function getridofdups(){ //duplicates occur whenever you shift between units within a section.
var seen = {};
$('p').each(function() {
var txt = String($(this).attr('id'));
console.debug(txt);
if(txt.slice(0,13) == 'thisparagraph'){
if (seen[txt]){
$(this).remove();
}else{
seen[txt] = true;}
}});}*/
$(document).on('click', '.submitbutton', function(){ //upvote
issubmittinghint = 0;
issubmitting += 1;
if(issubmitting != repeatcounter){
console.debug(returnpls);
}else{
id = this.id;
id = id.slice(15);
console.log(id);
value = document.getElementById(id).getAttribute('data-value');
$('.submitbutton').show();
$('.math').remove();
$('#submit').remove();
$(this).hide();
$('#hintstoshow' + value).prepend("<p><input type=\"text\" name=\"studentinput\" id=\"" + id + "\" class=\"math\" size=\"40\"><input id=\"submit\" type=\"button\" data-is=\"" + id + "\" class=\"button\" value=\"Submit Hint\"> </p>");
}})
$(document).on('click', '#submit', function(){
issubmittinghint += 1;
if(issubmittinghint != repeatcounter){
console.debug(returnpls);
}else{
if($('.math').val() != null){
var valueid = String;
issubmitting = 0;
$('#submit').each(function(){
valueid = $(this).attr('data-is');
});
$('.submitbutton').show();
console.log('valueidworks' + valueid);
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'give_hint'),
data: JSON.stringify({"submission": $('.math').val(), "id": valueid}), //give hin for first incorrect answer
success: finish
});
$("#answer").val('');
dataval = document.getElementById(valueid).getAttribute('data-value');
dataval = String('hintstoshow' + dataval);
$(this).remove();
$('.math').remove();
document.getElementById("submitbuttonfor" + valueid).remove();
$('#submitbuttonfor' + valueid).remove();
$('#'+valueid).remove();
console.log('pls');
value = document.getElementById(id).getAttribute('data-value');
$('#hintstoshow' + value).prepend("<p> Thankyou! </p>");
$('#submit'+valueid).prepend('Thankyou for your hint!');
}}})
$(document).on('click', '.hintbutton', function(){ //upvote
canhint = 0;
id = this.id;
console.log('hi world');
$(this).hide();
$('.hintbutton').each(function(){
if($(this).attr('id') == String(id)){
$(this).hide();}
});
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'rate_hint'),
data: JSON.stringify({"rating": $(this).attr('data-rate'), "ansnum": $(this).attr('id'), "value": $(this).attr('data-value')}),
success: finish
});})
function finish(result){
if(canhint != 0){
console.debug('canthint');
}else{
canhint = 1;
console.debug('plsdontbehappeningalot');
$('.Thankyou', element).text("Thankyou for your help!");
idtouse = String('thisparagraph' + result.origdata);
ratin = result.rating;
if(result.rating == "zzeerroo"){
ratin = 0;
}if(result.rating == "thiswasflaggedyo"){
ratin = 999;
}
//idtouse = idtouse.replace('ddeecciimmaallppooiinntt', /\./g);
//idtouse = idtouse.replace('ccoolloonn', /\:/g);
//idtouse = idtouse.replace('sseemmiiccoolloonn', /\;/g);
//idtouse = idtouse.replace('eeqquuaallss', /\=/g);
$('p').each(function(){
if($(this).attr('id') == idtouse){
if(ratin != "You have already voted on this hint!" && ratin != 999){
$(this).prepend("<div><p style=\"float: left;\"><b> This hint's rating is:" + " " + " " + ratin + "</p></div>");
}if (ratin == "You have already voted on this hint!"){
$(this).prepend("<div><p style=\"float: left;\"><b> You have already voted on this hint.</p></div>");
}if (ratin == 999){
$(this).prepend("<div><p style=\"float: left;\"><b><font color=\"red\"> This hint has been flagged for moderation.</font></p></div>");}
}
});}
}
function clearstates(){
$('.Thankyou', element).text();
$('.correct', element).hide();
$( ".hintansarea" ).empty();
$("#answer").hide();
$(".problem").hide();
}
}
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