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