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
cc9498cd
Commit
cc9498cd
authored
Oct 31, 2015
by
Braden MacDonald
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactored Step Builder Messages
parent
5b751841
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
479 additions
and
469 deletions
+479
-469
problem_builder/mentoring.py
+30
-18
problem_builder/message.py
+0
-17
problem_builder/plot.py
+2
-2
problem_builder/public/css/problem-builder-edit.css
+7
-0
problem_builder/public/css/problem-builder.css
+5
-0
problem_builder/public/js/container_edit.js
+12
-0
problem_builder/public/js/mentoring_with_steps.js
+30
-68
problem_builder/public/js/mentoring_with_steps_edit.js
+0
-45
problem_builder/public/js/plot_edit.js
+0
-5
problem_builder/public/js/review_step.js
+0
-53
problem_builder/public/js/review_step_edit.js
+0
-46
problem_builder/public/js/step_edit.js
+0
-5
problem_builder/step.py
+5
-105
problem_builder/step_review.py
+291
-0
problem_builder/templates/html/mentoring_review_templates.html
+0
-9
problem_builder/templates/html/mentoring_with_steps.html
+1
-17
problem_builder/templates/html/review_step.html
+0
-78
problem_builder/templates/html/sb-review-per-question-feedback.html
+9
-0
problem_builder/templates/html/sb-review-score.html
+83
-0
setup.py
+4
-1
No files found.
problem_builder/mentoring.py
View file @
cc9498cd
...
...
@@ -35,15 +35,15 @@ from xblock.fragment import Fragment
from
xblock.validation
import
ValidationMessage
from
.message
import
MentoringMessageBlock
from
.mixins
import
(
_normalize_id
,
QuestionMixin
,
MessageParentMixin
,
StepParentMixin
,
XBlockWithTranslationServiceMixin
)
from
.step_review
import
ReviewStepBlock
from
xblockutils.helpers
import
child_isinstance
from
xblockutils.resources
import
ResourceLoader
from
xblockutils.studio_editable
import
(
StudioEditableXBlockMixin
,
StudioContainerXBlockMixin
,
StudioContainerWithNestedXBlocksMixin
NestedXBlockSpec
,
StudioEditableXBlockMixin
,
StudioContainerXBlockMixin
,
StudioContainerWithNestedXBlocksMixin
,
)
...
...
@@ -925,10 +925,16 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes
@property
def
has_review_step
(
self
):
from
.step
import
ReviewStepBlock
return
any
(
child_isinstance
(
self
,
child_id
,
ReviewStepBlock
)
for
child_id
in
self
.
children
)
@property
def
review_step
(
self
):
""" Get the Review Step XBlock child, if any. Otherwise returns None """
for
step_id
in
self
.
children
:
if
child_isinstance
(
self
,
step_id
,
ReviewStepBlock
):
return
self
.
runtime
.
get_block
(
step_id
)
@property
def
score
(
self
):
questions
=
self
.
questions
total_child_weight
=
sum
(
float
(
question
.
weight
)
for
question
in
questions
)
...
...
@@ -981,11 +987,12 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes
context
=
context
or
{}
context
[
'hide_prev_answer'
]
=
True
# For Step Builder, we don't show the users' old answers when they try again
context
[
'score_summary'
]
=
self
.
get_score_summary
()
for
child_id
in
self
.
children
:
child
=
self
.
runtime
.
get_block
(
child_id
)
if
child
is
None
:
# child should not be None but it can happen due to bugs or permission issues
child_content
=
u"<p>[{}]</p>"
.
format
(
self
.
_
(
u"Error: Unable to load child component."
))
el
if
not
isinstance
(
child
,
MentoringMessageBlock
)
:
el
se
:
child_fragment
=
self
.
_render_child_fragment
(
child
,
context
,
view
=
'mentoring_view'
)
fragment
.
add_frag_resources
(
child_fragment
)
child_content
=
child_fragment
.
content
...
...
@@ -1002,11 +1009,12 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/mentoring_with_steps.js'
))
fragment
.
add_resource
(
loader
.
load_unicode
(
'templates/html/mentoring_attempts.html'
),
"text/html"
)
fragment
.
add_resource
(
loader
.
load_unicode
(
'templates/html/mentoring_review_templates.html'
),
"text/html"
)
self
.
include_theme_files
(
fragment
)
fragment
.
initialize_js
(
'MentoringWithStepsBlock'
)
fragment
.
initialize_js
(
'MentoringWithStepsBlock'
,
{
'show_extended_feedback'
:
self
.
show_extended_feedback
(),
})
return
fragment
...
...
@@ -1021,10 +1029,11 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes
NestedXBlockSpec allows explicitly setting disabled/enabled state, disabled reason (if any) and single/multiple
instances
"""
from
.step
import
MentoringStepBlock
,
ReviewStepBlock
# Import here to avoid circular dependency
# Import here to avoid circular dependency
from
.step
import
MentoringStepBlock
return
[
MentoringStepBlock
,
ReviewStepBlock
,
NestedXBlockSpec
(
ReviewStepBlock
,
single_instance
=
True
)
,
]
@XBlock.json_handler
...
...
@@ -1048,11 +1057,15 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes
self
.
active_step
=
new_value
elif
new_value
==
len
(
self
.
step_ids
):
# The user just completed the final step.
if
self
.
has_review_step
:
self
.
active_step
=
-
1
# Update the number of attempts, if necessary:
if
self
.
num_attempts
<
self
.
max_attempts
:
self
.
num_attempts
+=
1
# Do we need to render a review (summary of the user's score):
if
self
.
has_review_step
:
self
.
active_step
=
-
1
response_data
[
'review_html'
]
=
self
.
runtime
.
render
(
self
.
review_step
,
"mentoring_view"
,
{
'score_summary'
:
self
.
get_score_summary
(),
})
.
content
response_data
[
'num_attempts'
]
=
self
.
num_attempts
# And publish the score:
score
=
self
.
score
...
...
@@ -1061,12 +1074,13 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes
'max_value'
:
self
.
max_score
(),
}
self
.
runtime
.
publish
(
self
,
'grade'
,
grade_data
)
response_data
[
'grade_data'
]
=
self
.
get_grade
()
response_data
[
'active_step'
]
=
self
.
active_step
return
response_data
def
get_grade
(
self
,
data
=
None
,
suffix
=
None
):
def
get_score_summary
(
self
):
if
self
.
num_attempts
==
0
:
return
{}
score
=
self
.
score
return
{
'score'
:
score
.
percentage
,
...
...
@@ -1078,7 +1092,7 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes
'partial'
:
self
.
partial_json
(
stringify
=
False
),
'complete'
:
self
.
complete
,
'max_attempts_reached'
:
self
.
max_attempts_reached
,
'
assessment_
review_tips'
:
self
.
review_tips
,
'review_tips'
:
self
.
review_tips
,
}
@XBlock.json_handler
...
...
@@ -1101,9 +1115,7 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes
}
def
author_preview_view
(
self
,
context
):
context
=
context
.
copy
()
if
context
else
{}
context
[
'author_preview_view'
]
=
True
return
super
(
MentoringWithExplicitStepsBlock
,
self
)
.
author_preview_view
(
context
)
return
self
.
student_view
(
context
)
def
author_edit_view
(
self
,
context
):
"""
...
...
@@ -1121,6 +1133,6 @@ class MentoringWithExplicitStepsBlock(BaseMentoringBlock, StudioContainerWithNes
fragment
.
add_css_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/css/problem-builder-edit.css'
))
fragment
.
add_css_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/css/problem-builder-tinymce-content.css'
))
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/util.js'
))
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/
mentoring_with_steps
_edit.js'
))
fragment
.
initialize_js
(
'
MentoringWithSteps
Edit'
)
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/
container
_edit.js'
))
fragment
.
initialize_js
(
'
ProblemBuilderContainer
Edit'
)
return
fragment
problem_builder/message.py
View file @
cc9498cd
...
...
@@ -100,18 +100,6 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla
"used up all of their allowed attempts."
),
},
"on-review"
:
{
"display_name"
:
_
(
u"Message shown when no attempts left"
),
"long_display_name"
:
_
(
u"Message shown during review when no attempts remain"
),
"default"
:
_
(
u"Note: you have used all attempts. Continue to the next unit."
),
"description"
:
_
(
u"This message will be shown when the student is reviewing their answers to the assessment, "
"if the student has used up all of their allowed attempts. "
"It is not shown if the student is allowed to try again."
),
},
}
content
=
String
(
...
...
@@ -203,8 +191,3 @@ class CompletedMentoringMessageShim(object):
class
IncompleteMentoringMessageShim
(
object
):
CATEGORY
=
'pb-message'
STUDIO_LABEL
=
_
(
"Message (Incomplete)"
)
class
OnReviewMentoringMessageShim
(
object
):
CATEGORY
=
'pb-message'
STUDIO_LABEL
=
_
(
"Message (Review)"
)
problem_builder/plot.py
View file @
cc9498cd
...
...
@@ -357,8 +357,8 @@ class PlotBlock(StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin
fragment
=
super
(
PlotBlock
,
self
)
.
author_edit_view
(
context
)
fragment
.
add_css_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/css/problem-builder-edit.css'
))
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/util.js'
))
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/
plot
_edit.js'
))
fragment
.
initialize_js
(
'P
lot
Edit'
)
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/
container
_edit.js'
))
fragment
.
initialize_js
(
'P
roblemBuilderContainer
Edit'
)
return
fragment
...
...
problem_builder/public/css/problem-builder-edit.css
View file @
cc9498cd
...
...
@@ -6,6 +6,13 @@
font-style
:
italic
;
}
.xblock
[
data-block-type
=
sb-step
]
.author-preview-view
,
.xblock
[
data-block-type
=
step-builder
]
.author-preview-view
,
.xblock
[
data-block-type
=
problem-builder
]
.author-preview-view
,
.xblock
[
data-block-type
=
mentoring
]
.author-preview-view
{
margin
:
10px
;
}
.xblock
[
data-block-type
=
sb-step
]
.url-name-footer
.url-name
,
.xblock
[
data-block-type
=
step-builder
]
.url-name-footer
.url-name
,
.xblock
[
data-block-type
=
problem-builder
]
.url-name-footer
.url-name
,
...
...
problem_builder/public/css/problem-builder.css
View file @
cc9498cd
...
...
@@ -234,6 +234,11 @@
position
:
relative
;
}
.assessment-question-block
div
[
data-block-type
=
sb-step
],
.assessment-question-block
div
[
data-block-type
=
sb-review-step
]
{
display
:
none
;
/* Hidden until revealed by JS */
}
.mentoring
.sb-step
.sb-step-message
{
position
:
absolute
;
top
:
50%
;
...
...
problem_builder/public/js/container_edit.js
0 → 100644
View file @
cc9498cd
function
ProblemBuilderContainerEdit
(
runtime
,
element
)
{
"use strict"
;
// Standard initialization for any Problem Builder / Step Builder container XBlocks
// that are instances of StudioContainerXBlockWithNestedXBlocksMixin
StudioContainerXBlockWithNestedXBlocksMixin
(
runtime
,
element
);
if
(
window
.
ProblemBuilderUtil
)
{
ProblemBuilderUtil
.
transformClarifications
(
element
);
}
}
problem_builder/public/js/mentoring_with_steps.js
View file @
cc9498cd
function
MentoringWithStepsBlock
(
runtime
,
element
)
{
function
MentoringWithStepsBlock
(
runtime
,
element
,
params
)
{
// Set up gettext in case it isn't available in the client runtime:
if
(
typeof
gettext
==
"undefined"
)
{
...
...
@@ -8,23 +8,22 @@ function MentoringWithStepsBlock(runtime, element) {
var
children
=
runtime
.
children
(
element
);
var
steps
=
[];
var
reviewStep
;
for
(
var
i
=
0
;
i
<
children
.
length
;
i
++
)
{
var
child
=
children
[
i
];
var
blockType
=
$
(
child
.
element
).
data
(
'block-type'
);
if
(
blockType
===
'sb-step'
)
{
steps
.
push
(
child
);
}
else
if
(
blockType
===
'sb-review-step'
)
{
reviewStep
=
child
;
}
}
var
activeStep
=
$
(
'.mentoring'
,
element
).
data
(
'active-step'
);
var
reviewTipsTemplate
=
_
.
template
(
$
(
'#xblock-review-tips-template'
).
html
());
// Tips about specific questions the user got wrong
var
attemptsTemplate
=
_
.
template
(
$
(
'#xblock-attempts-template'
).
html
());
var
message
=
$
(
'.sb-step-message'
,
element
);
var
checkmark
,
submitDOM
,
nextDOM
,
reviewDOM
,
tryAgainDOM
,
gradeDOM
,
attemptsDOM
,
reviewTipsDOM
,
reviewLinkDOM
,
submitXHR
;
var
checkmark
,
submitDOM
,
nextDOM
,
reviewButtonDOM
,
tryAgainDOM
,
gradeDOM
,
attemptsDOM
,
reviewLinkDOM
,
submitXHR
;
var
reviewStepDOM
=
$
(
"[data-block-type=sb-review-step]"
,
element
);
var
hasAReviewStep
=
reviewStepDOM
.
length
==
1
;
function
isLastStep
()
{
return
(
activeStep
===
steps
.
length
-
1
);
...
...
@@ -43,8 +42,7 @@ function MentoringWithStepsBlock(runtime, element) {
}
function
extendedFeedbackEnabled
()
{
var
data
=
gradeDOM
.
data
();
return
data
.
extended_feedback
===
"True"
;
return
!!
(
params
.
extended_feedback
);
// Show extended feedback when all attempts are used up?
}
function
showFeedback
(
response
)
{
...
...
@@ -61,22 +59,6 @@ function MentoringWithStepsBlock(runtime, element) {
}
}
function
updateGrade
(
grade_data
)
{
gradeDOM
.
data
(
'score'
,
grade_data
.
score
);
gradeDOM
.
data
(
'correct_answer'
,
grade_data
.
correct_answers
);
gradeDOM
.
data
(
'incorrect_answer'
,
grade_data
.
incorrect_answers
);
gradeDOM
.
data
(
'partially_correct_answer'
,
grade_data
.
partially_correct_answers
);
gradeDOM
.
data
(
'correct'
,
grade_data
.
correct
);
gradeDOM
.
data
(
'incorrect'
,
grade_data
.
incorrect
);
gradeDOM
.
data
(
'partial'
,
grade_data
.
partial
);
gradeDOM
.
data
(
'assessment_review_tips'
,
grade_data
.
assessment_review_tips
);
updateReviewStep
(
grade_data
);
}
function
updateReviewStep
(
response
)
{
reviewStep
.
updateAssessmentMessage
(
response
,
updateControls
);
}
function
updateControls
()
{
submitDOM
.
attr
(
'disabled'
,
'disabled'
);
...
...
@@ -84,8 +66,8 @@ function MentoringWithStepsBlock(runtime, element) {
if
(
nextDOM
.
is
(
':visible'
))
{
nextDOM
.
focus
();
}
if
(
atReviewStep
())
{
if
(
r
eviewStep
)
{
reviewDOM
.
removeAttr
(
'disabled'
);
if
(
hasAR
eviewStep
)
{
review
Button
DOM
.
removeAttr
(
'disabled'
);
}
else
{
if
(
someAttemptsLeft
())
{
tryAgainDOM
.
removeAttr
(
'disabled'
);
...
...
@@ -111,7 +93,8 @@ function MentoringWithStepsBlock(runtime, element) {
// We are now showing the review step / end
// Update the number of attempts.
attemptsDOM
.
data
(
'num_attempts'
,
response
.
num_attempts
);
updateGrade
(
response
.
grade_data
);
reviewStepDOM
.
html
(
$
(
response
.
review_html
).
html
());
updateControls
();
}
else
if
(
!
hasQuestion
)
{
// This was a step with no questions, so proceed to the next step / review:
updateDisplay
();
...
...
@@ -156,7 +139,6 @@ function MentoringWithStepsBlock(runtime, element) {
hideAllSteps
();
hideReviewStep
();
attemptsDOM
.
html
(
''
);
reviewTipsDOM
.
empty
().
hide
();
message
.
hide
();
}
...
...
@@ -186,54 +168,32 @@ function MentoringWithStepsBlock(runtime, element) {
}
else
{
nextDOM
.
removeAttr
(
'disabled'
);
}
if
(
isLastStep
()
&&
r
eviewStep
)
{
if
(
isLastStep
()
&&
hasAR
eviewStep
)
{
if
(
step
.
hasQuestion
())
{
reviewDOM
.
attr
(
'disabled'
,
'disabled'
);
review
Button
DOM
.
attr
(
'disabled'
,
'disabled'
);
}
else
{
reviewDOM
.
removeAttr
(
'disabled'
)
review
Button
DOM
.
removeAttr
(
'disabled'
)
}
reviewDOM
.
show
();
review
Button
DOM
.
show
();
}
}
}
function
showReviewStep
()
{
// Forward to review step to show assessment message
reviewStep
.
showAssessmentMessage
();
// Forward to review step to render grade data
var
showExtendedFeedback
=
(
!
someAttemptsLeft
()
&&
extendedFeedbackEnabled
());
reviewStep
.
renderGrade
(
gradeDOM
,
showExtendedFeedback
);
// Add click handler that takes care of showing associated step to step links
$
(
'a.step-link'
,
element
).
on
(
'click'
,
getStepToReview
);
if
(
someAttemptsLeft
())
{
tryAgainDOM
.
removeAttr
(
'disabled'
);
// Review tips
var
data
=
gradeDOM
.
data
();
if
(
data
.
assessment_review_tips
.
length
>
0
)
{
// on-assessment-review-question messages specific to questions the student got wrong:
reviewTipsDOM
.
html
(
reviewTipsTemplate
({
tips
:
data
.
assessment_review_tips
}));
reviewTipsDOM
.
show
();
}
}
submitDOM
.
hide
();
nextDOM
.
hide
();
reviewDOM
.
hide
();
review
Button
DOM
.
hide
();
tryAgainDOM
.
show
();
reviewStepDOM
.
show
();
}
function
hideReviewStep
()
{
if
(
reviewStep
)
{
reviewStep
.
hideAssessmentMessage
();
reviewStep
.
clearGrade
(
gradeDOM
);
}
reviewStepDOM
.
hide
()
}
function
getStepToReview
(
event
)
{
...
...
@@ -249,8 +209,8 @@ function MentoringWithStepsBlock(runtime, element) {
updateNextLabel
();
if
(
isLastStep
())
{
reviewDOM
.
show
();
reviewDOM
.
removeAttr
(
'disabled'
);
review
Button
DOM
.
show
();
review
Button
DOM
.
removeAttr
(
'disabled'
);
nextDOM
.
hide
();
nextDOM
.
attr
(
'disabled'
,
'disabled'
);
}
else
{
...
...
@@ -307,8 +267,8 @@ function MentoringWithStepsBlock(runtime, element) {
if
(
isLastStep
()
&&
step
.
hasQuestion
())
{
nextDOM
.
hide
();
}
else
if
(
isLastStep
())
{
reviewDOM
.
one
(
'click'
,
submit
);
reviewDOM
.
removeAttr
(
'disabled'
);
review
Button
DOM
.
one
(
'click'
,
submit
);
review
Button
DOM
.
removeAttr
(
'disabled'
);
nextDOM
.
hide
()
}
else
if
(
!
step
.
hasQuestion
())
{
nextDOM
.
one
(
'click'
,
submit
);
...
...
@@ -388,7 +348,7 @@ function MentoringWithStepsBlock(runtime, element) {
nextDOM
.
off
();
nextDOM
.
on
(
'click'
,
updateDisplay
);
nextDOM
.
show
();
reviewDOM
.
hide
();
review
Button
DOM
.
hide
();
}
}
...
...
@@ -434,7 +394,7 @@ function MentoringWithStepsBlock(runtime, element) {
hideAllSteps
();
// Initialize references to relevant DOM elements and set up event handlers
checkmark
=
$
(
'.
assessment
-checkmark'
,
element
);
checkmark
=
$
(
'.
step-overall
-checkmark'
,
element
);
submitDOM
=
$
(
element
).
find
(
'.submit .input-main'
);
submitDOM
.
on
(
'click'
,
submit
);
...
...
@@ -446,19 +406,21 @@ function MentoringWithStepsBlock(runtime, element) {
nextDOM
.
on
(
'click'
,
updateDisplay
);
}
reviewDOM
=
$
(
element
).
find
(
'.submit .input-review'
);
reviewDOM
.
on
(
'click'
,
showGrade
);
review
Button
DOM
=
$
(
element
).
find
(
'.submit .input-review'
);
review
Button
DOM
.
on
(
'click'
,
showGrade
);
tryAgainDOM
=
$
(
element
).
find
(
'.submit .input-try-again'
);
tryAgainDOM
.
on
(
'click'
,
tryAgain
);
gradeDOM
=
$
(
'.grade'
,
element
);
attemptsDOM
=
$
(
'.attempts'
,
element
);
reviewTipsDOM
=
$
(
'.assessment-review-tips'
,
element
);
reviewLinkDOM
=
$
(
element
).
find
(
'.review-link'
);
reviewLinkDOM
.
on
(
'click'
,
showGrade
);
// Add click handler that takes care of links to steps on the extended review:
$
(
'a.step-link'
,
element
).
on
(
'click'
,
getStepToReview
);
// Initialize individual steps
// (sets up click handlers for questions and makes sure answer data is up-to-date)
var
options
=
{
...
...
problem_builder/public/js/mentoring_with_steps_edit.js
deleted
100644 → 0
View file @
5b751841
function
MentoringWithStepsEdit
(
runtime
,
element
)
{
"use strict"
;
var
$buttons
=
$
(
'.add-xblock-component-button[data-category=sb-review-step]'
,
element
);
var
blockIsPresent
=
function
(
klass
)
{
return
$
(
'.xblock '
+
klass
).
length
>
0
;
};
var
updateButton
=
function
(
button
,
condition
)
{
button
.
toggleClass
(
'disabled'
,
condition
);
};
var
disableButton
=
function
(
ev
)
{
if
(
$
(
this
).
is
(
'.disabled'
))
{
ev
.
preventDefault
();
ev
.
stopPropagation
();
}
else
{
$
(
this
).
addClass
(
'disabled'
);
}
};
var
updateButtons
=
function
(
buttons
)
{
buttons
.
each
(
function
()
{
var
button
=
$
(
this
);
updateButton
(
button
,
blockIsPresent
(
'.xblock-header-sb-review-step'
));
});
};
var
initButtons
=
function
()
{
updateButtons
(
$buttons
);
$buttons
.
on
(
'click'
,
disableButton
);
};
var
resetButtons
=
function
()
{
var
$disabledButtons
=
$buttons
.
filter
(
'.disabled'
);
updateButtons
(
$disabledButtons
);
};
ProblemBuilderUtil
.
transformClarifications
(
element
);
initButtons
();
runtime
.
listenTo
(
'deleted-child'
,
resetButtons
);
}
problem_builder/public/js/plot_edit.js
deleted
100644 → 0
View file @
5b751841
function
PlotEdit
(
runtime
,
element
)
{
'use strict'
;
StudioContainerXBlockWithNestedXBlocksMixin
(
runtime
,
element
);
ProblemBuilderUtil
.
transformClarifications
(
element
);
}
problem_builder/public/js/review_step.js
deleted
100644 → 0
View file @
5b751841
function
ReviewStepBlock
(
runtime
,
element
)
{
var
gradeTemplate
=
_
.
template
(
$
(
'#xblock-feedback-template'
).
html
());
var
reviewStepsTemplate
=
_
.
template
(
$
(
'#xblock-step-links-template'
).
html
());
var
assessmentMessageDOM
=
$
(
'.assessment-message'
,
element
);
return
{
'showAssessmentMessage'
:
function
()
{
var
assessmentMessage
=
assessmentMessageDOM
.
data
(
'assessment_message'
);
assessmentMessageDOM
.
html
(
assessmentMessage
);
assessmentMessageDOM
.
show
();
},
'hideAssessmentMessage'
:
function
()
{
assessmentMessageDOM
.
html
(
''
);
assessmentMessageDOM
.
hide
();
},
'updateAssessmentMessage'
:
function
(
grade
,
callback
)
{
var
handlerUrl
=
runtime
.
handlerUrl
(
element
,
'get_assessment_message'
);
$
.
post
(
handlerUrl
,
JSON
.
stringify
(
grade
)).
success
(
function
(
response
)
{
assessmentMessageDOM
.
data
(
'assessment_message'
,
response
.
assessment_message
);
callback
();
});
},
'renderGrade'
:
function
(
gradeDOM
,
showExtendedFeedback
)
{
var
data
=
gradeDOM
.
data
();
_
.
extend
(
data
,
{
'runDetails'
:
function
(
correctness
)
{
if
(
!
showExtendedFeedback
)
{
return
''
;
}
var
self
=
this
;
return
reviewStepsTemplate
({
'questions'
:
self
[
correctness
],
'correctness'
:
correctness
});
}
});
gradeDOM
.
html
(
gradeTemplate
(
data
));
},
'clearGrade'
:
function
(
gradeDOM
)
{
gradeDOM
.
html
(
''
);
}
};
}
problem_builder/public/js/review_step_edit.js
deleted
100644 → 0
View file @
5b751841
function
ReviewStepEdit
(
runtime
,
element
)
{
"use strict"
;
var
$buttons
=
$
(
'.add-xblock-component-button[data-category=pb-message]'
,
element
);
var
blockIsPresent
=
function
(
klass
)
{
return
$
(
'.xblock '
+
klass
).
length
>
0
;
};
var
updateButton
=
function
(
button
,
condition
)
{
button
.
toggleClass
(
'disabled'
,
condition
);
};
var
disableButton
=
function
(
ev
)
{
if
(
$
(
this
).
is
(
'.disabled'
))
{
ev
.
preventDefault
();
ev
.
stopPropagation
();
}
else
{
$
(
this
).
addClass
(
'disabled'
);
}
};
var
updateButtons
=
function
(
buttons
)
{
buttons
.
each
(
function
()
{
var
button
=
$
(
this
);
var
msgType
=
button
.
data
(
'boilerplate'
);
updateButton
(
button
,
blockIsPresent
(
'.submission-message.'
+
msgType
));
});
};
var
initButtons
=
function
()
{
updateButtons
(
$buttons
);
$buttons
.
on
(
'click'
,
disableButton
);
};
var
resetButtons
=
function
()
{
var
$disabledButtons
=
$buttons
.
filter
(
'.disabled'
);
updateButtons
(
$disabledButtons
);
};
ProblemBuilderUtil
.
transformClarifications
(
element
);
initButtons
();
runtime
.
listenTo
(
'deleted-child'
,
resetButtons
);
}
problem_builder/public/js/step_edit.js
deleted
100644 → 0
View file @
5b751841
function
StepEdit
(
runtime
,
element
)
{
'use strict'
;
StudioContainerXBlockWithNestedXBlocksMixin
(
runtime
,
element
);
ProblemBuilderUtil
.
transformClarifications
(
element
);
}
problem_builder/step.py
View file @
cc9498cd
...
...
@@ -32,10 +32,7 @@ from xblockutils.studio_editable import (
from
problem_builder.answer
import
AnswerBlock
,
AnswerRecapBlock
from
problem_builder.mcq
import
MCQBlock
,
RatingBlock
from
.message
import
(
CompletedMentoringMessageShim
,
IncompleteMentoringMessageShim
,
OnReviewMentoringMessageShim
)
from
problem_builder.mixins
import
EnumerableChildMixin
,
MessageParentMixin
,
StepParentMixin
from
problem_builder.mixins
import
EnumerableChildMixin
,
StepParentMixin
from
problem_builder.mrq
import
MRQBlock
from
problem_builder.plot
import
PlotBlock
from
problem_builder.slider
import
SliderBlock
...
...
@@ -69,11 +66,6 @@ class Correctness(object):
INCORRECT
=
'incorrect'
class
HtmlBlockShim
(
object
):
CATEGORY
=
'html'
STUDIO_LABEL
=
_
(
u"HTML"
)
@XBlock.needs
(
'i18n'
)
class
MentoringStepBlock
(
StudioEditableXBlockMixin
,
StudioContainerWithNestedXBlocksMixin
,
XBlockWithPreviewMixin
,
...
...
@@ -152,7 +144,8 @@ class MentoringStepBlock(
return
[
NestedXBlockSpec
(
AnswerBlock
,
boilerplate
=
'studio_default'
),
MCQBlock
,
RatingBlock
,
MRQBlock
,
HtmlBlockShim
,
MCQBlock
,
RatingBlock
,
MRQBlock
,
NestedXBlockSpec
(
None
,
category
=
"html"
,
label
=
self
.
_
(
"HTML"
)),
AnswerRecapBlock
,
MentoringTableBlock
,
PlotBlock
,
SliderBlock
]
+
additional_blocks
...
...
@@ -229,8 +222,8 @@ class MentoringStepBlock(
fragment
.
add_css_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/css/problem-builder-edit.css'
))
fragment
.
add_css_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/css/problem-builder-tinymce-content.css'
))
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/util.js'
))
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/
step
_edit.js'
))
fragment
.
initialize_js
(
'
Step
Edit'
)
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/
container
_edit.js'
))
fragment
.
initialize_js
(
'
ProblemBuilderContainer
Edit'
)
return
fragment
def
mentoring_view
(
self
,
context
=
None
):
...
...
@@ -275,96 +268,3 @@ class MentoringStepBlock(
fragment
.
initialize_js
(
'MentoringStepBlock'
)
return
fragment
class
ReviewStepBlock
(
MessageParentMixin
,
StudioContainerWithNestedXBlocksMixin
,
XBlockWithPreviewMixin
,
XBlock
):
""" A dedicated step for reviewing results for a mentoring block """
CATEGORY
=
'sb-review-step'
STUDIO_LABEL
=
_
(
"Review Step"
)
display_name
=
String
(
default
=
"Review Step"
)
@property
def
allowed_nested_blocks
(
self
):
"""
Returns a list of allowed nested XBlocks. Each item can be either
* An XBlock class
* A NestedXBlockSpec
If XBlock class is used it is assumed that this XBlock is enabled and allows multiple instances.
NestedXBlockSpec allows explicitly setting disabled/enabled state,
disabled reason (if any) and single/multiple instances.
"""
return
[
NestedXBlockSpec
(
CompletedMentoringMessageShim
,
boilerplate
=
'completed'
),
NestedXBlockSpec
(
IncompleteMentoringMessageShim
,
boilerplate
=
'incomplete'
),
NestedXBlockSpec
(
OnReviewMentoringMessageShim
,
boilerplate
=
'on-review'
),
]
@XBlock.json_handler
def
get_assessment_message
(
self
,
grade
,
suffix
):
# Data passed as "grade" comes from "get_grade" handler of Step Builder (MentoringWithExplicitStepsBlock)
complete
=
grade
.
get
(
'complete'
)
max_attempts_reached
=
grade
.
get
(
'max_attempts_reached'
)
return
{
'assessment_message'
:
self
.
assessment_message
(
complete
,
max_attempts_reached
)
}
def
assessment_message
(
self
,
complete
=
None
,
max_attempts_reached
=
None
):
if
complete
is
None
and
max_attempts_reached
is
None
:
parent
=
self
.
get_parent
()
complete
=
parent
.
complete
max_attempts_reached
=
parent
.
max_attempts_reached
if
max_attempts_reached
:
assessment_message
=
self
.
get_message_content
(
'on-review'
,
or_default
=
True
)
else
:
if
complete
:
# All answers correct
assessment_message
=
self
.
get_message_content
(
'completed'
,
or_default
=
True
)
else
:
assessment_message
=
self
.
get_message_content
(
'incomplete'
,
or_default
=
True
)
return
assessment_message
def
mentoring_view
(
self
,
context
=
None
):
""" Mentoring View """
return
self
.
_render_view
(
context
)
def
student_view
(
self
,
context
=
None
):
""" Student View """
return
self
.
_render_view
(
context
)
def
studio_view
(
self
,
context
=
None
):
""" Studio View """
return
Fragment
(
u'<p>This is a preconfigured block. It is not editable.</p>'
)
def
_render_view
(
self
,
context
):
fragment
=
Fragment
()
fragment
.
add_content
(
loader
.
render_template
(
'templates/html/review_step.html'
,
{
'self'
:
self
,
}))
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/review_step.js'
))
fragment
.
initialize_js
(
'ReviewStepBlock'
)
return
fragment
def
author_preview_view
(
self
,
context
):
return
Fragment
(
u"<p>{}</p>"
.
format
(
_
(
u"This block summarizes a student's performance on the parent Step Builder block."
)
)
)
def
author_edit_view
(
self
,
context
):
"""
Add some HTML to the author view that allows authors to add child blocks.
"""
context
[
'wrap_children'
]
=
{
'head'
:
u'<div class="mentoring">'
,
'tail'
:
u'</div>'
}
fragment
=
super
(
ReviewStepBlock
,
self
)
.
author_edit_view
(
context
)
fragment
.
add_css_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/css/problem-builder-edit.css'
))
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/util.js'
))
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/review_step_edit.js'
))
fragment
.
initialize_js
(
'ReviewStepEdit'
)
return
fragment
problem_builder/step_review.py
0 → 100644
View file @
cc9498cd
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014-2015 Harvard, edX & OpenCraft
#
# This software's license gives you freedom; you can copy, convey,
# propagate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
import
logging
from
xblock.core
import
XBlock
from
xblock.fields
import
String
,
Scope
,
Integer
from
xblock.fragment
import
Fragment
from
xblockutils.resources
import
ResourceLoader
from
xblockutils.studio_editable
import
(
NestedXBlockSpec
,
StudioEditableXBlockMixin
,
StudioContainerWithNestedXBlocksMixin
,
XBlockWithPreviewMixin
)
from
.mixins
import
XBlockWithTranslationServiceMixin
log
=
logging
.
getLogger
(
__name__
)
loader
=
ResourceLoader
(
__name__
)
# Make '_' a no-op so we can scrape strings
def
_
(
text
):
return
text
@XBlock.needs
(
"i18n"
)
class
ConditionalMessageBlock
(
StudioEditableXBlockMixin
,
XBlockWithTranslationServiceMixin
,
XBlockWithPreviewMixin
,
XBlock
):
"""
A message shown as part of a Step Builder review step, but only under certain conditions.
"""
CATEGORY
=
'sb-conditional-message'
STUDIO_LABEL
=
_
(
"Conditional Message"
)
content
=
String
(
display_name
=
_
(
"Message"
),
help
=
_
(
"Message to display upon completion"
),
scope
=
Scope
.
content
,
default
=
""
,
multiline_editor
=
"html"
,
resettable_editor
=
False
,
)
SCORE_PERFECT
,
SCORE_IMPERFECT
,
SCORE_ANY
=
1
,
2
,
0
SCORE_CONDITIONS_DESCRIPTIONS
=
{
SCORE_PERFECT
:
_
(
"Show only if student got a perfect score"
),
SCORE_IMPERFECT
:
_
(
"Show only if student got at least one question wrong"
),
SCORE_ANY
:
_
(
"Show for any score"
),
}
score_condition
=
Integer
(
display_name
=
_
(
"Score condition"
),
default
=
SCORE_ANY
,
values
=
[{
"display_name"
:
val
,
"value"
:
key
}
for
key
,
val
in
SCORE_CONDITIONS_DESCRIPTIONS
.
items
()],
)
IF_ATTEMPTS_REMAIN
,
IF_NO_ATTEMPTS_REMAIN
,
ATTEMPTS_ANY
=
1
,
2
,
0
NUM_ATTEMPTS_COND_DESCRIPTIONS
=
{
IF_ATTEMPTS_REMAIN
:
_
(
"Show only if student can try again"
),
IF_NO_ATTEMPTS_REMAIN
:
_
(
"Show only if student has used up all attempts"
),
ATTEMPTS_ANY
:
_
(
"Show whether student can try again or not"
),
}
num_attempts_condition
=
Integer
(
display_name
=
_
(
"Try again condition"
),
default
=
ATTEMPTS_ANY
,
values
=
[{
"display_name"
:
val
,
"value"
:
key
}
for
key
,
val
in
NUM_ATTEMPTS_COND_DESCRIPTIONS
.
items
()],
)
editable_fields
=
(
'content'
,
'score_condition'
,
'num_attempts_condition'
)
has_author_view
=
True
# Without this flag, studio will use student_view on newly-added blocks :/
@property
def
display_name_with_default
(
self
):
return
self
.
_
(
self
.
STUDIO_LABEL
)
def
is_applicable
(
self
,
context
):
""" Return true if this block should appear in the review step, false otherwise """
score_summary
=
context
[
'score_summary'
]
attempts_remain
=
not
score_summary
[
'max_attempts_reached'
]
if
(
(
self
.
num_attempts_condition
==
self
.
IF_ATTEMPTS_REMAIN
and
not
attempts_remain
)
or
(
self
.
num_attempts_condition
==
self
.
IF_NO_ATTEMPTS_REMAIN
and
attempts_remain
)
):
return
False
perfect_score
=
(
score_summary
[
'incorrect'
]
==
0
and
score_summary
[
'partial'
]
==
0
)
if
(
(
self
.
score_condition
==
self
.
SCORE_PERFECT
and
not
perfect_score
)
or
(
self
.
score_condition
==
self
.
SCORE_IMPERFECT
and
perfect_score
)
):
return
False
return
True
def
student_view
(
self
,
context
=
None
):
""" Render this message. """
html
=
u'<div class="review-conditional-message">{content}</div>'
.
format
(
content
=
self
.
content
)
return
Fragment
(
html
)
preview_view
=
student_view
mentoring_view
=
student_view
# Same as student_view but Studio won't wrap it with the editing header/buttons
def
author_view
(
self
,
context
=
None
):
fragment
=
self
.
student_view
(
context
)
desc
=
""
if
self
.
num_attempts_condition
==
self
.
ATTEMPTS_ANY
and
self
.
score_condition
==
self
.
SCORE_ANY
:
desc
=
self
.
_
(
"Always shown"
)
else
:
if
self
.
score_condition
!=
self
.
SCORE_ANY
:
desc
+=
self
.
SCORE_CONDITIONS_DESCRIPTIONS
[
self
.
score_condition
]
+
"<br>"
if
self
.
num_attempts_condition
!=
self
.
ATTEMPTS_ANY
:
desc
+=
self
.
NUM_ATTEMPTS_COND_DESCRIPTIONS
[
self
.
num_attempts_condition
]
fragment
.
content
+=
u'<div class="submission-message-help"><p>{}</p></div>'
.
format
(
desc
)
return
fragment
@XBlock.needs
(
"i18n"
)
class
ScoreSummaryBlock
(
XBlockWithTranslationServiceMixin
,
XBlockWithPreviewMixin
,
XBlock
):
"""
Summaryize the score that the student earned.
"""
CATEGORY
=
'sb-review-score'
STUDIO_LABEL
=
_
(
"Score Summary"
)
has_author_view
=
True
# Without this flag, studio will use student_view on newly-added blocks :/
@property
def
display_name_with_default
(
self
):
return
self
.
_
(
self
.
STUDIO_LABEL
)
def
student_view
(
self
,
context
=
None
):
""" Render the score summary message. """
html
=
loader
.
render_template
(
"templates/html/sb-review-score.html"
,
context
.
get
(
"score_summary"
,
{}))
return
Fragment
(
html
)
mentoring_view
=
student_view
# Same as student_view but Studio won't wrap it with the editing header/buttons
def
author_view
(
self
,
context
=
None
):
if
not
context
.
get
(
"score_summary"
):
context
[
"score_summary"
]
=
{
'score'
:
75
,
'correct_answers'
:
3
,
'incorrect_answers'
:
1
,
'partially_correct_answers'
:
0
,
'correct'
:
[],
'incorrect'
:
[],
'partial'
:
[],
'complete'
:
True
,
'max_attempts_reached'
:
False
,
'is_example'
:
True
,
}
return
self
.
student_view
(
context
)
@XBlock.needs
(
"i18n"
)
class
PerQuestionFeedbackBlock
(
XBlockWithTranslationServiceMixin
,
XBlockWithPreviewMixin
,
XBlock
):
"""
Summaryize the score that the student earned.
"""
CATEGORY
=
'sb-review-per-question-feedback'
STUDIO_LABEL
=
_
(
"Per-Question Feedback"
)
has_author_view
=
True
# Without this flag, studio will use student_view on newly-added blocks :/
@property
def
display_name_with_default
(
self
):
return
self
.
_
(
self
.
STUDIO_LABEL
)
def
student_view
(
self
,
context
=
None
):
""" Render the per-question feedback, if any. """
review_tips
=
(
context
or
{})
.
get
(
"score_summary"
,
{})
.
get
(
"review_tips"
)
if
review_tips
:
html
=
loader
.
render_template
(
"templates/html/sb-review-per-question-feedback.html"
,
{
'tips'
:
review_tips
,
})
else
:
html
=
u""
return
Fragment
(
html
)
mentoring_view
=
student_view
# Same as student_view but Studio won't wrap it with the editing header/buttons
def
author_view
(
self
,
context
=
None
):
""" Show example content in Studio """
if
not
context
.
get
(
"per_question_review_tips"
):
example
=
self
.
_
(
"(Example tip:) Since you got Question 1 wrong, review Chapter 12 of your textbook."
)
context
[
"score_summary"
]
=
{
"review_tips"
:
[
example
]}
return
self
.
student_view
(
context
)
@XBlock.needs
(
"i18n"
)
class
ReviewStepBlock
(
StudioContainerWithNestedXBlocksMixin
,
XBlockWithTranslationServiceMixin
,
XBlockWithPreviewMixin
,
XBlock
):
"""
A dedicated step for reviewing results as the last step of a Step Builder sequence.
"""
CATEGORY
=
'sb-review-step'
STUDIO_LABEL
=
_
(
"Review Step"
)
display_name
=
String
(
default
=
"Review Step"
)
@property
def
allowed_nested_blocks
(
self
):
"""
Returns a list of allowed nested XBlocks. Each item can be either
* An XBlock class
* A NestedXBlockSpec
If XBlock class is used it is assumed that this XBlock is enabled and allows multiple instances.
NestedXBlockSpec allows explicitly setting disabled/enabled state,
disabled reason (if any) and single/multiple instances.
"""
return
[
ConditionalMessageBlock
,
NestedXBlockSpec
(
None
,
category
=
'html'
,
label
=
self
.
_
(
"HTML"
)),
NestedXBlockSpec
(
ScoreSummaryBlock
,
single_instance
=
True
),
NestedXBlockSpec
(
PerQuestionFeedbackBlock
,
single_instance
=
True
),
]
def
student_view
(
self
,
context
=
None
):
"""
Normal view of the review step.
The parent Step Builder block should pass in appropriate context information:
- score_summary
"""
context
=
context
.
copy
()
if
context
else
{}
fragment
=
Fragment
()
if
"score_summary"
not
in
context
:
fragment
.
add_content
(
u"Error: This block only works inside a Step Builder block."
)
elif
not
context
[
"score_summary"
]:
# Note: The following text should never be seen (in theory) so does not need to be translated.
fragment
.
add_content
(
u"Your score and review messages will appear here."
)
else
:
for
child_id
in
self
.
children
:
child
=
self
.
runtime
.
get_block
(
child_id
)
if
child
is
None
:
# child should not be None but it can happen due to bugs or permission issues
fragment
.
add_content
(
u"<p>[{}]</p>"
.
format
(
u"Error: Unable to load child component."
))
else
:
if
hasattr
(
child
,
'is_applicable'
):
if
not
child
.
is_applicable
(
context
):
continue
# Hide conditional messages that don't meet their criteria
context
[
"is_pages_view"
]
=
True
# This is a hack so Studio doesn't wrap our component blocks.
child_fragment
=
child
.
render
(
'student_view'
,
context
)
fragment
.
add_frag_resources
(
child_fragment
)
fragment
.
add_content
(
child_fragment
.
content
)
return
fragment
mentoring_view
=
student_view
def
studio_view
(
self
,
context
=
None
):
""" Studio View """
return
Fragment
(
u'<p>{}</p>'
.
format
(
self
.
_
(
"This XBlock does not have any settings."
)))
def
author_edit_view
(
self
,
context
):
"""
Add some HTML to the author view that allows authors to add child blocks.
"""
context
[
'wrap_children'
]
=
{
'head'
:
u'<div class="mentoring">'
,
'tail'
:
u'</div>'
}
fragment
=
super
(
ReviewStepBlock
,
self
)
.
author_edit_view
(
context
)
fragment
.
add_css_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/css/problem-builder.css'
))
fragment
.
add_css_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/css/problem-builder-edit.css'
))
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/util.js'
))
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/container_edit.js'
))
fragment
.
initialize_js
(
'ProblemBuilderContainerEdit'
)
return
fragment
problem_builder/templates/html/mentoring_review_templates.html
deleted
100644 → 0
View file @
5b751841
<!-- Tips about specific questions the student got wrong. From pb-message[type=on-assessment-review-question] blocks -->
<script
type=
"text/template"
id=
"xblock-review-tips-template"
>
<
p
class
=
"review-tips-intro"
><%=
gettext
(
"You might consider reviewing the following items before your next assessment attempt:"
)
%><
/p
>
<
ul
class
=
"review-tips-list"
>
<%
for
(
var
tip_idx
in
tips
)
{{
%>
<
li
><%=
tips
[
tip_idx
]
%><
/li
>
<%
}}
%>
<
/ul
>
</script>
problem_builder/templates/html/mentoring_with_steps.html
View file @
cc9498cd
...
...
@@ -13,20 +13,8 @@
{{ child_content|safe }}
{% endfor %}
<div
class=
"grade"
data-score=
"{{ self.score.percentage }}"
data-correct_answer=
"{{ self.score.correct|length }}"
data-incorrect_answer=
"{{ self.score.incorrect|length }}"
data-partially_correct_answer=
"{{ self.score.partially_correct|length }}"
data-assessment_review_tips=
"{{ self.review_tips_json }}"
data-extended_feedback=
"{{ self.extended_feedback }}"
data-correct=
"{{ self.correct_json }}"
data-incorrect=
"{{ self.incorrect_json }}"
data-partial=
"{{ self.partial_json }}"
>
</div>
<div
class=
"submit"
>
<span
class=
"
assessment
-checkmark fa icon-2x"
></span>
<span
class=
"
step-overall
-checkmark fa icon-2x"
></span>
<input
type=
"button"
class=
"input-main"
value=
"Submit"
disabled=
"disabled"
/>
<input
type=
"button"
class=
"input-next"
value=
"Next Step"
disabled=
"disabled"
/>
<input
type=
"button"
class=
"input-review"
value=
"Review grade"
disabled=
"disabled"
/>
...
...
@@ -35,11 +23,7 @@
<div
class=
"attempts"
data-max_attempts=
"{{ self.max_attempts }}"
data-num_attempts=
"{{ self.num_attempts }}"
>
</div>
</div>
<div
class=
"assessment-review-tips"
></div>
</div>
<div
class=
"review-link"
><a
href=
"#"
>
Review final grade
</a></div>
...
...
problem_builder/templates/html/review_step.html
deleted
100644 → 0
View file @
5b751841
<div
class=
"sb-review-step"
>
<div
class=
"assessment-message"
data-assessment_message=
"{{ self.assessment_message }}"
></div>
<script
type=
"text/template"
id=
"xblock-feedback-template"
>
<
div
class
=
"grade-result"
>
<
h2
>
<%=
_
.
template
(
gettext
(
"You scored {percent}% on this assessment."
),
{
percent
:
score
},
{
interpolate
:
/
\{(
.+
?)\}
/g
})
%>
<
/h2
>
<
hr
/>
<
span
class
=
"assessment-checkmark icon-2x checkmark-correct icon-ok fa fa-check"
><
/span
>
<
div
class
=
"results-section"
>
<
p
>
<%=
_
.
template
(
ngettext
(
"You answered 1 question correctly."
,
"You answered {number_correct} questions correctly."
,
correct_answer
),
{
number_correct
:
correct_answer
},
{
interpolate
:
/
\{(
.+
?)\}
/g
})
%>
<
/p
>
<%=
runDetails
(
'correct'
)
%>
<
/div
>
<
div
class
=
"clear"
><
/div
>
<
span
class
=
"assessment-checkmark icon-2x checkmark-partially-correct icon-ok fa fa-check"
><
/span
>
<
div
class
=
"results-section"
>
<
p
>
<%=
_
.
template
(
ngettext
(
"You answered 1 question partially correctly."
,
"You answered {number_partially_correct} questions partially correctly."
,
partially_correct_answer
),
{
number_partially_correct
:
partially_correct_answer
},
{
interpolate
:
/
\{(
.+
?)\}
/g
})
%>
<
/p
>
<%=
runDetails
(
'partial'
)
%>
<
/div
>
<
div
class
=
"clear"
><
/div
>
<
span
class
=
"assessment-checkmark icon-2x checkmark-incorrect icon-exclamation fa fa-exclamation"
><
/span
>
<
div
class
=
"results-section"
>
<
p
>
<%=
_
.
template
(
ngettext
(
"You answered 1 question incorrectly."
,
"You answered {number_incorrect} questions incorrectly."
,
incorrect_answer
),
{
number_incorrect
:
incorrect_answer
},
{
interpolate
:
/
\{(
.+
?)\}
/g
})
%>
<
/p
>
<%=
runDetails
(
'incorrect'
)
%>
<
/div
>
<
div
class
=
"clear"
><
/div
>
<
hr
/>
<
/div
>
</script>
<!-- Template for extended feedback: Show extended feedback details when all attempts are used up. -->
<script
type=
"text/template"
id=
"xblock-step-links-template"
>
<
ul
class
=
"review-list <%= correctness %>-list"
>
<%
for
(
var
question
in
questions
)
{
%>
<%
var
q
=
questions
[
question
];
var
last_question
=
question
==
questions
.
length
-
1
;
var
second_last_question
=
question
==
questions
.
length
-
2
;
%>
<
li
>
<
a
href
=
"#"
class
=
"step-link"
data
-
step
=
"<%= q.step %>"
><%=
_
.
template
(
gettext
(
"Question {number}"
),
{
number
:
q
.
number
},
{
interpolate
:
/
\{(
.+
?)\}
/g
})
%><
/a><% if
(
!last_question
)
{ %><%=
(
questions.length > 2
?
", " : ""
)
%><%=
(
second_last_question
?
" " + gettext
(
"and"
)
: ""
)
%><% } %
>
<
/li
>
<%
}
%>
<
/ul
>
</script>
</div>
problem_builder/templates/html/sb-review-per-question-feedback.html
0 → 100644
View file @
cc9498cd
{% load i18n %}
<!-- Tips about specific questions the student got wrong. From pb-message[type=on-assessment-review-question] blocks -->
<p
class=
"review-tips-intro"
>
{% trans "You might consider reviewing the following items before your next assessment attempt:" %}
</p>
<ul
class=
"review-tips-list"
>
{% for tip in tips %}
<li>
{{tip}}
</li>
{% endfor %}
</ul>
problem_builder/templates/html/sb-review-score.html
0 → 100644
View file @
cc9498cd
{% load i18n %}
<div
class=
"sb-review-score"
>
<div
class=
"grade-result"
>
<h2>
{% blocktrans %}You scored {{score}}% on this assessment. {% endblocktrans %}
</h2>
{% if is_example %}
<p><em>
{% trans "Note: This is an example score, to show how the review step will look." %}
</em></p>
{% endif %}
<hr/>
<span
class=
"assessment-checkmark icon-2x checkmark-correct icon-ok fa fa-check"
></span>
<div
class=
"results-section"
>
<p>
{% blocktrans count correct_answers=correct_answers %}
You answered 1 question correctly.
{% plural %}
You answered {{correct_answers}} questions correctly.
{% endblocktrans %}
</p>
{% if show_extended_review %}
<ul
class=
"review-list correct-list"
>
{% for question in correct %}
<li>
{% if forloop.last and not forloop.first %} {% trans "and" %} {% endif %}
<a
href=
"#"
class=
"step-link"
data-step=
"{{ question.step }}"
>
{% blocktrans %}Question {{question.number}}{% endblocktrans %}
</a>
{% if forloop.revcounter > 1 and correct|length > 2 %},{%endif%}
</li>
{% endfor %}
</ul>
{% endif %}
</div>
<div
class=
"clear"
></div>
<span
class=
"assessment-checkmark icon-2x checkmark-partially-correct icon-ok fa fa-check"
></span>
<div
class=
"results-section"
>
<p>
{% blocktrans count partially_correct_answers=partially_correct_answers %}
You answered 1 question partially correctly.
{% plural %}
You answered {{partially_correct_answers}} questions partially correctly.
{% endblocktrans %}
</p>
{% if show_extended_review %}
<ul
class=
"review-list partial-list"
>
{% for question in partial %}
<li>
{% if forloop.last and not forloop.first %} {% trans "and" %} {% endif %}
<a
href=
"#"
class=
"step-link"
data-step=
"{{ question.step }}"
>
{% blocktrans %}Question {{question.number}}{% endblocktrans %}
</a>
{% if forloop.revcounter > 1 and partial|length > 2 %},{%endif%}
</li>
{% endfor %}
</ul>
{% endif %}
</div>
<div
class=
"clear"
></div>
<span
class=
"assessment-checkmark icon-2x checkmark-incorrect icon-exclamation fa fa-exclamation"
></span>
<div
class=
"results-section"
>
<p>
{% blocktrans count incorrect_answers=incorrect_answers %}
You answered 1 question incorrectly.
{% plural %}
You answered {{incorrect_answers}} questions incorrectly.
{% endblocktrans %}
</p>
{% if show_extended_review %}
<ul
class=
"review-list incorrect-list"
>
{% for question in incorrect %}
<li>
{% if forloop.last and not forloop.first %} {% trans "and" %} {% endif %}
<a
href=
"#"
class=
"step-link"
data-step=
"{{ question.step }}"
>
{% blocktrans %}Question {{question.number}}{% endblocktrans %}
</a>
{% if forloop.revcounter > 1 and incorrect|length > 2 %},{%endif%}
</li>
{% endfor %}
</ul>
{% endif %}
</div>
<div
class=
"clear"
></div>
<hr/>
</div>
</div>
setup.py
View file @
cc9498cd
...
...
@@ -43,7 +43,10 @@ BLOCKS = [
'problem-builder = problem_builder.mentoring:MentoringBlock'
,
'step-builder = problem_builder.mentoring:MentoringWithExplicitStepsBlock'
,
'sb-step = problem_builder.step:MentoringStepBlock'
,
'sb-review-step = problem_builder.step:ReviewStepBlock'
,
'sb-review-step = problem_builder.step_review:ReviewStepBlock'
,
'sb-conditional-message = problem_builder.step_review:ConditionalMessageBlock'
,
'sb-review-score = problem_builder.step_review:ScoreSummaryBlock'
,
'sb-review-per-question-feedback = problem_builder.step_review:PerQuestionFeedbackBlock'
,
'sb-plot = problem_builder.plot:PlotBlock'
,
'sb-plot-overlay = problem_builder.plot:PlotOverlayBlock'
,
...
...
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