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
1fc4ac86
Commit
1fc4ac86
authored
Jun 06, 2013
by
Renzo Lucioni
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add feature showing current score next to problem title
parent
73e6e6f4
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
165 additions
and
25 deletions
+165
-25
common/lib/xmodule/xmodule/capa_module.py
+17
-15
common/lib/xmodule/xmodule/css/capa/display.scss
+8
-0
common/lib/xmodule/xmodule/js/fixtures/problem.html
+1
-1
common/lib/xmodule/xmodule/js/fixtures/problem_content.html
+3
-0
common/lib/xmodule/xmodule/js/spec/capa/display_spec.coffee
+19
-0
common/lib/xmodule/xmodule/js/src/capa/display.coffee
+21
-5
common/lib/xmodule/xmodule/tests/test_capa_module.py
+45
-0
lms/djangoapps/courseware/features/problems.feature
+42
-0
lms/djangoapps/courseware/features/problems.py
+5
-0
lms/templates/problem.html
+3
-3
lms/templates/problem_ajax.html
+1
-1
No files found.
common/lib/xmodule/xmodule/capa_module.py
View file @
1fc4ac86
...
@@ -309,7 +309,13 @@ class CapaModule(CapaFields, XModule):
...
@@ -309,7 +309,13 @@ class CapaModule(CapaFields, XModule):
d
=
self
.
get_score
()
d
=
self
.
get_score
()
score
=
d
[
'score'
]
score
=
d
[
'score'
]
total
=
d
[
'total'
]
total
=
d
[
'total'
]
if
total
>
0
:
if
total
>
0
:
if
self
.
weight
is
not
None
:
# scale score and total by weight/total:
score
=
score
*
self
.
weight
/
total
total
=
self
.
weight
try
:
try
:
return
Progress
(
score
,
total
)
return
Progress
(
score
,
total
)
except
(
TypeError
,
ValueError
):
except
(
TypeError
,
ValueError
):
...
@@ -321,11 +327,13 @@ class CapaModule(CapaFields, XModule):
...
@@ -321,11 +327,13 @@ class CapaModule(CapaFields, XModule):
"""
"""
Return some html with data about the module
Return some html with data about the module
"""
"""
progress
=
self
.
get_progress
()
return
self
.
system
.
render_template
(
'problem_ajax.html'
,
{
return
self
.
system
.
render_template
(
'problem_ajax.html'
,
{
'element_id'
:
self
.
location
.
html_id
(),
'element_id'
:
self
.
location
.
html_id
(),
'id'
:
self
.
id
,
'id'
:
self
.
id
,
'ajax_url'
:
self
.
system
.
ajax_url
,
'ajax_url'
:
self
.
system
.
ajax_url
,
'progress'
:
Progress
.
to_js_status_str
(
self
.
get_progress
())
'progress_status'
:
Progress
.
to_js_status_str
(
progress
),
'progress_detail'
:
Progress
.
to_js_detail_str
(
progress
),
})
})
def
check_button_name
(
self
):
def
check_button_name
(
self
):
...
@@ -485,8 +493,7 @@ class CapaModule(CapaFields, XModule):
...
@@ -485,8 +493,7 @@ class CapaModule(CapaFields, XModule):
"""
"""
Return html for the problem.
Return html for the problem.
Adds check, reset, save buttons as necessary based on the problem config
Adds check, reset, save buttons as necessary based on the problem config and state.
and state.
"""
"""
try
:
try
:
...
@@ -516,13 +523,12 @@ class CapaModule(CapaFields, XModule):
...
@@ -516,13 +523,12 @@ class CapaModule(CapaFields, XModule):
'reset_button'
:
self
.
should_show_reset_button
(),
'reset_button'
:
self
.
should_show_reset_button
(),
'save_button'
:
self
.
should_show_save_button
(),
'save_button'
:
self
.
should_show_save_button
(),
'answer_available'
:
self
.
answer_available
(),
'answer_available'
:
self
.
answer_available
(),
'ajax_url'
:
self
.
system
.
ajax_url
,
'attempts_used'
:
self
.
attempts
,
'attempts_used'
:
self
.
attempts
,
'attempts_allowed'
:
self
.
max_attempts
,
'attempts_allowed'
:
self
.
max_attempts
,
'progress'
:
self
.
get_progress
(),
}
}
html
=
self
.
system
.
render_template
(
'problem.html'
,
context
)
html
=
self
.
system
.
render_template
(
'problem.html'
,
context
)
if
encapsulate
:
if
encapsulate
:
html
=
u'<div id="problem_{id}" class="problem" data-url="{ajax_url}">'
.
format
(
html
=
u'<div id="problem_{id}" class="problem" data-url="{ajax_url}">'
.
format
(
id
=
self
.
location
.
html_id
(),
ajax_url
=
self
.
system
.
ajax_url
id
=
self
.
location
.
html_id
(),
ajax_url
=
self
.
system
.
ajax_url
...
@@ -584,6 +590,7 @@ class CapaModule(CapaFields, XModule):
...
@@ -584,6 +590,7 @@ class CapaModule(CapaFields, XModule):
result
.
update
({
result
.
update
({
'progress_changed'
:
after
!=
before
,
'progress_changed'
:
after
!=
before
,
'progress_status'
:
Progress
.
to_js_status_str
(
after
),
'progress_status'
:
Progress
.
to_js_status_str
(
after
),
'progress_detail'
:
Progress
.
to_js_detail_str
(
after
),
})
})
return
json
.
dumps
(
result
,
cls
=
ComplexEncoder
)
return
json
.
dumps
(
result
,
cls
=
ComplexEncoder
)
...
@@ -607,13 +614,7 @@ class CapaModule(CapaFields, XModule):
...
@@ -607,13 +614,7 @@ class CapaModule(CapaFields, XModule):
return
False
return
False
def
is_submitted
(
self
):
def
is_submitted
(
self
):
"""
# used by conditional module
Used to decide to show or hide RESET or CHECK buttons.
Means that student submitted problem and nothing more.
Problem can be completely wrong.
Pressing RESET button makes this function to return False.
"""
return
self
.
lcp
.
done
return
self
.
lcp
.
done
def
is_attempted
(
self
):
def
is_attempted
(
self
):
...
@@ -755,7 +756,8 @@ class CapaModule(CapaFields, XModule):
...
@@ -755,7 +756,8 @@ class CapaModule(CapaFields, XModule):
Used if we want to reconfirm we have the right thing e.g. after
Used if we want to reconfirm we have the right thing e.g. after
several AJAX calls.
several AJAX calls.
"""
"""
return
{
'html'
:
self
.
get_problem_html
(
encapsulate
=
False
)}
return
{
'html'
:
self
.
get_problem_html
()}
@staticmethod
@staticmethod
def
make_dict_of_responses
(
data
):
def
make_dict_of_responses
(
data
):
...
@@ -934,7 +936,7 @@ class CapaModule(CapaFields, XModule):
...
@@ -934,7 +936,7 @@ class CapaModule(CapaFields, XModule):
self
.
system
.
psychometrics_handler
(
self
.
get_state_for_lcp
())
self
.
system
.
psychometrics_handler
(
self
.
get_state_for_lcp
())
# render problem into HTML
# render problem into HTML
html
=
self
.
get_problem_html
(
encapsulate
=
False
)
html
=
self
.
get_problem_html
()
return
{
'success'
:
success
,
return
{
'success'
:
success
,
'contents'
:
html
,
'contents'
:
html
,
...
@@ -1101,7 +1103,7 @@ class CapaModule(CapaFields, XModule):
...
@@ -1101,7 +1103,7 @@ class CapaModule(CapaFields, XModule):
self
.
system
.
track_function
(
'reset_problem'
,
event_info
)
self
.
system
.
track_function
(
'reset_problem'
,
event_info
)
return
{
'success'
:
True
,
return
{
'success'
:
True
,
'html'
:
self
.
get_problem_html
(
encapsulate
=
False
)}
'html'
:
self
.
get_problem_html
()}
class
CapaDescriptor
(
CapaFields
,
RawDescriptor
):
class
CapaDescriptor
(
CapaFields
,
RawDescriptor
):
...
...
common/lib/xmodule/xmodule/css/capa/display.scss
View file @
1fc4ac86
...
@@ -3,6 +3,7 @@ h2 {
...
@@ -3,6 +3,7 @@ h2 {
margin-bottom
:
15px
;
margin-bottom
:
15px
;
&
.problem-header
{
&
.problem-header
{
display
:
inline-block
;
section
.staff
{
section
.staff
{
margin-top
:
30px
;
margin-top
:
30px
;
font-size
:
80%
;
font-size
:
80%
;
...
@@ -28,6 +29,13 @@ iframe[seamless]{
...
@@ -28,6 +29,13 @@ iframe[seamless]{
color
:
darken
(
$error-red
,
11%
);
color
:
darken
(
$error-red
,
11%
);
}
}
section
.problem-progress
{
display
:
inline-block
;
color
:
#999
;
font-size
:
em
(
16
);
font-weight
:
100
;
padding-left
:
5px
;
}
section
.problem
{
section
.problem
{
@media
print
{
@media
print
{
...
...
common/lib/xmodule/xmodule/js/fixtures/problem.html
View file @
1fc4ac86
<section
class=
'xmodule_display xmodule_CapaModule'
data-type=
'Problem'
>
<section
class=
'xmodule_display xmodule_CapaModule'
data-type=
'Problem'
>
<section
id=
'problem_1'
<section
id=
'problem_1'
class=
'problems-wrapper'
class=
'problems-wrapper'
data-problem-id=
'i4x://edX/101/problem/Problem1'
data-problem-id=
'i4x://edX/101/problem/Problem1'
data-url=
'/problem/Problem1'
>
data-url=
'/problem/Problem1'
>
</section>
</section>
...
...
common/lib/xmodule/xmodule/js/fixtures/problem_content.html
View file @
1fc4ac86
<h2
class=
"problem-header"
>
Problem Header
</h2>
<h2
class=
"problem-header"
>
Problem Header
</h2>
<section
class=
'problem-progress'
>
</section>
<section
class=
"problem"
>
<section
class=
"problem"
>
<p>
Problem Content
</p>
<p>
Problem Content
</p>
...
...
common/lib/xmodule/xmodule/js/spec/capa/display_spec.coffee
View file @
1fc4ac86
...
@@ -77,6 +77,25 @@ describe 'Problem', ->
...
@@ -77,6 +77,25 @@ describe 'Problem', ->
[
@
problem
.
updateMathML
,
@
stubbedJax
,
$
(
'#input_example_1'
).
get
(
0
)]
[
@
problem
.
updateMathML
,
@
stubbedJax
,
$
(
'#input_example_1'
).
get
(
0
)]
]
]
describe
'renderProgressState'
,
->
beforeEach
->
@
problem
=
new
Problem
(
$
(
'.xmodule_display'
))
#@renderProgressState = @problem.renderProgressState
describe
'with a status of "none"'
,
->
it
'reports the number of points possible'
,
->
@
problem
.
el
.
data
(
'progress_status'
,
'none'
)
@
problem
.
el
.
data
(
'progress_detail'
,
'0/1'
)
@
problem
.
renderProgressState
()
expect
(
@
problem
.
$
(
'.problem-progress'
).
html
()).
toEqual
"(1 point possible)"
describe
'with any other valid status'
,
->
it
'reports the current score'
,
->
@
problem
.
el
.
data
(
'progress_status'
,
'foo'
)
@
problem
.
el
.
data
(
'progress_detail'
,
'1/1'
)
@
problem
.
renderProgressState
()
expect
(
@
problem
.
$
(
'.problem-progress'
).
html
()).
toEqual
"(1/1 points)"
describe
'render'
,
->
describe
'render'
,
->
beforeEach
->
beforeEach
->
@
problem
=
new
Problem
(
$
(
'.xmodule_display'
))
@
problem
=
new
Problem
(
$
(
'.xmodule_display'
))
...
...
common/lib/xmodule/xmodule/js/src/capa/display.coffee
View file @
1fc4ac86
...
@@ -35,15 +35,31 @@ class @Problem
...
@@ -35,15 +35,31 @@ class @Problem
@
$
(
'input.math'
).
each
(
index
,
element
)
=>
@
$
(
'input.math'
).
each
(
index
,
element
)
=>
MathJax
.
Hub
.
Queue
[
@
refreshMath
,
null
,
element
]
MathJax
.
Hub
.
Queue
[
@
refreshMath
,
null
,
element
]
renderProgressState
:
()
=>
detail
=
@
el
.
data
(
'progress_detail'
)
status
=
@
el
.
data
(
'progress_status'
)
progress
=
"(
#{
detail
}
points)"
if
status
==
'none'
and
detail
?
and
detail
.
indexOf
(
'/'
)
>
0
a
=
detail
.
split
(
'/'
)
possible
=
parseInt
(
a
[
1
])
if
possible
==
1
progress
=
"(
#{
possible
}
point possible)"
else
progress
=
"(
#{
possible
}
points possible)"
@
$
(
'.problem-progress'
).
html
(
progress
)
updateProgress
:
(
response
)
=>
updateProgress
:
(
response
)
=>
if
response
.
progress_changed
if
response
.
progress_changed
@
el
.
attr
progress
:
response
.
progress_status
@
el
.
data
(
'progress_status'
,
response
.
progress_status
)
@
el
.
data
(
'progress_detail'
,
response
.
progress_detail
)
@
el
.
trigger
(
'progressChanged'
)
@
el
.
trigger
(
'progressChanged'
)
@
renderProgressState
()
forceUpdate
:
(
response
)
=>
forceUpdate
:
(
response
)
=>
@
el
.
attr
progress
:
response
.
progress_status
@
el
.
data
(
'progress_status'
,
response
.
progress_status
)
@
el
.
data
(
'progress_detail'
,
response
.
progress_detail
)
@
el
.
trigger
(
'progressChanged'
)
@
el
.
trigger
(
'progressChanged'
)
@
renderProgressState
()
queueing
:
=>
queueing
:
=>
@
queued_items
=
@
$
(
".xqueue"
)
@
queued_items
=
@
$
(
".xqueue"
)
...
@@ -113,7 +129,7 @@ class @Problem
...
@@ -113,7 +129,7 @@ class @Problem
@
setupInputTypes
()
@
setupInputTypes
()
@
bind
()
@
bind
()
@
queueing
()
@
queueing
()
@
renderProgressState
()
# TODO add hooks for problem types here by inspecting response.html and doing
# TODO add hooks for problem types here by inspecting response.html and doing
# stuff if a div w a class is found
# stuff if a div w a class is found
...
@@ -240,7 +256,7 @@ class @Problem
...
@@ -240,7 +256,7 @@ class @Problem
analytics
.
track
"Problem Checked"
,
analytics
.
track
"Problem Checked"
,
problem_id
:
@
id
problem_id
:
@
id
answers
:
@
answers
answers
:
@
answers
$
.
postWithPrefix
"
#{
@
url
}
/problem_check"
,
@
answers
,
(
response
)
=>
$
.
postWithPrefix
"
#{
@
url
}
/problem_check"
,
@
answers
,
(
response
)
=>
switch
response
.
success
switch
response
.
success
when
'incorrect'
,
'correct'
when
'incorrect'
,
'correct'
...
...
common/lib/xmodule/xmodule/tests/test_capa_module.py
View file @
1fc4ac86
...
@@ -1233,6 +1233,51 @@ class CapaModuleTest(unittest.TestCase):
...
@@ -1233,6 +1233,51 @@ class CapaModuleTest(unittest.TestCase):
mock_log
.
exception
.
assert_called_once_with
(
'Got bad progress'
)
mock_log
.
exception
.
assert_called_once_with
(
'Got bad progress'
)
mock_log
.
reset_mock
()
mock_log
.
reset_mock
()
@patch
(
'xmodule.capa_module.Progress'
)
def
test_get_progress_calculate_progress_fraction
(
self
,
mock_progress
):
"""
Check that score and total are calculated correctly for the progress fraction.
"""
module
=
CapaFactory
.
create
()
module
.
weight
=
1
module
.
get_progress
()
mock_progress
.
assert_called_with
(
0
,
1
)
other_module
=
CapaFactory
.
create
(
correct
=
True
)
other_module
.
weight
=
1
other_module
.
get_progress
()
mock_progress
.
assert_called_with
(
1
,
1
)
@patch
(
'xmodule.capa_module.Progress'
)
def
test_get_progress_calculate_progress_fraction
(
self
,
mock_progress
):
"""
Check that score and total are calculated correctly for the progress fraction.
"""
module
=
CapaFactory
.
create
()
module
.
get_progress
()
mock_progress
.
assert_called_with
(
0
,
1
)
other_module
=
CapaFactory
.
create
(
correct
=
True
)
other_module
.
get_progress
()
mock_progress
.
assert_called_with
(
1
,
1
)
def
test_get_html
(
self
):
"""
Check that get_html() calls get_progress() with no arguments.
"""
module
=
CapaFactory
.
create
()
module
.
get_progress
=
Mock
(
wraps
=
module
.
get_progress
)
module
.
get_html
()
module
.
get_progress
.
assert_called_once_with
()
def
test_get_problem
(
self
):
"""
Check that get_problem() returns the expected dictionary.
"""
module
=
CapaFactory
.
create
()
self
.
assertEquals
(
module
.
get_problem
(
"data"
),
{
'html'
:
module
.
get_problem_html
()})
class
ComplexEncoderTest
(
unittest
.
TestCase
):
class
ComplexEncoderTest
(
unittest
.
TestCase
):
def
test_default
(
self
):
def
test_default
(
self
):
...
...
lms/djangoapps/courseware/features/problems.feature
View file @
1fc4ac86
...
@@ -129,3 +129,45 @@ Feature: Answer problems
...
@@ -129,3 +129,45 @@ Feature: Answer problems
When
I press the button with the label
"Hide Answer(s)"
When
I press the button with the label
"Hide Answer(s)"
Then
the button with the label
"Show Answer(s)"
does appear
Then
the button with the label
"Show Answer(s)"
does appear
And
I should not see
"4.14159"
anywhere on the page
And
I should not see
"4.14159"
anywhere on the page
Scenario
:
I
can see my score on a problem when I answer it and after I reset it
Given
I am viewing a
"<ProblemType>"
problem
When
I answer a
"<ProblemType>"
problem
"<Correctness>ly"
Then
I should see a score of
"<Score>"
When
I reset the problem
Then
I should see a score of
"<Points Possible>"
Examples
:
|
ProblemType
|
Correctness
|
Score
|
Points
Possible
|
|
drop
down
|
correct
|
1/1
points
|
1
point
possible
|
|
drop
down
|
incorrect
|
1
point
possible
|
1
point
possible
|
|
multiple
choice
|
correct
|
1/1
points
|
1
point
possible
|
|
multiple
choice
|
incorrect
|
1
point
possible
|
1
point
possible
|
|
checkbox
|
correct
|
1/1
points
|
1
point
possible
|
|
checkbox
|
incorrect
|
1
point
possible
|
1
point
possible
|
|
radio
|
correct
|
1/1
points
|
1
point
possible
|
|
radio
|
incorrect
|
1
point
possible
|
1
point
possible
|
|
string
|
correct
|
1/1
points
|
1
point
possible
|
|
string
|
incorrect
|
1
point
possible
|
1
point
possible
|
|
numerical
|
correct
|
1/1
points
|
1
point
possible
|
|
numerical
|
incorrect
|
1
point
possible
|
1
point
possible
|
|
formula
|
correct
|
1/1
points
|
1
point
possible
|
|
formula
|
incorrect
|
1
point
possible
|
1
point
possible
|
|
script
|
correct
|
2/2
points
|
2
points
possible
|
|
script
|
incorrect
|
2
points
possible
|
2
points
possible
|
Scenario
:
I
can see my score on a problem to which I submit a blank answer
Given
I am viewing a
"<ProblemType>"
problem
When
I check a problem
Then
I should see a score of
"<Points Possible>"
Examples
:
|
ProblemType
|
Points
Possible
|
|
drop
down
|
1
point
possible
|
|
multiple
choice
|
1
point
possible
|
|
checkbox
|
1
point
possible
|
|
radio
|
1
point
possible
|
|
string
|
1
point
possible
|
|
numerical
|
1
point
possible
|
|
formula
|
1
point
possible
|
|
script
|
2
points
possible
|
lms/djangoapps/courseware/features/problems.py
View file @
1fc4ac86
...
@@ -142,6 +142,11 @@ def button_with_label_present(_step, buttonname, doesnt_appear):
...
@@ -142,6 +142,11 @@ def button_with_label_present(_step, buttonname, doesnt_appear):
assert
world
.
browser
.
is_text_present
(
buttonname
,
wait_time
=
5
)
assert
world
.
browser
.
is_text_present
(
buttonname
,
wait_time
=
5
)
@step
(
u'I should see a score of "([^"]*)"$'
)
def
see_score
(
_step
,
score
):
assert
world
.
browser
.
is_text_present
(
score
)
@step
(
u'My "([^"]*)" answer is marked "([^"]*)"'
)
@step
(
u'My "([^"]*)" answer is marked "([^"]*)"'
)
def
assert_answer_mark
(
step
,
problem_type
,
correctness
):
def
assert_answer_mark
(
step
,
problem_type
,
correctness
):
"""
"""
...
...
lms/templates/problem.html
View file @
1fc4ac86
<
%
namespace
name=
'static'
file=
'static_content.html'
/>
<
%
namespace
name=
'static'
file=
'static_content.html'
/>
<h2
class=
"problem-header"
>
<h2
class=
"problem-header"
>
${ problem['name'] }
${ problem['name'] }
% if problem['weight'] != 1 and problem['weight'] is not None:
: ${ problem['weight'] } points
% endif
</h2>
</h2>
<section
class=
"problem-progress"
>
</section>
<section
class=
"problem"
>
<section
class=
"problem"
>
${ problem['html'] }
${ problem['html'] }
...
...
lms/templates/problem_ajax.html
View file @
1fc4ac86
<section
id=
"problem_${element_id}"
class=
"problems-wrapper"
data-problem-id=
"${id}"
data-url=
"${ajax_url}"
progress=
"${progress
}"
></section>
<section
id=
"problem_${element_id}"
class=
"problems-wrapper"
data-problem-id=
"${id}"
data-url=
"${ajax_url}"
data-progress_status=
"${progress_status}"
data-progress_detail=
"${progress_detail
}"
></section>
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