Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
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
edx
edx-platform
Commits
48cc87e9
Commit
48cc87e9
authored
Feb 07, 2014
by
Matt Drayer
Committed by
Xavier Antoviaque
Feb 12, 2014
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added hint/feedback feature to multiple choice response
parent
f96e73b1
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
150 additions
and
6 deletions
+150
-6
common/lib/capa/capa/responsetypes.py
+27
-3
common/lib/capa/capa/templates/choicegroup.html
+49
-2
common/lib/capa/capa/tests/response_xml_factory.py
+38
-1
common/lib/capa/capa/tests/test_input_templates.py
+28
-0
common/lib/capa/capa/tests/test_responsetypes.py
+8
-0
No files found.
common/lib/capa/capa/responsetypes.py
View file @
48cc87e9
...
...
@@ -344,12 +344,15 @@ class LoncapaResponse(object):
hintmode
=
hintgroup
.
get
(
'mode'
,
'always'
)
for
hintpart
in
hintgroup
.
findall
(
'hintpart'
):
if
hintpart
.
get
(
'on'
)
in
hints_to_show
:
if
hintpart
.
find
(
'text'
)
is
not
None
:
hint_text
=
hintpart
.
find
(
'text'
)
.
text
elif
hintpart
.
text
:
hint_text
=
hintpart
.
text
# make the hint appear after the last answer box in this
# response
aid
=
self
.
answer_ids
[
-
1
]
new_cmap
.
set_hint_and_mode
(
aid
,
hint_text
,
hintmode
)
log
.
debug
(
'after hint: new_cmap =
%
s'
,
new_cmap
)
@abc.abstractmethod
def
get_score
(
self
,
student_answers
):
...
...
@@ -718,6 +721,7 @@ class MultipleChoiceResponse(LoncapaResponse):
# TODO: handle direction and randomize
tags
=
[
'multiplechoiceresponse'
]
hint_tag
=
'choicehint'
max_inputfields
=
1
allowed_inputfields
=
[
'choicegroup'
]
correct_choices
=
None
...
...
@@ -727,6 +731,10 @@ class MultipleChoiceResponse(LoncapaResponse):
# attributes
self
.
mc_setup_response
()
# These two fields are used for hint matching
self
.
regexp
=
True
self
.
case_insensitive
=
True
# define correct choices (after calling secondary setup)
xml
=
self
.
xml
cxml
=
xml
.
xpath
(
'//*[@id=$id]//choice'
,
id
=
xml
.
get
(
'id'
))
...
...
@@ -771,6 +779,24 @@ class MultipleChoiceResponse(LoncapaResponse):
def
get_answers
(
self
):
return
{
self
.
answer_id
:
self
.
correct_choices
}
def
check_string
(
self
,
expected
,
given
):
flags
=
re
.
IGNORECASE
if
self
.
case_insensitive
else
0
regexp
=
re
.
compile
(
'^'
+
'|'
.
join
(
expected
)
+
'$'
,
flags
=
flags
|
re
.
UNICODE
)
result
=
re
.
search
(
regexp
,
given
)
return
bool
(
result
)
def
check_hint_condition
(
self
,
hxml_set
,
student_answers
):
# stolen from StringResponse.check_hint_condition
given
=
student_answers
[
self
.
answer_id
]
.
strip
()
hints_to_show
=
[]
for
hxml
in
hxml_set
:
name
=
hxml
.
get
(
'name'
)
hinted_answer
=
contextualize_text
(
hxml
.
get
(
'answer'
),
self
.
context
)
.
strip
()
if
self
.
check_string
([
hinted_answer
],
given
):
hints_to_show
.
append
(
name
)
return
hints_to_show
@registry.register
class
TrueFalseResponse
(
MultipleChoiceResponse
):
...
...
@@ -1050,7 +1076,6 @@ class StringResponse(LoncapaResponse):
]
def
setup_response
(
self
):
self
.
backward
=
'_or_'
in
self
.
xml
.
get
(
'answer'
)
.
lower
()
self
.
regexp
=
False
self
.
case_insensitive
=
False
...
...
@@ -1147,7 +1172,6 @@ class CustomResponse(LoncapaResponse):
Custom response. The python code to be run should be in <answer>...</answer>
or in a <script>...</script>
"""
tags
=
[
'customresponse'
]
allowed_inputfields
=
[
'textline'
,
'textbox'
,
'crystallography'
,
...
...
common/lib/capa/capa/templates/choicegroup.html
View file @
48cc87e9
<form
class=
"choicegroup capa_inputtype"
id=
"inputtype_${id}"
>
<style>
#fieldset_container
{
margin
:
0
auto
;
overflow
:
hidden
;
position
:
relative
;
width
:
100%
;
}
#fieldset
{
float
:
left
;
margin
:
0
auto
;
width
:
50%
;
}
#fieldset_message
{
background
:
#99CCFF
;
color
:
#FFFFFF
;
font-family
:
arial
;
float
:
right
;
height
:
100%
;
margin
:
0
auto
;
padding
:
10px
;
position
:
absolute
;
right
:
0
;
width
:
50%
;
}
#fieldset_message_title
{
color
:
#FFFFFF
;
font-family
:
arial
;
font-size
:
18pt
;
}
</style>
<div
class=
"indicator_container"
>
% if input_type == 'checkbox' or not value:
% if status == 'unsubmitted' or show_correctness == 'never':
...
...
@@ -12,9 +46,9 @@
% endif
% endif
</div>
<div
id=
"fieldset_container"
>
<div
id=
"fieldset"
>
<fieldset>
% for choice_id, choice_description in choices:
<label
for=
"input_${id}_${choice_id}"
##
If
the
student
has
selected
this
choice
...
...
...
@@ -59,6 +93,19 @@
% endfor
<span
id=
"answer_${id}"
></span>
</fieldset>
</div>
## Message/hint display block -- empty/invisible unless msg is populated
<div
%
if
(
not
msg
is
UNDEFINED
)
and
(
len
(
msg
)
>
0):
id="fieldset_message"
% endif
>
% if (not msg is UNDEFINED) and (len(msg) > 0):
<span
id=
"fieldset_message_title"
>
Feedback
</span>
${msg}
% endif
</div>
</div>
% if show_correctness == "never" and (value or status not in ['unsubmitted']):
<div
class=
"capa_alert"
>
${submitted_message}
</div>
...
...
common/lib/capa/capa/tests/response_xml_factory.py
View file @
48cc87e9
...
...
@@ -58,6 +58,7 @@ class ResponseXMLFactory(object):
script
=
kwargs
.
get
(
'script'
,
None
)
num_responses
=
kwargs
.
get
(
'num_responses'
,
1
)
num_inputs
=
kwargs
.
get
(
'num_inputs'
,
1
)
hints
=
kwargs
.
get
(
'hints'
,
None
)
# The root is <problem>
root
=
etree
.
Element
(
"problem"
)
...
...
@@ -83,6 +84,12 @@ class ResponseXMLFactory(object):
if
not
(
None
==
input_element
):
response_element
.
append
(
input_element
)
# Add hintgroup, if specified
if
hints
is
not
None
and
hasattr
(
self
,
'create_hintgroup_element'
):
hintgroup_element
=
self
.
create_hintgroup_element
(
**
kwargs
)
if
hintgroup_element
is
not
None
:
response_element
.
append
(
hintgroup_element
)
# The problem has an explanation of the solution
if
explanation_text
:
explanation
=
etree
.
SubElement
(
root
,
"solution"
)
...
...
@@ -161,6 +168,28 @@ class ResponseXMLFactory(object):
return
group_element
@staticmethod
def
hintgroup_input_xml
(
**
kwargs
):
""" Create a <hintgroup> XML element"""
# Gather the troops
choice_names
=
kwargs
.
get
(
"choice_names"
)
hints
=
kwargs
.
get
(
"hints"
)
# Build the <hintgroup> child tree
group_element
=
etree
.
Element
(
"hintgroup"
)
for
(
choice_name
,
hint
)
in
zip
(
choice_names
,
hints
):
choicehint_element
=
etree
.
SubElement
(
group_element
,
"choicehint"
)
choicehint_answer
=
"choice_"
+
choice_name
choicehint_element
.
set
(
"answer"
,
choicehint_answer
)
choicehint_name
=
choice_name
+
"_hint"
choicehint_element
.
set
(
"name"
,
choicehint_name
)
hintpart_element
=
etree
.
SubElement
(
group_element
,
"hintpart"
)
hintpart_element
.
set
(
"on"
,
choicehint_name
)
hintpart_element
.
text
=
hint
return
group_element
class
NumericalResponseXMLFactory
(
ResponseXMLFactory
):
""" Factory for producing <numericalresponse> XML trees """
...
...
@@ -618,7 +647,15 @@ class MultipleChoiceResponseXMLFactory(ResponseXMLFactory):
def
create_input_element
(
self
,
**
kwargs
):
""" Create the <choicegroup> element"""
kwargs
[
'choice_type'
]
=
'multiple'
return
ResponseXMLFactory
.
choicegroup_input_xml
(
**
kwargs
)
choice_group
=
ResponseXMLFactory
.
choicegroup_input_xml
(
**
kwargs
)
return
choice_group
def
create_hintgroup_element
(
self
,
**
kwargs
):
""" Create the <hintgroup> element"""
hintgroup_element
=
ResponseXMLFactory
.
hintgroup_input_xml
(
**
kwargs
)
return
hintgroup_element
class
TrueFalseResponseXMLFactory
(
ResponseXMLFactory
):
...
...
common/lib/capa/capa/tests/test_input_templates.py
View file @
48cc87e9
...
...
@@ -255,6 +255,34 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
xpath
=
"//div[@class='indicator_container']/span"
self
.
assert_no_xpath
(
xml
,
xpath
,
self
.
context
)
def
test_option_marked_incorrect_with_feedback
(
self
):
"""
Test conditions under which a particular option
(not the entire problem) is marked incorrect, with feedback.
"""
conditions
=
[
{
'input_type'
:
'radio'
,
'value'
:
'2'
},
{
'input_type'
:
'radio'
,
'value'
:
[
'2'
]}]
self
.
context
[
'status'
]
=
'incorrect'
self
.
context
[
'msg'
]
=
"This is the feedback"
for
test_conditions
in
conditions
:
self
.
context
.
update
(
test_conditions
)
xml
=
self
.
render_to_xml
(
self
.
context
)
# Should include a choicegroup_incorrect class
xpath
=
"//label[@class='choicegroup_incorrect']"
self
.
assert_has_xpath
(
xml
,
xpath
,
self
.
context
)
# Should include a fieldset_message type
xpath
=
"//div[@id='fieldset_message']"
self
.
assert_has_xpath
(
xml
,
xpath
,
self
.
context
)
# Should include a fieldset_message_title type
xpath
=
"//span[@id='fieldset_message_title']"
self
.
assert_has_xpath
(
xml
,
xpath
,
self
.
context
)
def
test_never_show_correctness
(
self
):
"""
Test conditions under which we tell the template to
...
...
common/lib/capa/capa/tests/test_responsetypes.py
View file @
48cc87e9
...
...
@@ -93,6 +93,14 @@ class MultiChoiceResponseTest(ResponseTest):
self
.
assert_grade
(
problem
,
'choice_foil_2'
,
'correct'
)
self
.
assert_grade
(
problem
,
'choice_foil_3'
,
'incorrect'
)
def
test_named_multiple_choice_grade_with_hint
(
self
):
problem
=
self
.
build_problem
(
choices
=
[
False
],
choice_names
=
[
"foil_1"
],
hints
=
[
"h1"
])
# Ensure that we get the expected hint
self
.
assert_grade
(
problem
,
'choice_foil_1'
,
'incorrect'
,
'h1'
)
class
TrueFalseResponseTest
(
ResponseTest
):
from
capa.tests.response_xml_factory
import
TrueFalseResponseXMLFactory
...
...
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