Commit defc2ba5 by Sola Committed by Piotr Mitros

many changes

parent b6faa787
CHANGEchange This is the Crowd Sourced Hinter XBlock. The Crowd Sourced Hinter provides students
with hints when they incorrectly answer a numerical-or text-input problem. Additionally,
the hints that are provided to students are student-made hints.
This XBlock is still under construction. Functionalities to set default hints, properly moderate flagged hints, and lots of improvements to user interface are to be done soon.
backlog: https://docs.google.com/a/edx.org/document/d/1lOBLZWohwRmnfgwz53gc4b4GdfP4EER3UNohdYfcEJU/edit#
![CrowdSourcedHinter Hint Screenshot](crowdsourcedhinter_hint.png)
![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 will be "crowdxblock" in advanced settings.
After creating a new unit, add the crowdsourcedhinter xblock just like any other custom xblock. The name of the crowd sourced hinter may not show up for some reason, but an empty space where its name should be will be clickable.
Testing the functionality of the crowd sourced hinter works best when switching between different users and answering the same problem within a unit.
After a student incorrectly answers a problem, a hint will be provided. This hint will either be a default hint or a hint that has been submitted specifically for that incorrect answer (if such a hint has previously been submitted). If multiple hints exist for a single incorrect answer, the current system will choose the highest rated hint to show the student.
After a student has correctly answered the problem, they can give feedback on hints. Students can upvote, downvote, or flag the hint that they recieved for each aswer as well as 2 other hints that exist for that aswer (if these exist). Students also can submit new hints for their answer.
# pylint: disable=line-too-long
# pylint: disable=unused-argument
import pkg_resources import pkg_resources
import logging import logging
import operator import operator
...@@ -5,23 +7,47 @@ import random ...@@ -5,23 +7,47 @@ import random
import ast import ast
from xblock.core import XBlock from xblock.core import XBlock
from xblock.fields import Scope, Integer, Boolean, String, Dict, List from xblock.fields import Scope, Dict, List
from xblock.fragment import Fragment from xblock.fragment import Fragment
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
#get_hint and get_feedback are in
class CrowdXBlock(XBlock): class CrowdXBlock(XBlock):
correctanswer = String(default="2.0", scope=Scope.content) #should be irrelevant for completed version """
hints = Dict(default={"2": {"another 2 hint":0, "yet another 2 hint":0,"Keep significant figures in mind.":1, "So close yet so far.":0, "Try adding a .0.":0}, "8.6": {"You might have added all of the horizontal forces together to equal a total of 21 for the horizontal component of this object's force. The two forces are facing opposite direction, so you need to subtract them.":1, "Try 12-9 for the total horizontal force.":0}, "1.2": {"Did you remember to add in the vertical component of force for this object?":0}}, scope=Scope.content) This is the Crowd Sourced Hinter XBlock. This Xblock seeks to provide students with hints
#All hints. sorted by type of mistake. type_of_incorrect_answer{"hint":rating, "hint":rating} that specifically address their mistake. Additionally, the hints that this Xblock shows
HintsToUse = Dict(default={}, scope=Scope.user_state) #Dict of hints to provide user are created by the students themselves. This doc string will probably be edited later.
WrongAnswers = List(default=[], scope=Scope.user_state) #List of mistakes made by user """
DefaultHints = Dict(default={"Start with the equation F=ma":2, "This object has horizontal and vertical components of force. Solve for the total force in each direction, then compare it to the final acceleration":1, "A small example: If an object has a force of 10N applied to it in an upward direction and it's acceleration is 1m/s^2, the mass of that object is 10.0 kg. F=ma, 10N=m*(1m/s^2), m=10/1, m=10.":1}, scope=Scope.content) #Default hints in case no incorrect answers in hints match the user's mistake hint_database = Dict(default={"guess": {"hint": 1}}, scope=Scope.user_state_summary)
Used = List(default=[], scope=Scope.user_state)#List of used hints from HintsToUse # database of hints. hints are stored as such: {"incorrect_answer": {"hint": rating}}. each key (incorrect answer)
Voted = Integer(default=0, scope=Scope.user_state)#prevent multiple votes/hint submission # 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={"default hint 1": 2, "default hint 2": 1, "default hint 3": 1}, 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={"bad_hint": 1, "other_bad_hint": 4, "another_bad_hint": 3}, 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): def student_view(self, context=None):
"""
Student_view docstring. May be edited later. This is a placeholder for now.
"""
html = self.resource_string("static/html/crowdxblock.html") html = self.resource_string("static/html/crowdxblock.html")
frag = Fragment(html.format(self=self)) frag = Fragment(html.format(self=self))
frag.add_css(self.resource_string("static/css/crowdxblock.css")) frag.add_css(self.resource_string("static/css/crowdxblock.css"))
...@@ -29,162 +55,325 @@ class CrowdXBlock(XBlock): ...@@ -29,162 +55,325 @@ class CrowdXBlock(XBlock):
frag.initialize_js('CrowdXBlock') frag.initialize_js('CrowdXBlock')
return frag return frag
def studio_view(self, context=None):
"""
Studio_view docstring. This is also a place holder.
"""
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): def resource_string(self, path):
"""
This is also a place holder docstring.
"""
data = pkg_resources.resource_string(__name__, path) data = pkg_resources.resource_string(__name__, path)
return data.decode("utf8") return data.decode("utf8")
@XBlock.json_handler @XBlock.json_handler
def get_hint(self, data, suffix=''): #get hints into HintsToUse dict, pass on to user 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 = []
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 = str(data["submittedanswer"])
if str(data["submittedanswer"]) == self.correctanswer: answer = answer.lower()
return{"correct": 1} foundeq = 0
hintsarehere = 0 hints_used = 0
self.WrongAnswers.append(str(data["submittedanswer"])) #add user's incorrect answer to WrongAnswers if "=" in answer:
for key in self.hints: if foundeq == 0:
temphints = str(self.hints[str(key)]) #perhaps a better way to do this exists, but for now this works foundeq = 1
if str(key) == str(data["submittedanswer"]): eqplace = answer.index("=") + 1
self.HintsToUse = {} answer = answer[eqplace:]
self.HintsToUse.update(ast.literal_eval(temphints)) self.find_hints(answer)
for key in self.HintsToUse: if str(answer) not in self.hint_database:
if key not in self.Used: self.hint_database[str(answer)] = {}
hintsarehere = 1 self.HintsToUse.clear()
if hintsarehere == 0:
self.HintsToUse.update(self.DefaultHints) #Use DefaultHints if there aren't enough other hints
if str(data["submittedanswer"]) not in self.hints:
self.hints[str(data["submittedanswer"])] = {} #add user's incorrect answer to WrongAnswers
self.HintsToUse = {}
self.HintsToUse.update(self.DefaultHints) self.HintsToUse.update(self.DefaultHints)
if max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0] not in self.Used: if max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0] not in self.Used:
self.Used.append(max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0]) #Highest rated hint is shown first if max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0] not in self.Flagged.keys():
return {'HintsToUse': max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0]} self.Used.append(max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0])
return {'HintsToUse': max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0]}
else: else:
NotUsed = random.choice(self.HintsToUse.keys()) not_used = random.choice(self.HintsToUse.keys())
if len(self.HintsToUse) > len(self.Used): for used_hints in self.Used:
while NotUsed in self.Used: if used_hints in self.HintsToUse.keys():
NotUsed = random.choice(self.HintsToUse.keys()) #Choose random hint that hasn't already been Used hints_used += 1
if str(len(self.HintsToUse)) > str(hints_used):
while not_used in self.Used:
while not_used in self.Flagged.keys():
not_used = random.choice(self.HintsToUse.keys())
else: else:
usedef = 0 self.Used.append(str("There are no hints for" + " " + answer))
for h in self.HintsToUse: return {'HintsToUse': "Sorry, there are no more hints for this answer."}
if str(h) not in self.Used: self.Used.append(not_used)
NotUsed = h return {'HintsToUse': not_used}
usedef = 1
if usedef == 0: def find_hints(self, answer):
self.Used.append(str("There are no hints for" + " " + data["submittedanswer"])) """
print str(self.Used) This function is used to find all appropriate hints that would be provided for
return {'HintsToUse': "Sorry, there are no more hints for this answer."} an incorrect answer. Flagged hints are not added into the HintsToUse dictionary.
self.Used.append(NotUsed)
return {'HintsToUse': NotUsed} #note to self dont let python get into endless notused loop Args:
answer: This is equal to answer from get_hint, the answer the student submitted
"""
hints_exist = 0
isflagged = []
self.WrongAnswers.append(str(answer))
for answer_keys in self.hint_database:
temphints = str(self.hint_database[str(answer_keys)])
if str(answer_keys) == str(answer):
self.HintsToUse.clear()
self.HintsToUse.update(ast.literal_eval(temphints))
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:
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 @XBlock.json_handler
def get_feedback(self, data, suffix=''): #start feedback, sent hint/answer data def get_feedback(self, data, suffix=''):
feedbackdict = {} """
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 = {}
feedbacklist = [] feedbacklist = []
number_of_hints = 0
if len(self.WrongAnswers) == 0: if len(self.WrongAnswers) == 0:
return #Do nothing if no mistakes were made return
else: #lenth of Used will be used to dictate flow of feedback else:
for i in range(0, len(self.Used)): for index in range(0, len(self.Used)):
ans = str('wngans' + str(i)) for answer_keys in self.hint_database:
hnt = str('hntusd' + str(i)) if str(self.Used[index]) in self.hint_database[str(answer_keys)]:
for keys in self.hints: feedback_data[str(self.Used[index])] = str(self.WrongAnswers[index])
if str(self.Used[i]) in self.hints[str(keys)]: feedbacklist.append(str(self.Used[index]))
feedbackdict[str(self.Used[i])] = str(self.WrongAnswers[i]) for answer_keys in self.hint_database:
feedbacklist.append(str(self.Used[i])) if str(answer_keys) == str(self.WrongAnswers[index]):
for key in self.hints: if str(len(self.hint_database[str(answer_keys)])) != str(0):
if str(key) == str(self.WrongAnswers[i]): number_of_hints = 0
if len(self.hints[str(key)]) != 0: hint_key_shuffled = self.hint_database[str(answer_keys)].keys()
howmanyhints = 0 random.shuffle(hint_key_shuffled)
print('hints are' + str(self.hints[str(key)])) for random_hint_key in hint_key_shuffled:
keysh = self.hints[str(key)].keys() if str(random_hint_key) not in self.Flagged.keys():
random.shuffle(keysh) if number_of_hints < 3:
print('keysh is ' + str(keysh)) number_of_hints += 1
for nextkey in keysh: feedback_data[str(random_hint_key)] = str(self.WrongAnswers[index])
if howmanyhints < 3: self.WrongAnswers.append(str(self.WrongAnswers[index]))
howmanyhints += 1 #limit number of hints shown self.Used.append(str(random_hint_key))
feedbackdict[str(nextkey)] = str(self.WrongAnswers[i])
self.WrongAnswers.append(str(self.WrongAnswers[i]))
self.Used.append(str(nextkey))
else: else:
feedbackdict[str("There are no hints for" + " " + str(self.WrongAnswers[i]))] = str(self.WrongAnswers[i]) self.no_hints(index)
self.WrongAnswers.append(str(self.WrongAnswers[i])) feedback_data[str("There are no hints for" + " " + str(self.WrongAnswers[index]))] = str(self.WrongAnswers[index])
self.Used.append(str("There are no hints for" + " " + str(self.WrongAnswers[i]))) return feedback_data
print("used is " + str(self.Used))
print(str(feedbackdict)) def no_hints(self, index):
return feedbackdict """
This function is used when no hints exist for an answer. The feedback_data within
@XBlock.json_handler #add 1 or -1 to rating of a hint 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=''): def rate_hint(self, data, suffix=''):
data['ansnum'] = data['ansnum'].replace('ddeecciimmaallppooiinntt', '.') """
data['ansnum'] = data['ansnum'].replace('qquueessttiioonnmmaarrkk', '?') Used to facilitate hint rating by students. Ratings are -1, 1, or 0. -1 is downvote, 1 is upvote, and 0 is
data['ansnum'] = data['ansnum'].replace('ccoolloonn', ':') when a student flags a hint. 'zzeerroo' is returned to JS when a hint's rating is 0 because whenever 0 was
data['ansnum'] = data['ansnum'].replace('sseemmiiccoolloonn', ';') simply returned, JS would interpret it as null.
data['ansnum'] = data['ansnum'].replace('qquuoottaattiioonnmmaarrkkss', '"')
for usdkey in self.Used: Hint ratings in hint_database are updated and the resulting hint rating (or flagged status) is returned to JS.
if str(usdkey) == str(data['ansnum']):
print str(self.Used) Args:
ansnum = self.Used.index(str(data['ansnum'])) data['answer']: The incorrect answer that corresponds to the hint that is being voted on
if self.Voted == 0: data['value']: The hint that is being voted on
for key in self.DefaultHints: data['student_rating']: The rating chosen by the student. The value is -1, 1, or 0.
if key == self.Used[int(ansnum)]: #rating for hints in DefaultHints
self.DefaultHints[str(key)] += int(data["rating"]) Returns:
self.Voted = 1 "rating": The rating of the hint. 'zzeerroo' is returned if the hint's rating is 0.
return If the hint has already been voted on, 'You have already voted on this hint!'
for key in self.hints: will be returned to JS.
tempdict = str(self.hints[str(key)]) #rate hint that is in hints """
tempdict = (ast.literal_eval(tempdict)) original_data = data['answer']
if str(key) == str(self.WrongAnswers[ansnum]): #ansnum will the the answer/hint pair that is selected answer_data = data['answer']
tempdict[self.Used[int(ansnum)]] += int(data["rating"]) data_rating = data['student_rating']
self.hints[str(key)] = tempdict data_value = data['value']
self.Voted = 1 answer_data = self.remove_symbols(answer_data)
return if str(data['student_rating']) == str(0):
for key in self.hints: self.hint_flagged(data['value'], answer_data)
if str(key) == str(data['value']): return {"rating": 'thiswasflagged', 'origdata': original_data}
for nextkey in self.hints[key]: if str(answer_data) not in self.Voted:
if str(nextkey) == str(data['ansnum']): self.Voted.append(str(answer_data))
ansnum = self.hints[str(key)[str(nextkey)]] rating = self.change_rating(data_value, int(data_rating), answer_data)
tempdict = str(self.hints[str(key)]) #rate hint that is in hints print str(self.change_rating(data['value'], int(data['student_rating']), answer_data))
tempdict = (ast.literal_eval(tempdict)) if str(rating) == str(0):
tempdict[self.hints[str(key)[ansnum]]] += 1 return {"rating": str('zzeerroo'), 'origdata': original_data}
selff.hints[str(key)] = tempdict else:
self.Voted = 1 return {"rating": str(rating), 'origdata': original_data}
return 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):
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 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 @XBlock.json_handler
def give_hint(self, data, suffix=''): #add student-made hint into hints def give_hint(self, data, suffix=''):
data['submission'] = data['submission'].replace('ddeecciimmaallppooiinntt', '.') """
data['id'] = data['id'].replace('ddeecciimmaallppooiinntt', '.') This function adds a new hint submitted by the student into the hint_database.
if self.Voted == 0:
for key in self.hints: Args:
print(str(self.Used)) data['submission']: This is the text of the new hint that the student has submitted.
print(str(self.Used.index(str(data['id'])))) data['answer']: This is the incorrect answer for which the student is submitting a new hint.
if str(key) == self.WrongAnswers[self.Used.index(str(data['id']))]: """
if str(data['submission']) not in self.hints[str(key)]: submission = data['submission'].replace('ddeecciimmaallppooiinntt', '.')
tempdict = str(self.hints[str(key)]) #rate hint that is in hints hint_id = data['answer'].replace('ddeecciimmaallppooiinntt', '.')
tempdict = (ast.literal_eval(tempdict)) for answer_keys in self.hint_database:
tempdict.update({data['submission']: 0}) if str(answer_keys) == self.WrongAnswers[self.Used.index(hint_id)]:
self.hints[str(key)] = tempdict if str(submission) not in self.hint_database[str(answer_keys)]:
self.Voted = 1 temporary_dictionary = str(self.hint_database[str(answer_keys)])
return temporary_dictionary = (ast.literal_eval(temporary_dictionary))
else: temporary_dictionary.update({submission: 0})
ansnum = self.Used.index(data['submission']) self.hint_database[str(answer_keys)] = temporary_dictionary
for key in self.DefaultHints: return
if key == self.Used[int(ansnum)]: #rating for hints in DefaultHints else:
self.DefaultHints[str(key)] += int(1) hint_index = self.Used.index(submission)
self.Voted = 1 for default_hints in self.DefaultHints:
return if default_hints == self.Used[int(hint_index)]:
for key in self.hints: self.DefaultHints[str(default_hints)] += int(1)
tempdict = str(self.hints[str(key)]) #rate hint that is in hints return
tempdict = (ast.literal_eval(tempdict)) for answer_keys in self.hint_database:
if str(key) == str(self.WrongAnswers[int(ansnum)]): #ansnum will the the answer/hint pair that is selected temporary_dictionary = str(self.hint_database[str(answer_keys)])
tempdict[self.Used[int(ansnum)]] += int(1) temporary_dictionary = (ast.literal_eval(temporary_dictionary))
self.hints[str(key)] = tempdict if str(answer_keys) == str(self.WrongAnswers[int(hint_index)]):
self.Voted = 1 temporary_dictionary[self.Used[int(hint_index)]] += int(data['rating'])
self.hint_database[str(answer_keys)] = temporary_dictionary
@XBlock.json_handler @XBlock.json_handler
def clear_states(self, data, suffix=''): def studiodata(self, data, suffix=''):
self.Used = [] """
self.HintsToUse = {} This function serves to return the dictionary of flagged hints to JS. This is intended for use in
self.Voted = 0 the studio_view, which is under construction at the moment
self.WrongAnswers = [] """
return self.Flagged
@staticmethod @staticmethod
def workbench_scenarios(): def workbench_scenarios():
...@@ -196,4 +385,3 @@ class CrowdXBlock(XBlock): ...@@ -196,4 +385,3 @@ class CrowdXBlock(XBlock):
</vertical_demo> </vertical_demo>
"""), """),
] ]
# pylint: disable=line-too-long
# pylint: disable=unused-argument
import pkg_resources import pkg_resources
import logging import logging
import operator import operator
...@@ -5,23 +7,47 @@ import random ...@@ -5,23 +7,47 @@ import random
import ast import ast
from xblock.core import XBlock from xblock.core import XBlock
from xblock.fields import Scope, Integer, Boolean, String, Dict, List from xblock.fields import Scope, Dict, List
from xblock.fragment import Fragment from xblock.fragment import Fragment
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
#get_hint and get_feedback are in
class CrowdXBlock(XBlock): class CrowdXBlock(XBlock):
correctanswer = String(default="2.0", scope=Scope.content) #should be irrelevant for completed version """
hints = Dict(default={"2": {"another 2 hint":0, "yet another 2 hint":0,"Keep significant figures in mind.":1, "So close yet so far.":0, "Try adding a .0.":0}, "8.6": {"You might have added all of the horizontal forces together to equal a total of 21 for the horizontal component of this object's force. The two forces are facing opposite direction, so you need to subtract them.":1, "Try 12-9 for the total horizontal force.":0}, "1.2": {"Did you remember to add in the vertical component of force for this object?":0}}, scope=Scope.content) This is the Crowd Sourced Hinter XBlock. This Xblock seeks to provide students with hints
#All hints. sorted by type of mistake. type_of_incorrect_answer{"hint":rating, "hint":rating} that specifically address their mistake. Additionally, the hints that this Xblock shows
HintsToUse = Dict(default={}, scope=Scope.user_state) #Dict of hints to provide user are created by the students themselves. This doc string will probably be edited later.
WrongAnswers = List(default=[], scope=Scope.user_state) #List of mistakes made by user """
DefaultHints = Dict(default={"Start with the equation F=ma":2, "This object has horizontal and vertical components of force. Solve for the total force in each direction, then compare it to the final acceleration":1, "A small example: If an object has a force of 10N applied to it in an upward direction and it's acceleration is 1m/s^2, the mass of that object is 10.0 kg. F=ma, 10N=m*(1m/s^2), m=10/1, m=10.":1}, scope=Scope.content) #Default hints in case no incorrect answers in hints match the user's mistake hint_database = Dict(default={"guess": {"hint": 1}}, scope=Scope.user_state_summary)
Used = List(default=[], scope=Scope.user_state)#List of used hints from HintsToUse # database of hints. hints are stored as such: {"incorrect_answer": {"hint": rating}}. each key (incorrect answer)
Voted = Integer(default=0, scope=Scope.user_state)#prevent multiple votes/hint submission # 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={"default hint 1": 2, "default hint 2": 1, "default hint 3": 1}, 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={"bad_hint": 1, "other_bad_hint": 4, "another_bad_hint": 3}, 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): def student_view(self, context=None):
"""
Student_view docstring. May be edited later. This is a placeholder for now.
"""
html = self.resource_string("static/html/crowdxblock.html") html = self.resource_string("static/html/crowdxblock.html")
frag = Fragment(html.format(self=self)) frag = Fragment(html.format(self=self))
frag.add_css(self.resource_string("static/css/crowdxblock.css")) frag.add_css(self.resource_string("static/css/crowdxblock.css"))
...@@ -29,161 +55,325 @@ class CrowdXBlock(XBlock): ...@@ -29,161 +55,325 @@ class CrowdXBlock(XBlock):
frag.initialize_js('CrowdXBlock') frag.initialize_js('CrowdXBlock')
return frag return frag
def studio_view(self, context=None):
"""
Studio_view docstring. This is also a place holder.
"""
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): def resource_string(self, path):
"""
This is also a place holder docstring.
"""
data = pkg_resources.resource_string(__name__, path) data = pkg_resources.resource_string(__name__, path)
return data.decode("utf8") return data.decode("utf8")
@XBlock.json_handler @XBlock.json_handler
def get_hint(self, data, suffix=''): #get hints into HintsToUse dict, pass on to user 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 = []
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 = str(data["submittedanswer"])
if str(data["submittedanswer"]) == self.correctanswer: answer = answer.lower()
return{"correct": 1} foundeq = 0
hintsarehere = 0 hints_used = 0
self.WrongAnswers.append(str(data["submittedanswer"])) #add user's incorrect answer to WrongAnswers if "=" in answer:
for key in self.hints: if foundeq == 0:
temphints = str(self.hints[str(key)]) #perhaps a better way to do this exists, but for now this works foundeq = 1
if str(key) == str(data["submittedanswer"]): eqplace = answer.index("=") + 1
self.HintsToUse = {} answer = answer[eqplace:]
self.HintsToUse.update(ast.literal_eval(temphints)) self.find_hints(answer)
for key in self.HintsToUse: if str(answer) not in self.hint_database:
if key not in self.Used: self.hint_database[str(answer)] = {}
hintsarehere = 1 self.HintsToUse.clear()
if hintsarehere == 0:
self.HintsToUse.update(self.DefaultHints) #Use DefaultHints if there aren't enough other hints
if str(data["submittedanswer"]) not in self.hints:
self.hints[str(data["submittedanswer"])] = {} #add user's incorrect answer to WrongAnswers
self.HintsToUse = {}
self.HintsToUse.update(self.DefaultHints) self.HintsToUse.update(self.DefaultHints)
if max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0] not in self.Used: if max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0] not in self.Used:
self.Used.append(max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0]) #Highest rated hint is shown first if max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0] not in self.Flagged.keys():
return {'HintsToUse': max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0]} self.Used.append(max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0])
return {'HintsToUse': max(self.HintsToUse.iteritems(), key=operator.itemgetter(1))[0]}
else: else:
NotUsed = random.choice(self.HintsToUse.keys()) not_used = random.choice(self.HintsToUse.keys())
if len(self.HintsToUse) > len(self.Used): for used_hints in self.Used:
while NotUsed in self.Used: if used_hints in self.HintsToUse.keys():
NotUsed = random.choice(self.HintsToUse.keys()) #Choose random hint that hasn't already been Used hints_used += 1
if str(len(self.HintsToUse)) > str(hints_used):
while not_used in self.Used:
while not_used in self.Flagged.keys():
not_used = random.choice(self.HintsToUse.keys())
else: else:
usedef = 0 self.Used.append(str("There are no hints for" + " " + answer))
for h in self.HintsToUse: return {'HintsToUse': "Sorry, there are no more hints for this answer."}
if str(h) not in self.Used: self.Used.append(not_used)
NotUsed = h return {'HintsToUse': not_used}
usedef = 1
if usedef == 0: def find_hints(self, answer):
self.Used.append(str("There are no hints for" + " " + data["submittedanswer"])) """
print str(self.Used) This function is used to find all appropriate hints that would be provided for
return {'HintsToUse': "Sorry, there are no more hints for this answer."} an incorrect answer. Flagged hints are not added into the HintsToUse dictionary.
self.Used.append(NotUsed)
return {'HintsToUse': NotUsed} #note to self dont let python get into endless notused loop Args:
answer: This is equal to answer from get_hint, the answer the student submitted
"""
hints_exist = 0
isflagged = []
self.WrongAnswers.append(str(answer))
for answer_keys in self.hint_database:
temphints = str(self.hint_database[str(answer_keys)])
if str(answer_keys) == str(answer):
self.HintsToUse.clear()
self.HintsToUse.update(ast.literal_eval(temphints))
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:
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 @XBlock.json_handler
def get_feedback(self, data, suffix=''): #start feedback, sent hint/answer data def get_feedback(self, data, suffix=''):
feedbackdict = {} """
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 = {}
feedbacklist = [] feedbacklist = []
number_of_hints = 0
if len(self.WrongAnswers) == 0: if len(self.WrongAnswers) == 0:
return #Do nothing if no mistakes were made return
else: #lenth of Used will be used to dictate flow of feedback else:
for i in range(0, len(self.Used)): for index in range(0, len(self.Used)):
ans = str('wngans' + str(i)) for answer_keys in self.hint_database:
hnt = str('hntusd' + str(i)) if str(self.Used[index]) in self.hint_database[str(answer_keys)]:
for keys in self.hints: feedback_data[str(self.Used[index])] = str(self.WrongAnswers[index])
if str(self.Used[i]) in self.hints[str(keys)]: feedbacklist.append(str(self.Used[index]))
feedbackdict[str(self.Used[i])] = str(self.WrongAnswers[i]) for answer_keys in self.hint_database:
feedbacklist.append(str(self.Used[i])) if str(answer_keys) == str(self.WrongAnswers[index]):
for key in self.hints: if str(len(self.hint_database[str(answer_keys)])) != str(0):
if str(key) == str(self.WrongAnswers[i]): number_of_hints = 0
if len(self.hints[str(key)]) != 0: hint_key_shuffled = self.hint_database[str(answer_keys)].keys()
howmanyhints = 0 random.shuffle(hint_key_shuffled)
print('hints are' + str(self.hints[str(key)])) for random_hint_key in hint_key_shuffled:
keysh = self.hints[str(key)].keys() if str(random_hint_key) not in self.Flagged.keys():
random.shuffle(keysh) if number_of_hints < 3:
print('keysh is ' + str(keysh)) number_of_hints += 1
for nextkey in keysh: feedback_data[str(random_hint_key)] = str(self.WrongAnswers[index])
if howmanyhints < 3: self.WrongAnswers.append(str(self.WrongAnswers[index]))
howmanyhints += 1 #limit number of hints shown self.Used.append(str(random_hint_key))
feedbackdict[str(nextkey)] = str(self.WrongAnswers[i])
self.WrongAnswers.append(str(self.WrongAnswers[i]))
self.Used.append(str(nextkey))
else: else:
feedbackdict[str("There are no hints for" + " " + str(self.WrongAnswers[i]))] = str(self.WrongAnswers[i]) self.no_hints(index)
self.WrongAnswers.append(str(self.WrongAnswers[i])) feedback_data[str("There are no hints for" + " " + str(self.WrongAnswers[index]))] = str(self.WrongAnswers[index])
self.Used.append(str("There are no hints for" + " " + str(self.WrongAnswers[i]))) return feedback_data
print("used is " + str(self.Used))
print(str(feedbackdict)) def no_hints(self, index):
return feedbackdict """
This function is used when no hints exist for an answer. The feedback_data within
@XBlock.json_handler #add 1 or -1 to rating of a hint 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=''): def rate_hint(self, data, suffix=''):
data['ansnum'] = data['ansnum'].replace('ddeecciimmaallppooiinntt', '.') """
data['ansnum'] = data['ansnum'].replace('qquueessttiioonnmmaarrkk', '?') Used to facilitate hint rating by students. Ratings are -1, 1, or 0. -1 is downvote, 1 is upvote, and 0 is
data['ansnum'] = data['ansnum'].replace('ccoolloonn', ':') when a student flags a hint. 'zzeerroo' is returned to JS when a hint's rating is 0 because whenever 0 was
data['ansnum'] = data['ansnum'].replace('sseemmiiccoolloonn', ';') simply returned, JS would interpret it as null.
for usdkey in self.Used:
if str(usdkey) == str(data['ansnum']): Hint ratings in hint_database are updated and the resulting hint rating (or flagged status) is returned to JS.
print str(self.Used)
ansnum = self.Used.index(str(data['ansnum'])) Args:
if self.Voted == 0: data['answer']: The incorrect answer that corresponds to the hint that is being voted on
for key in self.DefaultHints: data['value']: The hint that is being voted on
if key == self.Used[int(ansnum)]: #rating for hints in DefaultHints data['student_rating']: The rating chosen by the student. The value is -1, 1, or 0.
self.DefaultHints[str(key)] += int(data["rating"])
self.Voted = 1 Returns:
return "rating": The rating of the hint. 'zzeerroo' is returned if the hint's rating is 0.
for key in self.hints: If the hint has already been voted on, 'You have already voted on this hint!'
tempdict = str(self.hints[str(key)]) #rate hint that is in hints will be returned to JS.
tempdict = (ast.literal_eval(tempdict)) """
if str(key) == str(self.WrongAnswers[ansnum]): #ansnum will the the answer/hint pair that is selected original_data = data['answer']
tempdict[self.Used[int(ansnum)]] += int(data["rating"]) answer_data = data['answer']
self.hints[str(key)] = tempdict data_rating = data['student_rating']
self.Voted = 1 data_value = data['value']
return answer_data = self.remove_symbols(answer_data)
for key in self.hints: if str(data['student_rating']) == str(0):
if str(key) == str(data['value']): self.hint_flagged(data['value'], answer_data)
for nextkey in self.hints[key]: return {"rating": 'thiswasflagged', 'origdata': original_data}
if str(nextkey) == str(data['ansnum']): if str(answer_data) not in self.Voted:
ansnum = self.hints[str(key)[str(nextkey)]] self.Voted.append(str(answer_data))
tempdict = str(self.hints[str(key)]) #rate hint that is in hints rating = self.change_rating(data_value, int(data_rating), answer_data)
tempdict = (ast.literal_eval(tempdict)) print str(self.change_rating(data['value'], int(data['student_rating']), answer_data))
tempdict[self.hints[str(key)[ansnum]]] += 1 if str(rating) == str(0):
selff.hints[str(key)] = tempdict return {"rating": str('zzeerroo'), 'origdata': original_data}
self.Voted = 1 else:
return 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):
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 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 @XBlock.json_handler
def give_hint(self, data, suffix=''): #add student-made hint into hints def give_hint(self, data, suffix=''):
data['submission'] = data['submission'].replace('ddeecciimmaallppooiinntt', '.') """
data['id'] = data['id'].replace('ddeecciimmaallppooiinntt', '.') This function adds a new hint submitted by the student into the hint_database.
if self.Voted == 0:
for key in self.hints: Args:
print(str(self.Used)) data['submission']: This is the text of the new hint that the student has submitted.
print(str(self.Used.index(str(data['id'])))) data['answer']: This is the incorrect answer for which the student is submitting a new hint.
if str(key) == self.WrongAnswers[self.Used.index(str(data['id']))]: """
if str(data['submission']) not in self.hints[str(key)]: submission = data['submission'].replace('ddeecciimmaallppooiinntt', '.')
tempdict = str(self.hints[str(key)]) #rate hint that is in hints hint_id = data['answer'].replace('ddeecciimmaallppooiinntt', '.')
tempdict = (ast.literal_eval(tempdict)) for answer_keys in self.hint_database:
tempdict.update({data['submission']: 0}) if str(answer_keys) == self.WrongAnswers[self.Used.index(hint_id)]:
self.hints[str(key)] = tempdict if str(submission) not in self.hint_database[str(answer_keys)]:
self.Voted = 1 temporary_dictionary = str(self.hint_database[str(answer_keys)])
return temporary_dictionary = (ast.literal_eval(temporary_dictionary))
else: temporary_dictionary.update({submission: 0})
ansnum = self.Used.index(data['submission']) self.hint_database[str(answer_keys)] = temporary_dictionary
for key in self.DefaultHints: return
if key == self.Used[int(ansnum)]: #rating for hints in DefaultHints else:
self.DefaultHints[str(key)] += int(1) hint_index = self.Used.index(submission)
self.Voted = 1 for default_hints in self.DefaultHints:
return if default_hints == self.Used[int(hint_index)]:
for key in self.hints: self.DefaultHints[str(default_hints)] += int(1)
tempdict = str(self.hints[str(key)]) #rate hint that is in hints return
tempdict = (ast.literal_eval(tempdict)) for answer_keys in self.hint_database:
if str(key) == str(self.WrongAnswers[int(ansnum)]): #ansnum will the the answer/hint pair that is selected temporary_dictionary = str(self.hint_database[str(answer_keys)])
tempdict[self.Used[int(ansnum)]] += int(1) temporary_dictionary = (ast.literal_eval(temporary_dictionary))
self.hints[str(key)] = tempdict if str(answer_keys) == str(self.WrongAnswers[int(hint_index)]):
self.Voted = 1 temporary_dictionary[self.Used[int(hint_index)]] += int(data['rating'])
self.hint_database[str(answer_keys)] = temporary_dictionary
@XBlock.json_handler @XBlock.json_handler
def clear_states(self, data, suffix=''): def studiodata(self, data, suffix=''):
self.Used = [] """
self.HintsToUse = {} This function serves to return the dictionary of flagged hints to JS. This is intended for use in
self.Voted = 0 the studio_view, which is under construction at the moment
self.WrongAnswers = [] """
return self.Flagged
@staticmethod @staticmethod
def workbench_scenarios(): def workbench_scenarios():
...@@ -195,4 +385,3 @@ class CrowdXBlock(XBlock): ...@@ -195,4 +385,3 @@ class CrowdXBlock(XBlock):
</vertical_demo> </vertical_demo>
"""), """),
] ]
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
font-weight: bold; font-weight: bold;
} }
.crowdxblock_block button { /*.crowdxblock_block {
cursor: pointer; cursor: pointer;
background: rgb(0,0,200); background: rgb(0,0,200);
} }*/
.crowdxblock_block .crowdsource-wrapper { .crowdxblock_block .crowdsource-wrapper {
box-shadow: inset 0 1px 2px 1px rgba(0,0,0,0.1); box-shadow: inset 0 1px 2px 1px rgba(0,0,0,0.1);
...@@ -22,19 +22,31 @@ ...@@ -22,19 +22,31 @@
padding-left: 15px; padding-left: 15px;
padding-right: 15px; padding-right: 15px;
font-size: 16px; font-size: 16px;
} }
.crowdxblock_block .hintsarea {
border-style: solid;
border-width: thin;
border-color: rgb(200,200,200);
}
/*.crowdxblock_block .hintbutton {
height: 10px;
}*/
.crowdxblock_block .vote { .crowdxblock_block .vote {
padding-top: 0px !important; padding-top: 0px !important;
padding-bottom: 0px !important; padding-bottom: 0px !important;
} }
.crowdxblock_block .hintbutton { /*.crowdxblock_block .hintbutton {
background: rgb(230, 250,230); }*/
}
.crowdxblock_block .hintsarea { /*.crowdxblock_block .hintsarea {
width: 600px; width: 600px;
padding: 10px; padding: 10px;
border: 5px solid gray; border: 5px solid gray;
margin: 0px; margin: 0px;
} }*/
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
font-weight: bold; font-weight: bold;
} }
.crowdxblock_block button { /*.crowdxblock_block {
cursor: pointer; cursor: pointer;
background: rgb(0,0,200); background: rgb(0,0,200);
} }*/
.crowdxblock_block .crowdsource-wrapper { .crowdxblock_block .crowdsource-wrapper {
box-shadow: inset 0 1px 2px 1px rgba(0,0,0,0.1); box-shadow: inset 0 1px 2px 1px rgba(0,0,0,0.1);
...@@ -22,19 +22,35 @@ ...@@ -22,19 +22,35 @@
padding-left: 15px; padding-left: 15px;
padding-right: 15px; padding-right: 15px;
font-size: 16px; 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 { .crowdxblock_block .vote {
padding-top: 0px !important; padding-top: 0px !important;
padding-bottom: 0px !important; padding-bottom: 0px !important;
} }
.crowdxblock_block .hintbutton { /*.crowdxblock_block .hintbutton {
background: rgb(230, 250,230); }*/
}
.crowdxblock_block .hintsarea { /*.crowdxblock_block .hintsarea {
width: 300px; width: 600px;
padding: 10px; padding: 10px;
border: 5px solid gray; border: 5px solid gray;
margin: 0px; margin: 0px;
} }*/
<div class="crowdxblock_block"> <!--most stuff just for testing purposes--> <div class="crowdxblock_block"> <!--most stuff just for testing purposes-->
<p> <img src="demoproblem.png">
</p>
<p> <span class='HintsToUse'>{self.HintsToUse}</span> <p> <span class='HintsToUse'>{self.HintsToUse}</span>
</p> </p>
<span><br><span> Dummy answer submission below: </span></span>
<section id="studentinput" class="textinput">
<input type="text" name="studentanswer" id="studentsubmit" class="notmath" size="40">
<input id="studentanswer" type="button" class="button" value="Submit Answer">
</div> </div>
<div class="crowdxblock_block"> <div class="crowdxblock_block">
...@@ -22,7 +17,7 @@ ...@@ -22,7 +17,7 @@
</div> </div>
<p> <p>
<input id="caus" type="button" class="button" value="clearAllUserState">
</p> </p>
<section class="action"> <section class="action">
...@@ -32,6 +27,8 @@ ...@@ -32,6 +27,8 @@
</div></section></span> </div></section></span>
</section> </section>
<section class="crowdsource-wrapper">
</section>
</section> </section>
......
<div class="crowdxblock_block"> <!--most stuff just for testing purposes--> <div class="crowdxblock_block"> <!--most stuff just for testing purposes-->
<p> <img src="demoproblem.png">
</p>
<p> <span class='HintsToUse'>{self.HintsToUse}</span> <p> <span class='HintsToUse'>{self.HintsToUse}</span>
</p> </p>
<span><br><span> Dummy answer submission below: </span></span>
<section id="studentinput" class="textinput">
<input type="text" name="studentanswer" id="studentsubmit" class="notmath" size="40">
<input id="studentanswer" type="button" class="button" value="Submit Answer">
</div> </div>
<div class="crowdxblock_block"> <div class="crowdxblock_block">
...@@ -22,7 +17,7 @@ ...@@ -22,7 +17,7 @@
</div> </div>
<p> <p>
<input id="caus" type="button" class="button" value="clearAllUserState">
</p> </p>
<section class="action"> <section class="action">
...@@ -32,6 +27,8 @@ ...@@ -32,6 +27,8 @@
</div></section></span> </div></section></span>
</section> </section>
<section class="crowdsource-wrapper">
</section>
</section> </section>
......
var repeating = 0;
//my coding attemps start here i guess var repeatcounter = 0;
/*this.url = this.el.data('url'); var canhint = 0;
Logger.listen('problem_graded', this.el.data('child-id'), this.capture_problem); var issubmitting = 0;
this.render();*/ var issubmittinghint = 0;
/*function capture_problem(event_type, data, element) {
var answers, response,
_this = this;
answers = data[0];
response = data[1];*/ //use this snippet for actual code? maybe?
function CrowdXBlock(runtime, element){ function CrowdXBlock(runtime, element){
var WrongAnswer = []; var WrongAnswer = [];
var HintUsed = []; var HintUsed = [];
var HintShown = [];
$("#answer").hide(); $("#answer").hide();
$(".problem").hide(); $(".problem").hide();
$("#feedback").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 function seehint(result){//use html to show these results somewhere i guess
$('.HintsToUse', element).text(result.HintsToUse); //text(result.self.hints?) 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){ function getfeedback(result){
...@@ -31,103 +122,118 @@ function CrowdXBlock(runtime, element){ ...@@ -31,103 +122,118 @@ function CrowdXBlock(runtime, element){
indexid = indexid.replace(/\:/g, 'ccoolloonn'); indexid = indexid.replace(/\:/g, 'ccoolloonn');
valueid = valueid.replace(/\;/g, 'sseemmiiccoolloonn'); valueid = valueid.replace(/\;/g, 'sseemmiiccoolloonn');
indexid = indexid.replace(/\;/g, 'sseemmiiccoolloonn'); indexid = indexid.replace(/\;/g, 'sseemmiiccoolloonn');
valueid = valueid.replace(/\"/g, 'qquuoottaattiioonnmmaarrkkss'); valueid = valueid.replace(/\=/g, 'eeqquuaallss');
indexid = indexid.replace(/\"/g, 'qquuoottaattiioonnmmaarrkkss'); indexid = indexid.replace(/\=/g, 'eeqquuaallss');
if($("#submit"+valueid).length == 0){ if($("#submit"+valueid).length == 0){
$('.hintansarea').append("<p id=\"submit" + valueid + "\" class=\"hintsarea\"> </p>"); $('.hintansarea').append("<p id=\"submit" + valueid + "\" class=\"hintsarea\"> </p>");
$('#submit'+valueid).append("For your incorrect answer of:" + " " + value + " <p id=\"hintstoshow" + valueid + "\"> The following hints exist: </p><p> <input id=\"" + indexid + "\" type=\"button\" class=\"submitbutton\" value=\"Submit a hint for this problem\">"); $('#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(indexid.slice(0,22) != "There are no hints for"){
$('#hintstoshow'+valueid).append("<p>" + index + "<input data-value=\"" + valueid + "\" id=\"" + indexid + "\" type=\"button\" class=\"hintbutton\" value=\"Upvote this Hint\"></p>"); if($.inArray(index, HintUsed) == -1){
}else{ 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(); $('#hintstoshow'+valueid).empty();
$('#hintstoshow'+valueid).append("<p id=\"hintstoshow" + valueid + "\"> No hints exist in the database.</p> <p data-value=\"" + valueid + "\" id=\"" + indexid + "\"</p>"); 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 $(document).on('click', '.submitbutton', function(){ //upvote
issubmittinghint = 0;
issubmitting += 1;
if(issubmitting == repeatcounter){
id = this.id; id = this.id;
value = document.getElementById(this.id).getAttribute('data-value'); id = id.slice(15);
value = document.getElementById(id).getAttribute('data-value');
$('.submitbutton').show();
$('.math').remove();
$('#submit').remove();
$(this).hide(); $(this).hide();
$('#submit' + value).append("<p><input type=\"text\" name=\"studentinput\" id=\"" + id + "\" class=\"math\" size=\"40\"><<input id=\"submit\" type=\"button\" class=\"button\" value=\"Submit Hint\"> </p>");}) $('#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(){ $(document).on('click', '#submit', function(){
$.ajax({ issubmittinghint += 1;
type: "POST", if(issubmittinghint == repeatcounter){
url: runtime.handlerUrl(element, 'give_hint'), if($('.math').val() != null){
data: JSON.stringify({"submission": $('.math').val(), "id": $(".math").attr('id')}), //give hin for first incorrect answer var valueid = String;
success: finish issubmitting = 0;
}); $('#submit').each(function(){
$("#answer").val('');}) 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(), "answer": 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 $(document).on('click', '.hintbutton', function(){ //upvote
canhint = 0;
id = this.id; id = this.id;
$(this).hide();
$('.hintbutton').each(function(){
if($(this).attr('id') == String(id)){
$(this).hide();}
});
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: runtime.handlerUrl(element, 'rate_hint'), url: runtime.handlerUrl(element, 'rate_hint'),
data: JSON.stringify({"rating": 1, "ansnum": $(this).attr('id'), "value": $(this).attr('data-value')}), data: JSON.stringify({"student_rating": $(this).attr('data-rate'), "answer": $(this).attr('id'), "value": $(this).attr('data-value')}),
success: finish success: finish
});}) });})
$('#caus', element).click(function(eventObject) {
console.debug("ca working this is edited");
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'clear_states'),
data: JSON.stringify({"hello": "world"}), //give hin for first incorrect answer
success: clearstates
});
$("#studentsubmit").val('');
$("#answer").val('');})
function finish(){
$('.Thankyou', element).text("Thankyou for your help!");
$('.correct', element).hide();
$( ".hintansarea" ).empty();
}
function clearstates(){
$('.Thankyou', element).text();
$('.correct', element).hide();
$( ".hintansarea" ).empty();
$("#answer").hide();
$(".problem").hide();
}
function checkreply(result){
if(result.correct == 1){ function finish(result){
console.debug("yay"); if(canhint == 0){
$('.correct', element).show(); canhint = 1;
$('.correct', element).text("You're correct! Please choose the best hint, or provide us with one of your own!"); $('.Thankyou', element).text("Thankyou for your help!");
$.ajax({ idtouse = String('thisparagraph' + result.origdata);
type: "POST", hint_rating = result.rating;
url: runtime.handlerUrl(element, 'get_feedback'), if(result.rating == "zzeerroo"){
data: JSON.stringify({"hello": "world"}), hint_rating = 0;
success: getfeedback }if(result.rating == "thiswasflagged"){
}); hint_rating = 999;
}else{
seehint(result)
} }
//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>");}
}
});}
} }
$('#studentanswer', element).click(function(eventObject) { //for test //when answer is incorrect /*response.search(/class="correct/) === -1*/ }
console.debug($('#studentsubmit').val()); //currently data is passed to python and then returned whether it is correct or not
$.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": $('#studentsubmit').val()}), //return student's incorrect answer here
success: checkreply
});
$("#studentsubmit").val('');
/* } else { //answer is correct
$('.correct', element).text("You're correct! Please choose the best hint, or provide us with one of your own!");
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'get_feedback'),
data: JSON.stringify({"hello": "world"}),
success: getfeedback
});
};*/
}
)}
var repeating = 0;
//my coding attemps start here i guess var repeatcounter = 0;
/*this.url = this.el.data('url'); var canhint = 0;
Logger.listen('problem_graded', this.el.data('child-id'), this.capture_problem); var issubmitting = 0;
this.render();*/ var issubmittinghint = 0;
/*function capture_problem(event_type, data, element) {
var answers, response,
_this = this;
answers = data[0];
response = data[1];*/ //use this snippet for actual code? maybe?
function CrowdXBlock(runtime, element){ function CrowdXBlock(runtime, element){
var WrongAnswer = []; var WrongAnswer = [];
var HintUsed = []; var HintUsed = [];
var HintShown = [];
$("#answer").hide(); $("#answer").hide();
$(".problem").hide(); $(".problem").hide();
$("#feedback").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 function seehint(result){//use html to show these results somewhere i guess
$('.HintsToUse', element).text(result.HintsToUse); //text(result.self.hints?) 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){ function getfeedback(result){
...@@ -31,105 +122,118 @@ function CrowdXBlock(runtime, element){ ...@@ -31,105 +122,118 @@ function CrowdXBlock(runtime, element){
indexid = indexid.replace(/\:/g, 'ccoolloonn'); indexid = indexid.replace(/\:/g, 'ccoolloonn');
valueid = valueid.replace(/\;/g, 'sseemmiiccoolloonn'); valueid = valueid.replace(/\;/g, 'sseemmiiccoolloonn');
indexid = indexid.replace(/\;/g, 'sseemmiiccoolloonn'); indexid = indexid.replace(/\;/g, 'sseemmiiccoolloonn');
valueid = valueid.replace(/\?/g, 'qquueessttiioonnmmaarrkk'); valueid = valueid.replace(/\=/g, 'eeqquuaallss');
indexid = indexid.replace(/\?/g, 'qquueessttiioonnmmaarrkk'); indexid = indexid.replace(/\=/g, 'eeqquuaallss');
valueid = valueid.replace(/\"/g, 'qquuoottaattiioonnmmaarrkkss');
indexid = indexid.replace(/\"/g, 'qquuoottaattiioonnmmaarrkkss');
if($("#submit"+valueid).length == 0){ if($("#submit"+valueid).length == 0){
$('.hintansarea').append("<p id=\"submit" + valueid + "\" class=\"hintsarea\"> </p>"); $('.hintansarea').append("<p id=\"submit" + valueid + "\" class=\"hintsarea\"> </p>");
$('#submit'+valueid).append("For your incorrect answer of:" + " " + value + " <p id=\"hintstoshow" + valueid + "\"> The following hints exist: </p><p> <input id=\"" + indexid + "\" type=\"button\" class=\"submitbutton\" value=\"Submit a hint for this problem\">"); $('#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(indexid.slice(0,22) != "There are no hints for"){
$('#hintstoshow'+valueid).append("<p>" + index + "<input data-value=\"" + valueid + "\" id=\"" + indexid + "\" type=\"button\" class=\"hintbutton\" value=\"Upvote this Hint\"></p>"); if($.inArray(index, HintUsed) == -1){
}else{ 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(); $('#hintstoshow'+valueid).empty();
$('#hintstoshow'+valueid).append("<p id=\"hintstoshow" + valueid + "\"> No hints exist in the database.</p> <p data-value=\"" + valueid + "\" id=\"" + indexid + "\"</p>"); 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 $(document).on('click', '.submitbutton', function(){ //upvote
issubmittinghint = 0;
issubmitting += 1;
if(issubmitting == repeatcounter){
id = this.id; id = this.id;
value = document.getElementById(this.id).getAttribute('data-value'); id = id.slice(15);
value = document.getElementById(id).getAttribute('data-value');
$('.submitbutton').show();
$('.math').remove();
$('#submit').remove();
$(this).hide(); $(this).hide();
$('#submit' + value).append("<p><input type=\"text\" name=\"studentinput\" id=\"" + id + "\" class=\"math\" size=\"40\"><<input id=\"submit\" type=\"button\" class=\"button\" value=\"Submit Hint\"> </p>");}) $('#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(){ $(document).on('click', '#submit', function(){
$.ajax({ issubmittinghint += 1;
type: "POST", if(issubmittinghint == repeatcounter){
url: runtime.handlerUrl(element, 'give_hint'), if($('.math').val() != null){
data: JSON.stringify({"submission": $('.math').val(), "id": $(".math").attr('id')}), //give hin for first incorrect answer var valueid = String;
success: finish issubmitting = 0;
}); $('#submit').each(function(){
$("#answer").val('');}) 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 $(document).on('click', '.hintbutton', function(){ //upvote
canhint = 0;
id = this.id; id = this.id;
$(this).hide();
$('.hintbutton').each(function(){
if($(this).attr('id') == String(id)){
$(this).hide();}
});
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: runtime.handlerUrl(element, 'rate_hint'), url: runtime.handlerUrl(element, 'rate_hint'),
data: JSON.stringify({"rating": 1, "ansnum": $(this).attr('id'), "value": $(this).attr('data-value')}), data: JSON.stringify({"student_rating": $(this).attr('data-rate'), "answer": $(this).attr('id'), "value": $(this).attr('data-value')}),
success: finish success: finish
});}) });})
$('#caus', element).click(function(eventObject) {
console.debug("ca working this is edited");
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'clear_states'),
data: JSON.stringify({"hello": "world"}), //give hin for first incorrect answer
success: clearstates
});
$("#studentsubmit").val('');
$("#answer").val('');})
function finish(){
$('.Thankyou', element).text("Thankyou for your help!");
$('.correct', element).hide();
$( ".hintansarea" ).empty();
}
function clearstates(){
$('.Thankyou', element).text();
$('.correct', element).hide();
$( ".hintansarea" ).empty();
$("#answer").hide();
$(".problem").hide();
}
function checkreply(result){
if(result.correct == 1){ function finish(result){
console.debug("yay"); if(canhint == 0){
$('.correct', element).show(); canhint = 1;
$('.correct', element).text("You're correct! Please choose the best hint, or provide us with one of your own!"); $('.Thankyou', element).text("Thankyou for your help!");
$.ajax({ idtouse = String('thisparagraph' + result.origdata);
type: "POST", hint_rating = result.rating;
url: runtime.handlerUrl(element, 'get_feedback'), if(result.rating == "zzeerroo"){
data: JSON.stringify({"hello": "world"}), hint_rating = 0;
success: getfeedback }if(result.rating == "thiswasflagged"){
}); hint_rating = 999;
}else{
seehint(result)
} }
//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>");}
}
});}
} }
$('#studentanswer', element).click(function(eventObject) { //for test //when answer is incorrect /*response.search(/class="correct/) === -1*/ }
console.debug($('#studentsubmit').val()); //currently data is passed to python and then returned whether it is correct or not
$.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": $('#studentsubmit').val()}), //return student's incorrect answer here
success: checkreply
});
$("#studentsubmit").val('');
/* } else { //answer is correct
$('.correct', element).text("You're correct! Please choose the best hint, or provide us with one of your own!");
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'get_feedback'),
data: JSON.stringify({"hello": "world"}),
success: getfeedback
});
};*/
}
)}
setup.py
crowdxblock/__init__.py crowdxblock/__init__.py
crowdxblock/crowdxblock.py crowdxblock/crowdxblock.py
crowdxblock/static/README.txt crowdxblock/static/README.txt
crowdxblock/static/css/crowdxblock.css crowdxblock/static/css/crowdxblock.css
crowdxblock/static/css/crowdxblock.css~
crowdxblock/static/html/crowdxblock.html crowdxblock/static/html/crowdxblock.html
crowdxblock/static/html/crowdxblock.html~
crowdxblock/static/html/demoproblem.png
crowdxblock/static/js/src/crowdxblock.js crowdxblock/static/js/src/crowdxblock.js
crowdxblock/static/js/src/crowdxblock.js~
crowdxblock_xblock.egg-info/PKG-INFO crowdxblock_xblock.egg-info/PKG-INFO
crowdxblock_xblock.egg-info/SOURCES.txt crowdxblock_xblock.egg-info/SOURCES.txt
crowdxblock_xblock.egg-info/dependency_links.txt crowdxblock_xblock.egg-info/dependency_links.txt
......
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