Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
crowdsourcehinter
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
crowdsourcehinter
Commits
b19291c6
Commit
b19291c6
authored
May 01, 2015
by
solashirai
Committed by
Piotr Mitros
Oct 12, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
removed unused funcitonality, fixed naming
parent
b0d54190
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
81 additions
and
141 deletions
+81
-141
crowdsourcehinter/crowdsourcehinter.py
+62
-122
crowdsourcehinter/static/html/crowdsourcehinter.html
+1
-1
crowdsourcehinter/static/js/src/crowdsourcehinter.js
+18
-18
No files found.
crowdsourcehinter/crowdsourcehinter.py
View file @
b19291c6
...
...
@@ -28,10 +28,10 @@ class CrowdsourceHinter(XBlock):
# {"incorrect_answer": {"hint": rating}}
initial_hints
=
Dict
(
default
=
{},
scope
=
Scope
.
content
)
# 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.
#
when the student starts rating hints
, to find which incorrect answer's hint a student voted on.
#
# Example: ["personal computer", "PC", "computerr"]
WrongA
nswers
=
List
([],
scope
=
Scope
.
user_state
)
incorrect_a
nswers
=
List
([],
scope
=
Scope
.
user_state
)
# A dictionary of generic_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)
...
...
@@ -43,21 +43,13 @@ class CrowdsourceHinter(XBlock):
# multiple times)
#
# Example: ["You misspelled computer, remove the last r."]
Used
=
List
([],
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
Voted
=
List
(
default
=
[],
scope
=
Scope
.
user_state
)
used
=
List
([],
scope
=
Scope
.
user_state
)
# This is a dictionary of hints that have been reported. the values represent the incorrect answer submission, and the
# keys are the hints the corresponding hints. hints with identical text for differing answers will all not show up for the
# student.
#
# Example: {"desk": "You're completely wrong, the answer is supposed to be computer."}
Reported
=
Dict
(
default
=
{},
scope
=
Scope
.
user_state_summary
)
# This string determines whether or not to show only the best (highest rated) hint to a student
# When set to 'True' only the best hint will be shown to the student.
# Details on operation when set to 'False' are to be finalized.
show_best
=
Boolean
(
default
=
True
,
scope
=
Scope
.
user_state_summary
)
reported_hints
=
Dict
(
default
=
{},
scope
=
Scope
.
user_state_summary
)
# This String represents the xblock element for which the hinter is running. It is necessary to manually
# set this value in the XML file under the format "hinting_element": "i4x://edX/DemoX/problem/Text_Input" .
# Setting the element in the XML file is critical for the hinter to work.
...
...
@@ -139,42 +131,29 @@ class CrowdsourceHinter(XBlock):
answer
=
answer
[
eqplace
:]
remaining_hints
=
str
(
self
.
find_hints
(
answer
))
if
remaining_hints
!=
str
(
0
):
best_hint
=
max
(
self
.
hint_database
[
str
(
answer
)]
.
iteritems
(),
key
=
operator
.
itemgetter
(
1
))[
0
]
if
self
.
show_best
:
# if set to show best, only the best hint will be shown. Different hints will not be shown
# for multiple submissions/hint requests
# currently set by default to True
if
best_hint
not
in
self
.
Reported
.
keys
():
self
.
Used
.
append
(
best_hint
)
return
{
'BestHint'
:
best_hint
,
"StudentAnswer"
:
answer
}
if
best_hint
not
in
self
.
Used
:
# choose highest rated hint for the incorrect answer
if
best_hint
not
in
self
.
Reported
.
keys
():
self
.
Used
.
append
(
best_hint
)
return
{
'BestHint'
:
best_hint
,
"StudentAnswer"
:
answer
}
# choose another random hint for the answer.
temporary_hints_list
=
[]
for
hint_keys
in
self
.
hint_database
[
str
(
answer
)]:
if
hint_keys
not
in
self
.
Used
:
if
hint_keys
not
in
self
.
Reported
:
temporary_hints_list
.
append
(
str
(
hint_keys
))
not_used
=
random
.
choice
(
temporary_hints_list
)
self
.
Used
.
append
(
not_used
)
return
{
'BestHint'
:
not_used
,
"StudentAnswer"
:
answer
}
for
hint
in
self
.
hint_database
[
str
(
answer
)]:
print
hint
,
self
.
reported_hints
.
keys
()
print
str
(
self
.
reported_hints
)
if
hint
not
in
self
.
reported_hints
.
keys
():
#if best_hint hasn't been set yet or the rating of hints is greater than the rating of best_hint
if
(
best_hint
==
""
or
self
.
hint_database
[
str
(
answer
)][
hint
]
>
self
.
hint_database
[
str
(
answer
)][
str
(
best_hint
)]):
best_hint
=
hint
self
.
used
.
append
(
best_hint
)
return
{
'BestHint'
:
best_hint
,
"StudentAnswer"
:
answer
}
# find generic hints for the student if no specific hints exist
if
len
(
self
.
generic_hints
)
!=
0
:
not_used
=
random
.
choice
(
self
.
generic_hints
)
self
.
Used
.
append
(
not_used
)
return
{
'BestHint'
:
not_used
,
"StudentAnswer"
:
answer
}
generic_hint
=
random
.
choice
(
self
.
generic_hints
)
self
.
used
.
append
(
generic_hint
)
return
{
'BestHint'
:
generic_hint
,
"StudentAnswer"
:
answer
}
else
:
# if there are no
more hints left in either the database or defaul
ts
self
.
U
sed
.
append
(
str
(
"There are no hints for"
+
" "
+
answer
))
# if there are no
hints in either the database or generic hin
ts
self
.
u
sed
.
append
(
str
(
"There are no hints for"
+
" "
+
answer
))
return
{
'Hints'
:
"Sorry, there are no hints for this answer."
,
"StudentAnswer"
:
answer
}
def
find_hints
(
self
,
answer
):
"""
This function is used to
find all appropriate hints that would be provided for
an incorrect answer
.
This function is used to
check that an incorrect answer has available hints to show.
It will also add the incorrect answer test to self.incorrect_answers
.
Args:
answer: This is equal to answer from get_hint, the answer the student submitted
...
...
@@ -182,90 +161,60 @@ class CrowdsourceHinter(XBlock):
Returns 0 if no hints to show exist
"""
isreported
=
[]
isused
=
0
self
.
WrongAnswers
.
append
(
str
(
answer
))
# add the student's input to the temporary list
self
.
incorrect_answers
.
append
(
str
(
answer
))
# add the student's input to the temporary list
if
str
(
answer
)
not
in
self
.
hint_database
:
# add incorrect answer to hint_database if no precedent exists
self
.
hint_database
[
str
(
answer
)]
=
{}
return
str
(
0
)
for
hint_keys
in
self
.
hint_database
[
str
(
answer
)]:
for
reported_keys
in
self
.
Reported
:
for
reported_keys
in
self
.
reported_hints
:
if
hint_keys
==
reported_keys
:
isreported
.
append
(
hint_keys
)
if
str
(
hint_keys
)
in
self
.
Used
:
if
self
.
show_best
is
False
:
isused
+=
1
if
(
len
(
self
.
hint_database
[
str
(
answer
)])
-
len
(
isreported
)
-
isused
)
>
0
:
if
(
len
(
self
.
hint_database
[
str
(
answer
)])
-
len
(
isreported
))
>
0
:
return
str
(
1
)
else
:
return
str
(
0
)
@XBlock.json_handler
def
get_
feedback
(
self
,
data
,
suffix
=
''
):
def
get_
used_hint_answer_data
(
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.
This function helps to facilitate student rating of hints and contribution of new hints.
Specifically this function is used to send necessary data to JS about incorrect answer
submissions and hints. It also will return hints that have been reported, although this
is only for Staff.
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
used_hint_answer_text: This dicitonary contains reported hints/answers (if the user is staff) and the
first hint/answer pair that the student submitted for a problem.
"""
#
feedback_data
is a dictionary of hints (or lack thereof) used for a
#
used_hint_answer_text
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
feedback_data
=
{}
used_hint_answer_text
=
{}
if
self
.
get_user_is_staff
():
if
len
(
self
.
Reported
)
!=
0
:
for
answer_keys
in
self
.
hint_database
:
if
str
(
len
(
self
.
hint_database
[
str
(
answer_keys
)]))
!=
str
(
0
):
for
hints
in
self
.
hint_database
[
str
(
answer_keys
)]:
for
reported_hints
in
self
.
Reported
:
if
str
(
hints
)
==
reported_hints
:
feedback_data
[
str
(
hints
)]
=
str
(
"Reported"
)
if
len
(
self
.
WrongAnswers
)
==
0
:
return
feedback_data
for
index
in
range
(
0
,
len
(
self
.
reported_hints
)):
used_hint_answer_text
[
str
(
self
.
reported_hints
[
i
])]
=
str
(
"reported"
)
if
len
(
self
.
incorrect_answers
)
==
0
:
return
used_hint_answer_text
else
:
for
index
in
range
(
0
,
len
(
self
.
U
sed
)):
for
index
in
range
(
0
,
len
(
self
.
u
sed
)):
# each index is a hint that was used, in order of usage
if
str
(
self
.
Used
[
index
])
in
self
.
hint_database
[
self
.
WrongA
nswers
[
index
]]:
# add new key (hint) to
feedback_data
with a value (incorrect answer)
feedback_data
[
str
(
self
.
Used
[
index
])]
=
str
(
self
.
WrongA
nswers
[
index
])
self
.
WrongA
nswers
=
[]
self
.
U
sed
=
[]
return
feedback_data
if
str
(
self
.
used
[
index
])
in
self
.
hint_database
[
self
.
incorrect_a
nswers
[
index
]]:
# add new key (hint) to
used_hint_answer_text
with a value (incorrect answer)
used_hint_answer_text
[
str
(
self
.
used
[
index
])]
=
str
(
self
.
incorrect_a
nswers
[
index
])
self
.
incorrect_a
nswers
=
[]
self
.
u
sed
=
[]
return
used_hint_answer_text
else
:
# if the student's answer had no hints (or all the hints were reported and unavailable) return None
feedback_data
[
None
]
=
str
(
self
.
WrongAnswers
[
index
])
self
.
WrongAnswers
=
[]
self
.
Used
=
[]
return
feedback_data
self
.
WrongAnswers
=
[]
self
.
Used
=
[]
return
feedback_data
@XBlock.json_handler
def
get_ratings
(
self
,
data
,
suffix
=
''
):
"""
This function is used to return the ratings of hints during hint feedback.
data['student_answer'] is the answer for the hint being displayed
data['hint'] is the hint being shown to the student
returns:
hint_rating: the rating of the hint as well as data on what the hint in question is
"""
hint_rating
=
{}
if
data
[
'student_answer'
]
==
'Reported'
:
hint_rating
[
'rating'
]
=
0
hint_rating
[
'student_ansxwer'
]
=
'Reported'
hint_rating
[
'hint'
]
=
data
[
'hint'
]
return
hint_rating
hint_rating
[
'rating'
]
=
self
.
hint_database
[
data
[
'student_answer'
]][
data
[
'hint'
]]
hint_rating
[
'student_answer'
]
=
data
[
'student_answer'
]
hint_rating
[
'hint'
]
=
data
[
'hint'
]
return
hint_rating
used_hint_answer_text
[
None
]
=
str
(
self
.
incorrect_answers
[
index
])
self
.
incorrect_answers
=
[]
self
.
used
=
[]
return
used_hint_answer_text
self
.
incorrect_answers
=
[]
self
.
used
=
[]
return
used_hint_answer_text
@XBlock.json_handler
def
rate_hint
(
self
,
data
,
suffix
=
''
):
...
...
@@ -275,40 +224,31 @@ class CrowdsourceHinter(XBlock):
Hint ratings in hint_database are updated and the resulting hint rating (or reported status) is returned to JS.
Args:
data['student_answer']: The incorrect answer that corresponds to the hint that is being
voted on
data['hint']: The hint that is being
voted on
data['student_answer']: The incorrect answer that corresponds to the hint that is being
rated
data['hint']: The hint that is being
rated
data['student_rating']: The rating chosen by the student.
Returns:
"rating": The rating of the hint.
"""
answer_data
=
data
[
'student_answer'
]
data_rating
=
data
[
'student_rating'
]
data_hint
=
data
[
'hint'
]
print
data_rating
,
answer_data
if
data
[
'student_rating'
]
==
'unreport'
:
for
reported_hints
in
self
.
Reported
:
for
reported_hints
in
self
.
reported_hints
:
if
reported_hints
==
data_hint
:
self
.
Reported
.
pop
(
data_hint
,
None
)
self
.
reported_hints
.
pop
(
data_hint
,
None
)
return
{
'rating'
:
'unreported'
}
if
data
[
'student_rating'
]
==
'remove'
:
for
reported_hints
in
self
.
Reported
:
for
reported_hints
in
self
.
reported_hints
:
if
data_hint
==
reported_hints
:
self
.
hint_database
[
self
.
Reported
[
data_hint
]]
.
pop
(
data_hint
,
None
)
self
.
Reported
.
pop
(
data_hint
,
None
)
self
.
hint_database
[
self
.
reported_hints
[
data_hint
]]
.
pop
(
data_hint
,
None
)
self
.
reported_hints
.
pop
(
data_hint
,
None
)
return
{
'rating'
:
'removed'
}
if
data
[
'student_rating'
]
==
'report'
:
# add hint to Reported dictionary
self
.
Reported
[
str
(
data_hint
)]
=
answer_data
self
.
reported_hints
[
str
(
data_hint
)]
=
answer_data
return
{
"rating"
:
'reported'
,
'hint'
:
data_hint
}
if
str
(
data_hint
)
not
in
self
.
Voted
:
self
.
Voted
.
append
(
str
(
data_hint
))
# add data to Voted to prevent multiple votes
rating
=
self
.
change_rating
(
data_hint
,
data_rating
,
answer_data
)
# change hint rating
if
str
(
rating
)
==
str
(
0
):
return
{
"rating"
:
str
(
0
),
'hint'
:
data_hint
}
else
:
return
{
"rating"
:
str
(
rating
),
'hint'
:
data_hint
}
else
:
return
{
"rating"
:
str
(
'voted'
),
'hint'
:
data_hint
}
rating
=
self
.
change_rating
(
data_hint
,
data_rating
,
answer_data
)
# change hint rating
return
{
"rating"
:
str
(
rating
),
'hint'
:
data_hint
}
def
change_rating
(
self
,
data_hint
,
data_rating
,
answer_data
):
"""
...
...
@@ -358,7 +298,7 @@ class CrowdsourceHinter(XBlock):
This function serves to return the dictionary of reported hints to JS. This is intended for use in
the studio_view, which is under construction at the moment
"""
return
self
.
Reported
return
self
.
reported_hints
@staticmethod
def
workbench_scenarios
():
...
...
crowdsourcehinter/static/html/crowdsourcehinter.html
View file @
b19291c6
<script
type=
'x-tmpl/mustache'
id=
'show_hint_
contribution
'
>
<script
type=
'x-tmpl/mustache'
id=
'show_hint_
rating_ux
'
>
<
div
class
=
'csh_hint_value'
value
=
"{{hintText}}"
>
<
div
class
=
'csh_hint_data'
>
<
div
class
=
"csh_hint"
><
b
>
{{
hintText
}}
<
/b></
div
>
...
...
crowdsourcehinter/static/js/src/crowdsourcehinter.js
View file @
b19291c6
...
...
@@ -37,21 +37,21 @@ function CrowdsourceHinter(runtime, element, data){
}
/**
* Start student hint contribution. This will allow students to contribute new hints
* Start student hint
rating/
contribution. This will allow students to contribute new hints
* to the hinter as well as vote on the helpfulness of the first hint they received
* for the current problem. This function is called after the student answers
* the question correctly.
*/
function
startHint
Contribution
(){
function
startHint
Rating
(){
$
(
'.csh_correct'
,
element
).
show
();
$
(
".csh_hint_reveal"
,
element
).
hide
();
if
(
$
(
'.csh_hint_creation'
,
element
)){
//send empty data for ajax call because not having a data field causes error
$
.
ajax
({
type
:
"POST"
,
url
:
runtime
.
handlerUrl
(
element
,
'get_
feedback
'
),
url
:
runtime
.
handlerUrl
(
element
,
'get_
used_hint_answer_data
'
),
data
:
JSON
.
stringify
({}),
success
:
setHint
ContributionDivs
success
:
setHint
RatingUX
});
}
}
...
...
@@ -60,6 +60,7 @@ function CrowdsourceHinter(runtime, element, data){
* Check whether or not the question was correctly answered by the student.
* The current method of checking the correctness of the answer is very brittle
* since we simply look for a string within the problemGradedEventData.
* HACK
* @param problemGradedEventData is the data from problem_graded event.
*/
function
checkIsAnswerCorrect
(
problemGradedEventData
){
...
...
@@ -80,7 +81,7 @@ function CrowdsourceHinter(runtime, element, data){
//search method of correctness of problem is brittle due to checking for a class within
//the problem block.
if
(
checkIsAnswerCorrect
(
data
)){
startHint
Contribution
();
startHint
Rating
();
}
else
{
//if the submitted answer is incorrect
getHint
(
data
);
}
...
...
@@ -98,15 +99,15 @@ function CrowdsourceHinter(runtime, element, data){
}
/**
* Called by setHint
ContributionDivs
to append hints into divs created by
* Called by setHint
RatingUX
to append hints into divs created by
* showStudentSubmissoinHistory, after the student answered the question correctly.
* Feedback on hints at this stage consists of upvote/downvote/report buttons.
* @param hint is the first hint that was shown to the student
* @param student_answer is the first incorrect answer submitted by the student
*/
function
showStudentHint
Contribution
(
hint
,
student_answer
){
var
hint
ContributionTemplate
=
$
(
Mustache
.
render
(
$
(
'#show_hint_contribution
'
).
html
(),
{
hintText
:
hint
}));
$
(
'.csh_answer_text'
,
element
).
append
(
hint
Contribution
Template
);
function
showStudentHint
RatingUX
(
hint
,
student_answer
){
var
hint
RatingUXTemplate
=
$
(
Mustache
.
render
(
$
(
'#show_hint_rating_ux
'
).
html
(),
{
hintText
:
hint
}));
$
(
'.csh_answer_text'
,
element
).
append
(
hint
RatingUX
Template
);
var
hintCreationTemplate
=
$
(
Mustache
.
render
(
$
(
'#add_hint_creation'
).
html
(),
{}));
$
(
'.csh_answer_text'
,
element
).
append
(
hintCreationTemplate
);
}
...
...
@@ -125,8 +126,8 @@ function CrowdsourceHinter(runtime, element, data){
/**
* Append new divisions into html for each answer the student submitted before correctly
* answering the question. showStudentHint
Contribution
appends new hints into these divs.
*
When the hinter is set to show best, only one div will be created.
* answering the question. showStudentHint
RatingUX
appends new hints into these divs.
*
* @param student_answers is the text of the student's incorrect answer
*/
function
showStudentSubmissionHistory
(
student_answer
){
...
...
@@ -138,11 +139,11 @@ function CrowdsourceHinter(runtime, element, data){
* Set up student/staff voting on hints and contribution of new hints. The original incorrect answer and the
* the corresponding hint shown to the student is displayed. Students can upvote/downvote/report
* the hint or contribute a new hint for their incorrect answer.
*
Only one incorrect answer and hint will be shown when the hinter is set to show best.
*
* @param result is a dictionary of incorrect answers and hints, with the index being the hint and the value
* being the incorrect answer
*/
function
setHint
ContributionDivs
(
result
){
function
setHint
RatingUX
(
result
){
if
(
data
.
isStaff
){
//allow staff to see and remove/return reported hints to/from the hint pool for a problem
$
(
'.crowdsourcehinter_block'
,
element
).
attr
(
'class'
,
'crowdsourcehinter_block_is_staff'
);
$
.
each
(
result
,
function
(
index
,
value
)
{
...
...
@@ -164,7 +165,7 @@ function CrowdsourceHinter(runtime, element, data){
var
hintCreationTemplate
=
$
(
Mustache
.
render
(
$
(
'#add_hint_creation'
).
html
(),
{}));
$
(
'.csh_student_answer'
,
element
).
append
(
hintCreationTemplate
);
}
else
{
showStudentHint
Contribution
(
hint
,
student_answer
);
showStudentHint
RatingUX
(
hint
,
student_answer
);
}
}
});
...
...
@@ -175,7 +176,7 @@ function CrowdsourceHinter(runtime, element, data){
* is triggered by clicking the "contribute a new hint" button.
* @param createTextInputButtonHTML is the "contribute a new hint" button that was clicked
*/
function
create
_text_i
nput
(){
return
function
(
createTextInputButtonHTML
){
function
create
HintContributionTextI
nput
(){
return
function
(
createTextInputButtonHTML
){
$
(
'.csh_student_hint_creation'
,
element
).
each
(
function
(){
$
(
createTextInputButtonHTML
.
currentTarget
).
show
();
});
...
...
@@ -187,11 +188,11 @@ function CrowdsourceHinter(runtime, element, data){
var
hintTextInputTemplate
=
$
(
Mustache
.
render
(
$
(
'#hint_text_input'
).
html
(),
{
student_answer
:
student_answer
}));
$
(
'.csh_answer_text'
,
element
).
append
(
hintTextInputTemplate
);
}}
$
(
element
).
on
(
'click'
,
'.csh_student_hint_creation'
,
create
_text_i
nput
(
$
(
this
)));
$
(
element
).
on
(
'click'
,
'.csh_student_hint_creation'
,
create
HintContributionTextI
nput
(
$
(
this
)));
/**
* Submit a new hint created by the student to the hint pool. Hint text is in
* the text input area created by create
_text_i
nput. Contributed hints are specific to
* the text input area created by create
HintContributionTextI
nput. Contributed hints are specific to
* incorrect answers. Triggered by clicking the "submit hint" button.
* @param submitHintButtonHTML is the "submit hint" button clicked
*/
...
...
@@ -258,7 +259,6 @@ function CrowdsourceHinter(runtime, element, data){
*/
function
removeReportedHint
(){
Logger
.
log
(
'crowd_hinter.staff_rate_hint.click.event'
,
{
"hint"
:
hint
,
"student_answer"
:
student_answer
,
"rating"
:
rating
});
//TODO: change if statement, just find .csh_hint_value with attribute of hint
$
(
".csh_hint_value[value='"
+
hint
+
"']"
,
element
).
remove
();
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment