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
990ba628
Commit
990ba628
authored
Apr 09, 2015
by
Jonathan Piacenti
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add scaffolding for displaying list of questions in extended feedback.
parent
7f0d3def
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
168 additions
and
37 deletions
+168
-37
problem_builder/mentoring.py
+105
-7
problem_builder/public/js/mentoring_assessment_view.js
+16
-0
problem_builder/templates/html/mentoring.html
+3
-3
problem_builder/templates/html/mentoring_grade.html
+36
-27
problem_builder/templates/html/mentoring_review_questions.html
+8
-0
No files found.
problem_builder/mentoring.py
View file @
990ba628
...
@@ -61,6 +61,10 @@ def _(text):
...
@@ -61,6 +61,10 @@ def _(text):
Score
=
namedtuple
(
"Score"
,
[
"raw"
,
"percentage"
,
"correct"
,
"incorrect"
,
"partially_correct"
])
Score
=
namedtuple
(
"Score"
,
[
"raw"
,
"percentage"
,
"correct"
,
"incorrect"
,
"partially_correct"
])
CORRECT
=
'correct'
INCORRECT
=
'incorrect'
PARTIAL
=
'partial'
@XBlock.needs
(
"i18n"
)
@XBlock.needs
(
"i18n"
)
@XBlock.wants
(
'settings'
)
@XBlock.wants
(
'settings'
)
...
@@ -160,6 +164,11 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
...
@@ -160,6 +164,11 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
default
=
[],
default
=
[],
scope
=
Scope
.
user_state
scope
=
Scope
.
user_state
)
)
extended_feedback
=
Boolean
(
help
=
_
(
"Show extended feedback details when all attempts are used up."
),
default
=
False
,
Scope
=
Scope
.
content
)
# Global user state
# Global user state
next_step
=
String
(
next_step
=
String
(
...
@@ -201,17 +210,39 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
...
@@ -201,17 +210,39 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
return
xblock_settings
[
self
.
theme_key
]
return
xblock_settings
[
self
.
theme_key
]
return
_default_theme_config
return
_default_theme_config
def
get_question_number
(
self
,
question_id
):
"""
Get the step number of the question id
"""
for
child_id
in
self
.
children
:
question
=
self
.
runtime
.
get_block
(
child_id
)
if
isinstance
(
question
,
StepMixin
)
and
(
question
.
name
==
question_id
):
return
question
.
step_number
raise
ValueError
(
"Question ID in answer set not a step of this Mentoring Block!"
)
def
answer_mapper
(
self
,
answer_status
):
"""
Create a JSON-dumpable object with readable key names from a list of student answers.
"""
return
[
{
'number'
:
self
.
get_question_number
(
answer
[
0
]),
'id'
:
answer
[
0
],
'details'
:
answer
[
1
],
}
for
answer
in
self
.
student_results
if
answer
[
1
][
'status'
]
==
answer_status
]
@property
@property
def
score
(
self
):
def
score
(
self
):
"""Compute the student score taking into account the weight of each step."""
"""Compute the student score taking into account the weight of each step."""
weights
=
(
float
(
self
.
runtime
.
get_block
(
step_id
)
.
weight
)
for
step_id
in
self
.
steps
)
weights
=
(
float
(
self
.
runtime
.
get_block
(
step_id
)
.
weight
)
for
step_id
in
self
.
steps
)
total_child_weight
=
sum
(
weights
)
total_child_weight
=
sum
(
weights
)
if
total_child_weight
==
0
:
if
total_child_weight
==
0
:
return
Score
(
0
,
0
,
0
,
0
,
0
)
return
Score
(
0
,
0
,
[],
[],
[]
)
score
=
sum
(
r
[
1
][
'score'
]
*
r
[
1
][
'weight'
]
for
r
in
self
.
student_results
)
/
total_child_weight
score
=
sum
(
r
[
1
][
'score'
]
*
r
[
1
][
'weight'
]
for
r
in
self
.
student_results
)
/
total_child_weight
correct
=
s
um
(
1
for
r
in
self
.
student_results
if
r
[
1
][
'status'
]
==
'correct'
)
correct
=
s
elf
.
answer_mapper
(
CORRECT
)
incorrect
=
s
um
(
1
for
r
in
self
.
student_results
if
r
[
1
][
'status'
]
==
'incorrect'
)
incorrect
=
s
elf
.
answer_mapper
(
INCORRECT
)
partially_correct
=
s
um
(
1
for
r
in
self
.
student_results
if
r
[
1
][
'status'
]
==
'partial'
)
partially_correct
=
s
elf
.
answer_mapper
(
PARTIAL
)
return
Score
(
score
,
int
(
round
(
score
*
100
)),
correct
,
incorrect
,
partially_correct
)
return
Score
(
score
,
int
(
round
(
score
*
100
)),
correct
,
incorrect
,
partially_correct
)
...
@@ -259,6 +290,7 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
...
@@ -259,6 +290,7 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/mentoring.js'
))
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/mentoring.js'
))
fragment
.
add_resource
(
loader
.
load_unicode
(
'templates/html/mentoring_attempts.html'
),
"text/html"
)
fragment
.
add_resource
(
loader
.
load_unicode
(
'templates/html/mentoring_attempts.html'
),
"text/html"
)
fragment
.
add_resource
(
loader
.
load_unicode
(
'templates/html/mentoring_grade.html'
),
"text/html"
)
fragment
.
add_resource
(
loader
.
load_unicode
(
'templates/html/mentoring_grade.html'
),
"text/html"
)
fragment
.
add_resource
(
loader
.
load_unicode
(
'templates/html/mentoring_review_questions.html'
),
"text/html"
)
self
.
include_theme_files
(
fragment
)
self
.
include_theme_files
(
fragment
)
# Workbench doesn't have font awesome, so add it:
# Workbench doesn't have font awesome, so add it:
...
@@ -350,6 +382,72 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
...
@@ -350,6 +382,72 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
else
:
else
:
return
None
return
None
def
show_extended_feedback
(
self
):
return
self
.
extended_feedback
and
self
.
max_attempts_reached
def
feedback_dispatch
(
self
,
target_data
,
stringify
):
if
self
.
show_extended_feedback
():
if
stringify
:
return
json
.
dumps
(
target_data
)
else
:
return
target_data
def
correct_json
(
self
,
stringify
=
True
):
return
self
.
feedback_dispatch
(
self
.
score
.
correct
,
stringify
)
def
incorrect_json
(
self
,
stringify
=
True
):
return
self
.
feedback_dispatch
(
self
.
score
.
incorrect
,
stringify
)
def
partial_json
(
self
,
stringify
=
True
):
return
self
.
feedback_dispatch
(
self
.
score
.
partially_correct
,
stringify
)
@XBlock.json_handler
def
get_results
(
self
,
queries
,
suffix
=
''
):
"""
Gets detailed results in the case of extended feedback.
It may be a good idea to eventually have this function get results
in the general case instead of loading them in the template in the future,
and only using it for extended feedback situations.
Right now there are two ways to get results-- through the template upon loading up
the mentoring block, or after submission of an AJAX request like in
submit or get_results here.
"""
results
=
[]
if
not
self
.
show_extended_feedback
():
return
{
'results'
:
[],
'error'
:
'Extended feedback results cannot be obtained.'
}
completed
=
True
choices
=
dict
(
self
.
student_results
)
step
=
self
.
step
# Only one child should ever be of concern with this method.
for
child_id
in
self
.
steps
:
child
=
self
.
runtime
.
get_block
(
child_id
)
if
child
.
name
and
child
.
name
in
queries
:
results
=
[
child
.
name
,
child
.
get_results
(
choices
[
child
.
name
])]
# Children may have their own definition of 'completed' which can vary from the general case
# of the whole mentoring block being completed. This is because in standard mode, all children
# must be correct to complete the block. In assessment mode with extended feedback, completion
# happens when you're out of attempts, no matter how you did.
completed
=
choices
[
child
.
name
][
'status'
]
==
'correct'
break
# The 'completed' message should always be shown in this case, since no more attempts are available.
message
=
self
.
get_message
(
True
)
return
{
'results'
:
results
,
'completed'
:
completed
,
'attempted'
:
self
.
attempted
,
'message'
:
message
,
'step'
:
step
,
'max_attempts'
:
self
.
max_attempts
,
'num_attempts'
:
self
.
num_attempts
,
}
@XBlock.json_handler
@XBlock.json_handler
def
submit
(
self
,
submissions
,
suffix
=
''
):
def
submit
(
self
,
submissions
,
suffix
=
''
):
log
.
info
(
u'Received submissions: {}'
.
format
(
submissions
))
log
.
info
(
u'Received submissions: {}'
.
format
(
submissions
))
...
@@ -480,9 +578,9 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
...
@@ -480,9 +578,9 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
'num_attempts'
:
self
.
num_attempts
,
'num_attempts'
:
self
.
num_attempts
,
'step'
:
self
.
step
,
'step'
:
self
.
step
,
'score'
:
score
.
percentage
,
'score'
:
score
.
percentage
,
'correct_answer'
:
score
.
correct
,
'correct_answer'
:
len
(
score
.
correct
)
,
'incorrect_answer'
:
score
.
incorrect
,
'incorrect_answer'
:
len
(
score
.
incorrect
)
,
'partially_correct_answer'
:
score
.
partially_correct
,
'partially_correct_answer'
:
len
(
score
.
partially_correct
)
,
'assessment_message'
:
assessment_message
,
'assessment_message'
:
assessment_message
,
}
}
...
...
problem_builder/public/js/mentoring_assessment_view.js
View file @
990ba628
function
MentoringAssessmentView
(
runtime
,
element
,
mentoring
)
{
function
MentoringAssessmentView
(
runtime
,
element
,
mentoring
)
{
var
gradeTemplate
=
_
.
template
(
$
(
'#xblock-grade-template'
).
html
());
var
gradeTemplate
=
_
.
template
(
$
(
'#xblock-grade-template'
).
html
());
var
reviewQuestionsTemplate
=
_
.
template
(
$
(
'#xblock-review-questions-template'
).
html
());
var
submitDOM
,
nextDOM
,
reviewDOM
,
tryAgainDOM
,
messagesDOM
;
var
submitDOM
,
nextDOM
,
reviewDOM
,
tryAgainDOM
,
messagesDOM
;
var
submitXHR
;
var
submitXHR
;
var
checkmark
;
var
checkmark
;
...
@@ -24,9 +25,24 @@ function MentoringAssessmentView(runtime, element, mentoring) {
...
@@ -24,9 +25,24 @@ function MentoringAssessmentView(runtime, element, mentoring) {
messagesDOM
.
empty
().
hide
();
messagesDOM
.
empty
().
hide
();
}
}
function
no_more_attempts
()
{
var
attempts_data
=
$
(
'.attempts'
,
element
).
data
();
return
attempts_data
.
num_attempts
>=
attempts_data
.
max_attempts
;
}
function
renderGrade
()
{
function
renderGrade
()
{
notify
(
'navigation'
,
{
state
:
'unlock'
})
notify
(
'navigation'
,
{
state
:
'unlock'
})
var
data
=
$
(
'.grade'
,
element
).
data
();
var
data
=
$
(
'.grade'
,
element
).
data
();
_
.
extend
(
data
,
{
'enable_extended'
:
(
no_more_attempts
()
&&
data
.
extended_feedback
),
'runDetails'
:
function
(
label
)
{
if
(
!
this
.
enable_extended
)
{
return
'.'
}
var
self
=
this
;
return
reviewQuestionsTemplate
({
'questions'
:
self
[
label
],
'label'
:
label
})
}
});
cleanAll
();
cleanAll
();
$
(
'.grade'
,
element
).
html
(
gradeTemplate
(
data
));
$
(
'.grade'
,
element
).
html
(
gradeTemplate
(
data
));
reviewDOM
.
hide
();
reviewDOM
.
hide
();
...
...
problem_builder/templates/html/mentoring.html
View file @
990ba628
...
@@ -21,9 +21,9 @@
...
@@ -21,9 +21,9 @@
{% if self.display_submit %}
{% if self.display_submit %}
<div
class=
"grade"
data-score=
"{{ self.score.1 }}"
<div
class=
"grade"
data-score=
"{{ self.score.1 }}"
data-correct_answer=
"{{ self.score.2 }}"
data-correct_answer=
"{{ self.score.2
|length
}}"
data-incorrect_answer=
"{{ self.score.3 }}"
data-incorrect_answer=
"{{ self.score.3
|length
}}"
data-partially_correct_answer=
"{{ self.score.4 }}"
data-partially_correct_answer=
"{{ self.score.4
|length
}}"
data-max_attempts=
"{{ self.max_attempts }}"
data-max_attempts=
"{{ self.max_attempts }}"
data-num_attempts=
"{{ self.num_attempts }}"
data-num_attempts=
"{{ self.num_attempts }}"
data-assessment_message=
"{{ self.assessment_message }}"
>
data-assessment_message=
"{{ self.assessment_message }}"
>
...
...
problem_builder/templates/html/mentoring_grade.html
View file @
990ba628
...
@@ -10,33 +10,42 @@
...
@@ -10,33 +10,42 @@
<
hr
/>
<
hr
/>
<
span
class
=
"assessment-checkmark icon-2x checkmark-correct icon-ok fa fa-check"
><
/span
>
<
span
class
=
"assessment-checkmark icon-2x checkmark-correct icon-ok fa fa-check"
><
/span
>
<
p
>
<
div
class
=
"results-section"
>
<%=
_
.
template
(
<
p
>
ngettext
(
<%=
_
.
template
(
"You answered 1 question correctly."
,
ngettext
(
"You answered {number_correct} questions correctly."
,
"You answered 1 question correctly."
,
correct_answer
"You answered {number_correct} questions correctly."
,
),
{
number_correct
:
correct_answer
},
{
interpolate
:
/
\{(
.+
?)\}
/g
})
correct_answer
%>
),
{
number_correct
:
correct_answer
},
{
interpolate
:
/
\{(
.+
?)\}
/g
})
<
/p
>
%>
<
/p
>
<%=
runDetails
(
'correct'
)
%>
<
/div
>
<
span
class
=
"assessment-checkmark icon-2x checkmark-partially-correct icon-ok fa fa-check"
><
/span
>
<
span
class
=
"assessment-checkmark icon-2x checkmark-partially-correct icon-ok fa fa-check"
><
/span
>
<
p
>
<
div
class
=
"results-section"
>
<%=
_
.
template
(
<
p
>
ngettext
(
<%=
_
.
template
(
"You answered 1 question partially correctly."
,
ngettext
(
"You answered {number_partially_correct} questions partially correctly."
,
"You answered 1 question partially correctly."
,
partially_correct_answer
"You answered {number_partially_correct} questions partially correctly."
,
),
{
number_partially_correct
:
partially_correct_answer
},
{
interpolate
:
/
\{(
.+
?)\}
/g
})
partially_correct_answer
%>
),
{
number_partially_correct
:
partially_correct_answer
},
{
interpolate
:
/
\{(
.+
?)\}
/g
})
<
/p
>
%>
<
/p
>
<%=
runDetails
(
'partial'
)
%>
<
/div
>
<
span
class
=
"assessment-checkmark icon-2x checkmark-incorrect icon-exclamation fa fa-exclamation"
><
/span
>
<
span
class
=
"assessment-checkmark icon-2x checkmark-incorrect icon-exclamation fa fa-exclamation"
><
/span
>
<
p
>
<
div
class
=
"results-section"
>
<%=
_
.
template
(
<
p
>
ngettext
(
<%=
_
.
template
(
"You answered 1 question incorrectly."
,
ngettext
(
"You answered {number_incorrect} questions incorrectly."
,
"You answered 1 question incorrectly."
,
incorrect_answer
"You answered {number_incorrect} questions incorrectly."
,
),
{
number_incorrect
:
incorrect_answer
},
{
interpolate
:
/
\{(
.+
?)\}
/g
})
incorrect_answer
%>
),
{
number_incorrect
:
incorrect_answer
},
{
interpolate
:
/
\{(
.+
?)\}
/g
})
<
/p
>
%>
<
/p
>
<%=
runDetails
(
'incorrect'
)
%>
<
/div
>
</script>
</script>
problem_builder/templates/html/mentoring_review_questions.html
0 → 100644
View file @
990ba628
<script
type=
"text/template"
id=
"xblock-review-questions-template"
>
<%
var
q
,
last_question
;
%>
<
ul
class
=
"review-list <%= label %>-list"
>
<%
for
(
var
question
in
questions
)
{{
q
=
questions
[
question
];
last_question
=
question
==
questions
.
length
-
1
;
%>
<
li
><
a
href
=
"#"
class
=
"question-link"
data
-
name
=
"<%= q.id %>"
><=
gettext
(
"Question {number}"
,
{
number
:
q
.
number
})
=><
/a></
li
>
<%
}}
%>
<
/ul
>
</script>
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