Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
problem-builder
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
problem-builder
Commits
ff61a46d
Commit
ff61a46d
authored
Mar 25, 2016
by
Matjaz Gregoric
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #110 from open-craft/freetext-submit
Enable freeform answer submit when feedback is hidden
parents
8b4fd309
0be5bd6f
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
61 additions
and
30 deletions
+61
-30
problem_builder/mentoring.py
+7
-4
problem_builder/public/js/answer.js
+5
-0
problem_builder/public/js/mentoring_standard_view.js
+6
-2
problem_builder/public/js/mentoring_with_steps.js
+2
-1
problem_builder/public/js/questionnaire.js
+5
-1
problem_builder/templates/html/mentoring.html
+1
-1
problem_builder/templates/html/mrqblock.html
+1
-1
problem_builder/tests/integration/test_mentoring.py
+32
-19
problem_builder/tests/unit/test_problem_builder.py
+2
-1
No files found.
problem_builder/mentoring.py
View file @
ff61a46d
...
@@ -374,7 +374,7 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
...
@@ -374,7 +374,7 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
return
Score
(
score
,
int
(
round
(
score
*
100
)),
correct
,
incorrect
,
partially_correct
)
return
Score
(
score
,
int
(
round
(
score
*
100
)),
correct
,
incorrect
,
partially_correct
)
def
student_view
(
self
,
context
):
def
student_view
(
self
,
context
):
from
.
mcq
import
MCQ
Block
# Import here to avoid circular dependency
from
.
questionnaire
import
QuestionnaireAbstract
Block
# Import here to avoid circular dependency
# Migrate stored data if necessary
# Migrate stored data if necessary
self
.
migrate_fields
()
self
.
migrate_fields
()
...
@@ -398,7 +398,7 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
...
@@ -398,7 +398,7 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
if
self
.
is_assessment
and
isinstance
(
child
,
QuestionMixin
):
if
self
.
is_assessment
and
isinstance
(
child
,
QuestionMixin
):
child_fragment
=
child
.
render
(
'assessment_step_view'
,
context
)
child_fragment
=
child
.
render
(
'assessment_step_view'
,
context
)
else
:
else
:
if
mcq_hide_previous_answer
and
isinstance
(
child
,
MCQ
Block
):
if
mcq_hide_previous_answer
and
isinstance
(
child
,
QuestionnaireAbstract
Block
):
context
[
'hide_prev_answer'
]
=
True
context
[
'hide_prev_answer'
]
=
True
else
:
else
:
context
[
'hide_prev_answer'
]
=
False
context
[
'hide_prev_answer'
]
=
False
...
@@ -478,6 +478,10 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
...
@@ -478,6 +478,10 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
"""
"""
return
'/jump_to_id/{}'
.
format
(
self
.
next_step
)
return
'/jump_to_id/{}'
.
format
(
self
.
next_step
)
@property
def
hide_feedback
(
self
):
return
self
.
get_option
(
"pb_hide_feedback_if_attempts_remain"
)
and
not
self
.
max_attempts_reached
def
get_message
(
self
,
completed
):
def
get_message
(
self
,
completed
):
"""
"""
Get the message to display to a student following a submission in normal mode.
Get the message to display to a student following a submission in normal mode.
...
@@ -564,8 +568,7 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
...
@@ -564,8 +568,7 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
"""
"""
results
=
[]
results
=
[]
completed
=
True
completed
=
True
hide_feedback
=
self
.
get_option
(
"pb_hide_feedback_if_attempts_remain"
)
and
not
self
.
max_attempts_reached
show_message
=
(
not
self
.
hide_feedback
)
and
bool
(
self
.
student_results
)
show_message
=
(
not
hide_feedback
)
and
bool
(
self
.
student_results
)
# In standard mode, all children are visible simultaneously, so need to collect results for all of them
# In standard mode, all children are visible simultaneously, so need to collect results for all of them
for
child
in
self
.
steps
:
for
child
in
self
.
steps
:
...
...
problem_builder/public/js/answer.js
View file @
ff61a46d
...
@@ -6,6 +6,7 @@ function AnswerBlock(runtime, element) {
...
@@ -6,6 +6,7 @@ function AnswerBlock(runtime, element) {
$
(
':input'
,
element
).
on
(
'keyup'
,
options
.
onChange
);
$
(
':input'
,
element
).
on
(
'keyup'
,
options
.
onChange
);
this
.
mode
=
options
.
mode
;
this
.
mode
=
options
.
mode
;
this
.
validateXBlock
=
options
.
validateXBlock
;
// In the LMS, the HTML of multiple units can be loaded at once,
// In the LMS, the HTML of multiple units can be loaded at once,
// and the user can flip among them. If that happens, the answer in
// and the user can flip among them. If that happens, the answer in
...
@@ -72,6 +73,7 @@ function AnswerBlock(runtime, element) {
...
@@ -72,6 +73,7 @@ function AnswerBlock(runtime, element) {
},
},
refreshAnswer
:
function
()
{
refreshAnswer
:
function
()
{
var
self
=
this
;
$
.
ajax
({
$
.
ajax
({
type
:
'POST'
,
type
:
'POST'
,
url
:
runtime
.
handlerUrl
(
element
,
'answer_value'
),
url
:
runtime
.
handlerUrl
(
element
,
'answer_value'
),
...
@@ -86,6 +88,9 @@ function AnswerBlock(runtime, element) {
...
@@ -86,6 +88,9 @@ function AnswerBlock(runtime, element) {
if
(
currentAnswer
==
origAnswer
&&
currentAnswer
!=
newAnswer
)
{
if
(
currentAnswer
==
origAnswer
&&
currentAnswer
!=
newAnswer
)
{
$textarea
.
val
(
newAnswer
);
$textarea
.
val
(
newAnswer
);
}
}
if
(
self
.
validateXBlock
)
{
self
.
validateXBlock
();
}
},
},
});
});
}
}
...
...
problem_builder/public/js/mentoring_standard_view.js
View file @
ff61a46d
...
@@ -35,10 +35,13 @@ function MentoringStandardView(runtime, element, mentoring) {
...
@@ -35,10 +35,13 @@ function MentoringStandardView(runtime, element, mentoring) {
}
}
}
}
// Data may have changed, we have to re-validate.
validateXBlock
();
// Disable the submit button if we have just submitted new answers,
// Disable the submit button if we have just submitted new answers,
// or if we have just [re]loaded the page and are showing a complete set
// or if we have just [re]loaded the page and are showing a complete set
// of old answers.
// of old answers.
if
(
disable_submit
||
all_have_results
)
{
if
(
disable_submit
||
(
all_have_results
&&
mentoring
.
data
.
hide_feedback
!==
'True'
)
)
{
submitDOM
.
attr
(
'disabled'
,
'disabled'
);
submitDOM
.
attr
(
'disabled'
,
'disabled'
);
}
}
}
}
...
@@ -110,7 +113,8 @@ function MentoringStandardView(runtime, element, mentoring) {
...
@@ -110,7 +113,8 @@ function MentoringStandardView(runtime, element, mentoring) {
submitDOM
.
show
();
submitDOM
.
show
();
var
options
=
{
var
options
=
{
onChange
:
onChange
onChange
:
onChange
,
validateXBlock
:
validateXBlock
};
};
mentoring
.
initChildren
(
options
);
mentoring
.
initChildren
(
options
);
...
...
problem_builder/public/js/mentoring_with_steps.js
View file @
ff61a46d
...
@@ -302,7 +302,8 @@ function MentoringWithStepsBlock(runtime, element) {
...
@@ -302,7 +302,8 @@ function MentoringWithStepsBlock(runtime, element) {
var
step
=
steps
[
i
];
var
step
=
steps
[
i
];
var
mentoring
=
{
var
mentoring
=
{
setContent
:
setContent
,
setContent
:
setContent
,
publish_event
:
publishEvent
publish_event
:
publishEvent
,
is_step_builder
:
true
};
};
options
.
mentoring
=
mentoring
;
options
.
mentoring
=
mentoring
;
step
.
initChildren
(
options
);
step
.
initChildren
(
options
);
...
...
problem_builder/public/js/questionnaire.js
View file @
ff61a46d
...
@@ -210,7 +210,11 @@ function MRQBlock(runtime, element) {
...
@@ -210,7 +210,11 @@ function MRQBlock(runtime, element) {
var
questionnaireDOM
=
$
(
'fieldset.questionnaire'
,
element
);
var
questionnaireDOM
=
$
(
'fieldset.questionnaire'
,
element
);
var
data
=
questionnaireDOM
.
data
();
var
data
=
questionnaireDOM
.
data
();
var
hide_results
=
(
data
.
hide_results
===
'True'
);
var
hide_results
=
(
data
.
hide_results
===
'True'
||
(
data
.
hide_prev_answer
===
'True'
&&
!
mentoring
.
is_step_builder
));
// hide_prev_answer should only take effect when we initially render (previous) results,
// so set hide_prev_answer to False after initial render.
questionnaireDOM
.
data
(
'hide_prev_answer'
,
'False'
);
$
.
each
(
result
.
choices
,
function
(
index
,
choice
)
{
$
.
each
(
result
.
choices
,
function
(
index
,
choice
)
{
var
choiceInputDOM
=
$
(
'.choice input[value='
+
choice
.
value
+
']'
,
element
);
var
choiceInputDOM
=
$
(
'.choice input[value='
+
choice
.
value
+
']'
,
element
);
...
...
problem_builder/templates/html/mentoring.html
View file @
ff61a46d
{% load i18n %}
{% load i18n %}
<div
class=
"mentoring themed-xblock"
data-mode=
"{{ self.mode }}"
data-step=
"{{ self.step }}"
data-feedback_label=
"{{ self.feedback_label}}"
>
<div
class=
"mentoring themed-xblock"
data-mode=
"{{ self.mode }}"
data-step=
"{{ self.step }}"
data-feedback_label=
"{{ self.feedback_label
}}"
data-hide_feedback=
"{{ self.hide_feedback
}}"
>
<div
class=
"missing-dependency warning"
data-missing=
"{{ self.has_missing_dependency }}"
>
<div
class=
"missing-dependency warning"
data-missing=
"{{ self.has_missing_dependency }}"
>
{% with url=missing_dependency_url|safe %}
{% with url=missing_dependency_url|safe %}
{% blocktrans with link_start="
<a
href=
'"|add:url|add:"'
>
" link_end="
</a>
" %}
{% blocktrans with link_start="
<a
href=
'"|add:url|add:"'
>
" link_end="
</a>
" %}
...
...
problem_builder/templates/html/mrqblock.html
View file @
ff61a46d
<fieldset
class=
"choices questionnaire"
data-hide_results=
"{{self.hide_results}}"
>
<fieldset
class=
"choices questionnaire"
data-hide_results=
"{{self.hide_results}}"
data-hide_prev_answer=
"{{hide_prev_answer}}"
>
<legend
class=
"question"
>
<legend
class=
"question"
>
{% if not hide_header %}
<h3
class=
"question-title"
>
{{ self.display_name_with_default }}
</h3>
{% endif %}
{% if not hide_header %}
<h3
class=
"question-title"
>
{{ self.display_name_with_default }}
</h3>
{% endif %}
<p>
{{ self.question|safe }}
</p>
<p>
{{ self.question|safe }}
</p>
...
...
problem_builder/tests/integration/test_mentoring.py
View file @
ff61a46d
...
@@ -164,17 +164,22 @@ class ProblemBuilderQuestionnaireBlockTest(ProblemBuilderBaseTest):
...
@@ -164,17 +164,22 @@ class ProblemBuilderQuestionnaireBlockTest(ProblemBuilderBaseTest):
choice_input
=
choice
.
find_element_by_css_selector
(
'input'
)
choice_input
=
choice
.
find_element_by_css_selector
(
'input'
)
self
.
assertFalse
(
choice_input
.
is_selected
())
self
.
assertFalse
(
choice_input
.
is_selected
())
def
_assert_mrq
(
self
,
mrq
):
def
_assert_mrq
(
self
,
mrq
,
previous_answer_shown
=
True
):
self
.
_assert_feedback_shown
(
if
previous_answer_shown
:
mrq
,
0
,
"This is something everyone has to like about this MRQ"
,
self
.
_assert_feedback_shown
(
click_choice_result
=
True
mrq
,
0
,
"This is something everyone has to like about this MRQ"
,
)
click_choice_result
=
True
self
.
_assert_feedback_shown
(
)
mrq
,
1
,
"This is something everyone has to like about beauty"
,
self
.
_assert_feedback_shown
(
click_choice_result
=
True
,
success
=
False
mrq
,
1
,
"This is something everyone has to like about beauty"
,
)
click_choice_result
=
True
,
success
=
False
self
.
_assert_feedback_shown
(
mrq
,
2
,
"This MRQ is indeed very graceful"
,
click_choice_result
=
True
)
)
self
.
_assert_feedback_shown
(
mrq
,
3
,
"Nah, there aren't any!"
,
click_choice_result
=
True
,
success
=
False
)
self
.
_assert_feedback_shown
(
mrq
,
2
,
"This MRQ is indeed very graceful"
,
click_choice_result
=
True
)
self
.
_assert_feedback_shown
(
mrq
,
3
,
"Nah, there aren't any!"
,
click_choice_result
=
True
,
success
=
False
)
else
:
for
i
in
range
(
3
):
self
.
_assert_feedback_hidden
(
mrq
,
i
)
self
.
_assert_not_checked
(
mrq
,
i
)
def
_assert_messages
(
self
,
messages
,
shown
=
True
):
def
_assert_messages
(
self
,
messages
,
shown
=
True
):
if
shown
:
if
shown
:
...
@@ -227,8 +232,8 @@ class ProblemBuilderQuestionnaireBlockTest(ProblemBuilderBaseTest):
...
@@ -227,8 +232,8 @@ class ProblemBuilderQuestionnaireBlockTest(ProblemBuilderBaseTest):
self
.
_assert_answer
(
answer
)
self
.
_assert_answer
(
answer
)
# MCQ: Previous answer and results hidden
# MCQ: Previous answer and results hidden
self
.
_assert_mcq
(
mcq
,
previous_answer_shown
=
False
)
self
.
_assert_mcq
(
mcq
,
previous_answer_shown
=
False
)
# MRQ: Previous answer and results
visible
# MRQ: Previous answer and results
hidden
self
.
_assert_mrq
(
mrq
)
self
.
_assert_mrq
(
mrq
,
previous_answer_shown
=
False
)
# Rating: Previous answer and results hidden
# Rating: Previous answer and results hidden
self
.
_assert_rating
(
rating
,
previous_answer_shown
=
False
)
self
.
_assert_rating
(
rating
,
previous_answer_shown
=
False
)
# Messages visible
# Messages visible
...
@@ -251,8 +256,8 @@ class ProblemBuilderQuestionnaireBlockTest(ProblemBuilderBaseTest):
...
@@ -251,8 +256,8 @@ class ProblemBuilderQuestionnaireBlockTest(ProblemBuilderBaseTest):
self
.
_assert_answer
(
answer
,
results_shown
=
False
)
self
.
_assert_answer
(
answer
,
results_shown
=
False
)
# MCQ: Previous answer and results hidden
# MCQ: Previous answer and results hidden
self
.
_assert_mcq
(
mcq
,
previous_answer_shown
=
False
)
self
.
_assert_mcq
(
mcq
,
previous_answer_shown
=
False
)
# MRQ: Previous answer and results
visible
# MRQ: Previous answer and results
hidden
self
.
_assert_mrq
(
mrq
)
self
.
_assert_mrq
(
mrq
,
previous_answer_shown
=
False
)
# Rating: Previous answer and feedback hidden
# Rating: Previous answer and feedback hidden
self
.
_assert_rating
(
rating
,
previous_answer_shown
=
False
)
self
.
_assert_rating
(
rating
,
previous_answer_shown
=
False
)
# Messages hidden
# Messages hidden
...
@@ -338,11 +343,19 @@ class ProblemBuilderQuestionnaireBlockTest(ProblemBuilderBaseTest):
...
@@ -338,11 +343,19 @@ class ProblemBuilderQuestionnaireBlockTest(ProblemBuilderBaseTest):
# ... and see if previous answers, results, feedback are shown/hidden correctly
# ... and see if previous answers, results, feedback are shown/hidden correctly
getattr
(
self
,
after_reload_checks
)(
answer
,
mcq
,
mrq
,
rating
,
messages
)
getattr
(
self
,
after_reload_checks
)(
answer
,
mcq
,
mrq
,
rating
,
messages
)
# After reloading, submit is disabled...
# After reloading, submit is enabled only when:
self
.
assertFalse
(
submit
.
is_enabled
())
# - feedback is hidden; and
# - previous MCQ/MRQ answers are visible.
# ... until student makes changes
# When feedback is visible there's no need to resubmit the same answer;
# and when previous MCQ/MRQ answers are hidden, submit is disabled until you select some options.
if
options
[
'pb_hide_feedback_if_attempts_remain'
]
and
not
options
[
'pb_mcq_hide_previous_answer'
]:
self
.
assertTrue
(
submit
.
is_enabled
())
else
:
self
.
assertFalse
(
submit
.
is_enabled
())
# When student makes changes, submit is enabled again.
self
.
click_choice
(
mcq
,
"Maybe not"
)
self
.
click_choice
(
mcq
,
"Maybe not"
)
self
.
click_choice
(
mrq
,
"Its elegance"
)
self
.
click_choice
(
rating
,
"2"
)
self
.
click_choice
(
rating
,
"2"
)
self
.
assertTrue
(
submit
.
is_enabled
())
self
.
assertTrue
(
submit
.
is_enabled
())
...
...
problem_builder/tests/unit/test_problem_builder.py
View file @
ff61a46d
...
@@ -227,7 +227,8 @@ class TestMentoringBlockOptions(unittest.TestCase):
...
@@ -227,7 +227,8 @@ class TestMentoringBlockOptions(unittest.TestCase):
self
.
block
.
get_xblock_settings
=
Mock
(
return_value
=
{})
self
.
block
.
get_xblock_settings
=
Mock
(
return_value
=
{})
with
patch
.
object
(
self
.
block
,
'get_option'
)
as
patched_get_option
:
with
patch
.
object
(
self
.
block
,
'get_option'
)
as
patched_get_option
:
self
.
block
.
student_view
({})
self
.
block
.
student_view
({})
patched_get_option
.
assert_called_with
(
'pb_mcq_hide_previous_answer'
)
patched_get_option
.
assert_any_call
(
'pb_mcq_hide_previous_answer'
)
patched_get_option
.
assert_any_call
(
'pb_hide_feedback_if_attempts_remain'
)
def
test_get_standard_results_calls_get_option
(
self
):
def
test_get_standard_results_calls_get_option
(
self
):
with
patch
.
object
(
self
.
block
,
'get_option'
)
as
patched_get_option
:
with
patch
.
object
(
self
.
block
,
'get_option'
)
as
patched_get_option
:
...
...
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