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
a4f9ec66
Commit
a4f9ec66
authored
Jul 30, 2014
by
Xavier Antoviaque
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #49 from aboudreault/mentoring-assessment-mode
Mentoring assessment mode
parents
31042d9c
1105c899
Show whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
654 additions
and
164 deletions
+654
-164
README.md
+34
-7
mentoring/answer.py
+8
-5
mentoring/html.py
+4
-1
mentoring/light_children.py
+12
-0
mentoring/mcq.py
+1
-0
mentoring/mentoring.py
+142
-5
mentoring/mrq.py
+1
-0
mentoring/public/css/mentoring.css
+13
-0
mentoring/public/js/answer.js
+6
-2
mentoring/public/js/mentoring.js
+51
-135
mentoring/public/js/mentoring_assessment_view.js
+193
-0
mentoring/public/js/mentoring_standard_view.js
+125
-0
mentoring/public/js/questionnaire.js
+10
-0
mentoring/questionnaire.py
+5
-3
mentoring/table.py
+4
-4
mentoring/templates/html/mentoring.html
+20
-1
mentoring/templates/html/mentoring_grade.html
+14
-0
mentoring/templates/xml/mentoring_default.xml
+1
-1
mentoring/utils.py
+10
-0
No files found.
README.md
View file @
a4f9ec66
...
@@ -19,12 +19,12 @@ Examples
...
@@ -19,12 +19,12 @@ Examples
First XBlock instance:
First XBlock instance:
```
xml
```
xml
<mentoring
url_name=
"goal_definition"
followed_by=
"getting_feedback"
>
<mentoring
url_name=
"goal_definition"
followed_by=
"getting_feedback"
weight=
"20"
>
<html>
<html>
<p>
What is your goal?
</p>
<p>
What is your goal?
</p>
</html>
</html>
<answer
name=
"goal"
/>
<answer
name=
"goal"
weight=
"10"
/>
</mentoring>
</mentoring>
```
```
...
@@ -40,15 +40,18 @@ Second XBlock instance:
...
@@ -40,15 +40,18 @@ Second XBlock instance:
<html>
<html>
<p>
Ask feedback from friends about this goal - what did they think?
</p>
<p>
Ask feedback from friends about this goal - what did they think?
</p>
</html>
</html>
<answer
name=
"goal_feedback"
/>
<answer
name=
"goal_feedback"
weight=
"5"
/>
</mentoring>
</mentoring>
```
```
You can specify the weight of a free form answer. It will be considered during the
grade/score computation.
### Self-assessment MCQs
### Self-assessment MCQs
```
xml
```
xml
<mentoring
url_name=
"mcq_1"
enforce_dependency=
"false"
>
<mentoring
url_name=
"mcq_1"
enforce_dependency=
"false"
>
<mcq
name=
"mcq_1_1"
type=
"choices"
>
<mcq
name=
"mcq_1_1"
type=
"choices"
weight=
"10"
>
<question>
Do you like this MCQ?
</question>
<question>
Do you like this MCQ?
</question>
<choice
value=
"yes"
>
Yes
</choice>
<choice
value=
"yes"
>
Yes
</choice>
<choice
value=
"maybenot"
>
Maybe not
</choice>
<choice
value=
"maybenot"
>
Maybe not
</choice>
...
@@ -59,7 +62,7 @@ Second XBlock instance:
...
@@ -59,7 +62,7 @@ Second XBlock instance:
<tip
reject=
"understand"
><html><div
id=
"test-custom-html"
>
Really?
</div></html></tip>
<tip
reject=
"understand"
><html><div
id=
"test-custom-html"
>
Really?
</div></html></tip>
</mcq>
</mcq>
<mcq
name=
"mcq_1_2"
type=
"rating"
low=
"Not good at all"
high=
"Extremely good"
>
<mcq
name=
"mcq_1_2"
type=
"rating"
low=
"Not good at all"
high=
"Extremely good"
weight=
"5"
>
<question>
How much do you rate this MCQ?
</question>
<question>
How much do you rate this MCQ?
</question>
<choice
value=
"notwant"
>
I don't want to rate it
</choice>
<choice
value=
"notwant"
>
I don't want to rate it
</choice>
...
@@ -78,10 +81,13 @@ Second XBlock instance:
...
@@ -78,10 +81,13 @@ Second XBlock instance:
</mentoring>
</mentoring>
```
```
You can specify the weight of a self-assessment MCQ. It will be considered during the
grade/score computation.
### Self-assessment MRQs
### Self-assessment MRQs
```
xml
```
xml
<mentoring
url_name=
"m
c
q_1"
enforce_dependency=
"false"
>
<mentoring
url_name=
"m
r
q_1"
enforce_dependency=
"false"
>
<mrq
name=
"mrq_1_1"
type=
"choices"
hide_results=
"true"
>
<mrq
name=
"mrq_1_1"
type=
"choices"
hide_results=
"true"
weight=
"10"
>
<question>
What do you like in this MRQ?
</question>
<question>
What do you like in this MRQ?
</question>
<choice
value=
"elegance"
>
Its elegance
</choice>
<choice
value=
"elegance"
>
Its elegance
</choice>
<choice
value=
"beauty"
>
Its beauty
</choice>
<choice
value=
"beauty"
>
Its beauty
</choice>
...
@@ -104,6 +110,9 @@ Second XBlock instance:
...
@@ -104,6 +110,9 @@ Second XBlock instance:
</mentoring>
</mentoring>
```
```
You can specify the weight of a self-assessment MRQ. It will be considered during the
grade/score computation.
### Tables
### Tables
```
xml
```
xml
...
@@ -128,6 +137,24 @@ Second XBlock instance:
...
@@ -128,6 +137,24 @@ Second XBlock instance:
</vertical>
</vertical>
```
```
### Modes
There are 2 mentoring modes available:
*
standard: Traditional mentoring. All questions are displayed in the page and submitted at the
same time. The student get some tips and feedback about their answers. (default mode)
*
assessment: Questions are displayed and submitted one after one. The student dont get tips or
feedback but only know if their answer was correct. Assessment mode comes with a default
max_attempts of 2.
To set the
*assessment*
mode, set the mode attribute in the settings:
```
xml
<mentoring
url_name=
"mentoring_1"
mode=
"assesment"
>
...
</mentoring>
```
### Maximum Attempts
### Maximum Attempts
You can set the number of maximum attempts for the unit completion, as well as
You can set the number of maximum attempts for the unit completion, as well as
...
...
mentoring/answer.py
View file @
a4f9ec66
...
@@ -29,9 +29,9 @@ from lazy import lazy
...
@@ -29,9 +29,9 @@ from lazy import lazy
from
xblock.fragment
import
Fragment
from
xblock.fragment
import
Fragment
from
.light_children
import
LightChild
,
Boolean
,
Scope
,
String
,
Integer
from
.light_children
import
LightChild
,
Boolean
,
Scope
,
String
,
Integer
,
Float
from
.models
import
Answer
from
.models
import
Answer
from
.utils
import
render_template
,
serialize_opaque_key
from
.utils
import
render_
js_
template
,
serialize_opaque_key
# Globals ###########################################################
# Globals ###########################################################
...
@@ -53,6 +53,8 @@ class AnswerBlock(LightChild):
...
@@ -53,6 +53,8 @@ class AnswerBlock(LightChild):
default
=
None
,
scope
=
Scope
.
content
)
default
=
None
,
scope
=
Scope
.
content
)
min_characters
=
Integer
(
help
=
"Minimum number of characters allowed for the answer"
,
min_characters
=
Integer
(
help
=
"Minimum number of characters allowed for the answer"
,
default
=
0
,
scope
=
Scope
.
content
)
default
=
0
,
scope
=
Scope
.
content
)
weight
=
Float
(
help
=
"Defines the maximum total grade of the light child block."
,
default
=
1
,
scope
=
Scope
.
content
,
enforce_type
=
True
)
@lazy
@lazy
def
student_input
(
self
):
def
student_input
(
self
):
...
@@ -74,11 +76,11 @@ class AnswerBlock(LightChild):
...
@@ -74,11 +76,11 @@ class AnswerBlock(LightChild):
def
mentoring_view
(
self
,
context
=
None
):
def
mentoring_view
(
self
,
context
=
None
):
if
not
self
.
read_only
:
if
not
self
.
read_only
:
html
=
render_template
(
'templates/html/answer_editable.html'
,
{
html
=
render_
js_
template
(
'templates/html/answer_editable.html'
,
{
'self'
:
self
,
'self'
:
self
,
})
})
else
:
else
:
html
=
render_template
(
'templates/html/answer_read_only.html'
,
{
html
=
render_
js_
template
(
'templates/html/answer_read_only.html'
,
{
'self'
:
self
,
'self'
:
self
,
})
})
...
@@ -90,7 +92,7 @@ class AnswerBlock(LightChild):
...
@@ -90,7 +92,7 @@ class AnswerBlock(LightChild):
return
fragment
return
fragment
def
mentoring_table_view
(
self
,
context
=
None
):
def
mentoring_table_view
(
self
,
context
=
None
):
html
=
render_template
(
'templates/html/answer_table.html'
,
{
html
=
render_
js_
template
(
'templates/html/answer_table.html'
,
{
'self'
:
self
,
'self'
:
self
,
})
})
fragment
=
Fragment
(
html
)
fragment
=
Fragment
(
html
)
...
@@ -104,6 +106,7 @@ class AnswerBlock(LightChild):
...
@@ -104,6 +106,7 @@ class AnswerBlock(LightChild):
return
{
return
{
'student_input'
:
self
.
student_input
,
'student_input'
:
self
.
student_input
,
'completed'
:
self
.
completed
,
'completed'
:
self
.
completed
,
'weight'
:
self
.
weight
,
'score'
:
1
if
self
.
completed
else
0
,
'score'
:
1
if
self
.
completed
else
0
,
}
}
...
...
mentoring/html.py
View file @
a4f9ec66
...
@@ -56,7 +56,10 @@ class HTMLBlock(LightChild):
...
@@ -56,7 +56,10 @@ class HTMLBlock(LightChild):
return
block
return
block
def
student_view
(
self
,
context
=
None
):
def
student_view
(
self
,
context
=
None
):
return
Fragment
(
self
.
content
)
return
Fragment
(
u"<script type='text/template' id='{}'>
\n
{}
\n
</script>"
.
format
(
'light-child-template'
,
self
.
content
))
def
mentoring_view
(
self
,
context
=
None
):
def
mentoring_view
(
self
,
context
=
None
):
return
self
.
student_view
(
context
)
return
self
.
student_view
(
context
)
...
...
mentoring/light_children.py
View file @
a4f9ec66
...
@@ -365,6 +365,18 @@ class Boolean(LightChildField):
...
@@ -365,6 +365,18 @@ class Boolean(LightChildField):
self
.
data
[
instance
]
=
value
self
.
data
[
instance
]
=
value
class
Float
(
LightChildField
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
Float
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
default
=
kwargs
.
get
(
'default'
,
0
)
def
__set__
(
self
,
instance
,
value
):
try
:
self
.
data
[
instance
]
=
float
(
value
)
except
(
TypeError
,
ValueError
):
# not an integer
self
.
data
[
instance
]
=
0
class
List
(
LightChildField
):
class
List
(
LightChildField
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
List
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
super
(
List
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
...
...
mentoring/mcq.py
View file @
a4f9ec66
...
@@ -81,6 +81,7 @@ class MCQBlock(QuestionnaireAbstractBlock):
...
@@ -81,6 +81,7 @@ class MCQBlock(QuestionnaireAbstractBlock):
'submission'
:
submission
,
'submission'
:
submission
,
'completed'
:
completed
,
'completed'
:
completed
,
'tips'
:
tips
,
'tips'
:
tips
,
'weight'
:
self
.
weight
,
'score'
:
1
if
completed
else
0
,
'score'
:
1
if
completed
else
0
,
}
}
log
.
debug
(
u'MCQ submission result:
%
s'
,
result
)
log
.
debug
(
u'MCQ submission result:
%
s'
,
result
)
...
...
mentoring/mentoring.py
View file @
a4f9ec66
...
@@ -30,11 +30,12 @@ from lxml import etree
...
@@ -30,11 +30,12 @@ from lxml import etree
from
StringIO
import
StringIO
from
StringIO
import
StringIO
from
xblock.core
import
XBlock
from
xblock.core
import
XBlock
from
xblock.fields
import
Boolean
,
Scope
,
String
,
Integer
,
Float
from
xblock.fields
import
Boolean
,
Scope
,
String
,
Integer
,
Float
,
List
from
xblock.fragment
import
Fragment
from
xblock.fragment
import
Fragment
from
.light_children
import
XBlockWithLightChildren
from
.light_children
import
XBlockWithLightChildren
from
.title
import
TitleBlock
from
.title
import
TitleBlock
from
.html
import
HTMLBlock
from
.message
import
MentoringMessageBlock
from
.message
import
MentoringMessageBlock
from
.utils
import
get_scenarios_from_path
,
load_resource
,
render_template
from
.utils
import
get_scenarios_from_path
,
load_resource
,
render_template
...
@@ -75,9 +76,40 @@ class MentoringBlock(XBlockWithLightChildren):
...
@@ -75,9 +76,40 @@ class MentoringBlock(XBlockWithLightChildren):
default
=
0
,
scope
=
Scope
.
user_state
,
enforce_type
=
True
)
default
=
0
,
scope
=
Scope
.
user_state
,
enforce_type
=
True
)
max_attempts
=
Integer
(
help
=
"Number of max attempts for this questions"
,
default
=
0
,
max_attempts
=
Integer
(
help
=
"Number of max attempts for this questions"
,
default
=
0
,
scope
=
Scope
.
content
,
enforce_type
=
True
)
scope
=
Scope
.
content
,
enforce_type
=
True
)
mode
=
String
(
help
=
"Mode of the mentoring. 'standard' or 'accessment'"
,
default
=
'standard'
,
scope
=
Scope
.
content
)
step
=
Integer
(
help
=
"Keep track of the student assessment progress."
,
default
=
0
,
scope
=
Scope
.
user_state
,
enforce_type
=
True
)
student_results
=
List
(
help
=
"Store results of student choices."
,
default
=
[],
scope
=
Scope
.
user_state
)
icon_class
=
'problem'
icon_class
=
'problem'
has_score
=
True
has_score
=
True
MENTORING_MODES
=
(
'standard'
,
'assessment'
)
@property
def
is_assessment
(
self
):
return
self
.
mode
==
'assessment'
@property
def
steps
(
self
):
return
[
child
for
child
in
self
.
get_children_objects
()
if
not
isinstance
(
child
,
(
HTMLBlock
,
TitleBlock
,
MentoringMessageBlock
))]
@property
def
score
(
self
):
"""Compute the student score taking into account the light child weight."""
total_child_weight
=
sum
(
float
(
step
.
weight
)
for
step
in
self
.
steps
)
if
total_child_weight
==
0
:
return
(
0
,
0
,
0
)
score
=
sum
(
r
[
1
][
'score'
]
*
r
[
1
][
'weight'
]
\
for
r
in
self
.
student_results
)
/
total_child_weight
correct
=
sum
(
1
for
r
in
self
.
student_results
if
r
[
1
][
'completed'
]
==
True
)
incorrect
=
sum
(
1
for
r
in
self
.
student_results
if
r
[
1
][
'completed'
]
==
False
)
return
(
score
,
float
(
'
%0.2
f'
%
(
score
*
100
,)),
correct
,
incorrect
)
def
student_view
(
self
,
context
):
def
student_view
(
self
,
context
):
fragment
,
named_children
=
self
.
get_children_fragment
(
fragment
,
named_children
=
self
.
get_children_fragment
(
context
,
view_name
=
'mentoring_view'
,
context
,
view_name
=
'mentoring_view'
,
...
@@ -92,8 +124,18 @@ class MentoringBlock(XBlockWithLightChildren):
...
@@ -92,8 +124,18 @@ class MentoringBlock(XBlockWithLightChildren):
fragment
.
add_css_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/css/mentoring.css'
))
fragment
.
add_css_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/css/mentoring.css'
))
fragment
.
add_javascript_url
(
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/vendor/underscore-min.js'
))
self
.
runtime
.
local_resource_url
(
self
,
'public/js/vendor/underscore-min.js'
))
if
self
.
is_assessment
:
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/mentoring_assessment_view.js'
)
)
else
:
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/mentoring_standard_view.js'
)
)
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
(
load_resource
(
'templates/html/mentoring_attempts.html'
),
"text/html"
)
fragment
.
add_resource
(
load_resource
(
'templates/html/mentoring_attempts.html'
),
"text/html"
)
fragment
.
add_resource
(
load_resource
(
'templates/html/mentoring_grade.html'
),
"text/html"
)
fragment
.
initialize_js
(
'MentoringBlock'
)
fragment
.
initialize_js
(
'MentoringBlock'
)
...
@@ -129,6 +171,9 @@ class MentoringBlock(XBlockWithLightChildren):
...
@@ -129,6 +171,9 @@ class MentoringBlock(XBlockWithLightChildren):
log
.
info
(
u'Received submissions: {}'
.
format
(
submissions
))
log
.
info
(
u'Received submissions: {}'
.
format
(
submissions
))
self
.
attempted
=
True
self
.
attempted
=
True
if
self
.
is_assessment
:
return
self
.
handleAssessmentSubmit
(
submissions
,
suffix
)
submit_results
=
[]
submit_results
=
[]
completed
=
True
completed
=
True
for
child
in
self
.
get_children_objects
():
for
child
in
self
.
get_children_objects
():
...
@@ -163,9 +208,15 @@ class MentoringBlock(XBlockWithLightChildren):
...
@@ -163,9 +208,15 @@ class MentoringBlock(XBlockWithLightChildren):
# Once it was completed, lock score
# Once it was completed, lock score
if
not
self
.
completed
:
if
not
self
.
completed
:
score
=
sum
(
r
[
1
][
'score'
]
for
r
in
submit_results
)
/
float
(
len
(
submit_results
))
# save user score and results
while
self
.
student_results
:
self
.
student_results
.
pop
()
for
result
in
submit_results
:
self
.
student_results
.
append
(
result
)
(
raw_score
,
score
,
correct
,
incorrect
)
=
self
.
score
self
.
runtime
.
publish
(
self
,
'grade'
,
{
self
.
runtime
.
publish
(
self
,
'grade'
,
{
'value'
:
score
,
'value'
:
raw_
score
,
'max_value'
:
1
,
'max_value'
:
1
,
})
})
...
@@ -183,6 +234,77 @@ class MentoringBlock(XBlockWithLightChildren):
...
@@ -183,6 +234,77 @@ class MentoringBlock(XBlockWithLightChildren):
'num_attempts'
:
self
.
num_attempts
'num_attempts'
:
self
.
num_attempts
}
}
def
handleAssessmentSubmit
(
self
,
submissions
,
suffix
):
completed
=
False
step
=
0
children
=
[
child
for
child
in
self
.
get_children_objects
()
\
if
not
isinstance
(
child
,
TitleBlock
)]
for
child
in
children
:
if
child
.
name
and
child
.
name
in
submissions
:
submission
=
submissions
[
child
.
name
]
# Assessment mode doesn't allow to modify answers
# This will get the student back at the step he should be
step
=
children
.
index
(
child
)
if
self
.
step
>
step
or
self
.
max_attempts_reached
:
step
=
self
.
step
completed
=
False
break
self
.
step
=
step
+
1
child_result
=
child
.
submit
(
submission
)
if
'tips'
in
child_result
:
del
child_result
[
'tips'
]
self
.
student_results
.
append
([
child
.
name
,
child_result
])
child
.
save
()
completed
=
child_result
[
'completed'
]
(
raw_score
,
score
,
correct
,
incorrect
)
=
self
.
score
if
step
==
len
(
self
.
steps
):
log
.
info
(
u'Last assessment step submitted: {}'
.
format
(
submissions
))
if
not
self
.
max_attempts_reached
:
self
.
runtime
.
publish
(
self
,
'grade'
,
{
'value'
:
raw_score
,
'max_value'
:
1
,
})
self
.
num_attempts
+=
1
self
.
completed
=
True
return
{
'completed'
:
completed
,
'attempted'
:
self
.
attempted
,
'max_attempts'
:
self
.
max_attempts
,
'num_attempts'
:
self
.
num_attempts
,
'step'
:
self
.
step
,
'score'
:
score
,
'correct_answer'
:
correct
,
'incorrect_answer'
:
incorrect
}
@XBlock.json_handler
def
try_again
(
self
,
data
,
suffix
=
''
):
if
self
.
max_attempts_reached
:
return
{
'result'
:
'error'
,
'message'
:
'max attempts reached'
}
# reset
self
.
step
=
0
self
.
completed
=
False
while
self
.
student_results
:
self
.
student_results
.
pop
()
return
{
'result'
:
'success'
}
@property
@property
def
max_attempts_reached
(
self
):
def
max_attempts_reached
(
self
):
return
self
.
max_attempts
>
0
and
self
.
num_attempts
>=
self
.
max_attempts
return
self
.
max_attempts
>
0
and
self
.
num_attempts
>=
self
.
max_attempts
...
@@ -222,19 +344,34 @@ class MentoringBlock(XBlockWithLightChildren):
...
@@ -222,19 +344,34 @@ class MentoringBlock(XBlockWithLightChildren):
def
studio_submit
(
self
,
submissions
,
suffix
=
''
):
def
studio_submit
(
self
,
submissions
,
suffix
=
''
):
log
.
info
(
u'Received studio submissions: {}'
.
format
(
submissions
))
log
.
info
(
u'Received studio submissions: {}'
.
format
(
submissions
))
success
=
True
xml_content
=
submissions
[
'xml_content'
]
xml_content
=
submissions
[
'xml_content'
]
try
:
try
:
etree
.
parse
(
StringIO
(
xml_content
))
content
=
etree
.
parse
(
StringIO
(
xml_content
))
except
etree
.
XMLSyntaxError
as
e
:
except
etree
.
XMLSyntaxError
as
e
:
response
=
{
response
=
{
'result'
:
'error'
,
'result'
:
'error'
,
'message'
:
e
.
message
'message'
:
e
.
message
}
}
success
=
False
else
:
else
:
root
=
content
.
getroot
()
if
'mode'
in
root
.
attrib
:
if
root
.
attrib
[
'mode'
]
not
in
self
.
MENTORING_MODES
:
response
=
{
'result'
:
'error'
,
'message'
:
"Invalid mentoring mode: should be 'standard' or 'assessment'"
}
success
=
False
elif
root
.
attrib
[
'mode'
]
==
'assessment'
and
'max_attempts'
not
in
root
.
attrib
:
# assessment has a default of 2 max_attempts
root
.
attrib
[
'max_attempts'
]
=
'2'
if
success
:
response
=
{
response
=
{
'result'
:
'success'
,
'result'
:
'success'
,
}
}
self
.
xml_content
=
xml_content
self
.
xml_content
=
etree
.
tostring
(
content
,
pretty_print
=
True
)
log
.
debug
(
u'Response from Studio: {}'
.
format
(
response
))
log
.
debug
(
u'Response from Studio: {}'
.
format
(
response
))
return
response
return
response
...
...
mentoring/mrq.py
View file @
a4f9ec66
...
@@ -82,6 +82,7 @@ class MRQBlock(QuestionnaireAbstractBlock):
...
@@ -82,6 +82,7 @@ class MRQBlock(QuestionnaireAbstractBlock):
'completed'
:
completed
,
'completed'
:
completed
,
'choices'
:
results
,
'choices'
:
results
,
'message'
:
self
.
message
,
'message'
:
self
.
message
,
'weight'
:
self
.
weight
,
'score'
:
sum
(
1.0
for
r
in
results
if
r
[
'completed'
])
/
len
(
results
)
'score'
:
sum
(
1.0
for
r
in
results
if
r
[
'completed'
])
/
len
(
results
)
}
}
...
...
mentoring/public/css/mentoring.css
View file @
a4f9ec66
...
@@ -53,6 +53,10 @@
...
@@ -53,6 +53,10 @@
margin-top
:
20px
;
margin-top
:
20px
;
}
}
.mentoring
.submit
input
{
display
:
none
;
}
.mentoring
.attempts
{
.mentoring
.attempts
{
margin-left
:
10px
;
margin-left
:
10px
;
display
:
inline-block
;
display
:
inline-block
;
...
@@ -86,6 +90,15 @@
...
@@ -86,6 +90,15 @@
height
:
33.33px
;
height
:
33.33px
;
}
}
.mentoring
.assessment-checkmark
{
margin-right
:
10px
;
}
.mentoring
.grade
.checkmark-incorrect
{
margin-left
:
10px
;
margin-right
:
20px
;
}
.mentoring
input
[
type
=
button
],
.mentoring
input
[
type
=
button
],
.mentoring
input
[
type
=
button
]
:focus
{
.mentoring
input
[
type
=
button
]
:focus
{
background-color
:
#3384ca
;
background-color
:
#3384ca
;
...
...
mentoring/public/js/answer.js
View file @
a4f9ec66
function
AnswerBlock
(
runtime
,
element
)
{
function
AnswerBlock
(
runtime
,
element
)
{
return
{
return
{
mode
:
null
,
init
:
function
(
options
)
{
init
:
function
(
options
)
{
// register the child validator
// register the child validator
$
(
':input'
,
element
).
on
(
'keyup'
,
options
.
onChange
);
$
(
':input'
,
element
).
on
(
'keyup'
,
options
.
onChange
);
this
.
mode
=
options
.
mode
;
var
checkmark
=
$
(
'.answer-checkmark'
,
element
);
var
checkmark
=
$
(
'.answer-checkmark'
,
element
);
var
completed
=
$
(
'.xblock-answer'
,
element
).
data
(
'completed'
);
var
completed
=
$
(
'.xblock-answer'
,
element
).
data
(
'completed'
);
if
(
completed
===
'True'
)
{
if
(
completed
===
'True'
&&
this
.
mode
===
'standard'
)
{
checkmark
.
addClass
(
'checkmark-correct icon-ok fa-check'
);
checkmark
.
addClass
(
'checkmark-correct icon-ok fa-check'
);
}
}
},
},
...
@@ -17,6 +18,9 @@ function AnswerBlock(runtime, element) {
...
@@ -17,6 +18,9 @@ function AnswerBlock(runtime, element) {
},
},
handleSubmit
:
function
(
result
)
{
handleSubmit
:
function
(
result
)
{
if
(
this
.
mode
===
'assessment'
)
return
;
var
checkmark
=
$
(
'.answer-checkmark'
,
element
);
var
checkmark
=
$
(
'.answer-checkmark'
,
element
);
$
(
element
).
find
(
'.message'
).
text
((
result
||
{}).
error
||
''
);
$
(
element
).
find
(
'.message'
).
text
((
result
||
{}).
error
||
''
);
...
...
mentoring/public/js/mentoring.js
View file @
a4f9ec66
function
MentoringBlock
(
runtime
,
element
)
{
function
MentoringBlock
(
runtime
,
element
)
{
var
attemptsTemplate
=
_
.
template
(
$
(
'#xblock-attempts-template'
).
html
());
var
attemptsTemplate
=
_
.
template
(
$
(
'#xblock-attempts-template'
).
html
());
var
children
;
// Keep track of children. A Child need a single object scope for its data.
var
data
=
$
(
'.mentoring'
,
element
).
data
();
var
submitXHR
;
var
children_dom
=
[];
// Keep track of children. A Child need a single object scope for its data.
var
children
=
[];
function
renderAttempts
()
{
var
step
=
data
.
step
;
var
data
=
$
(
'.attempts'
,
element
).
data
();
$
(
'.attempts'
,
element
).
html
(
attemptsTemplate
(
data
));
}
function
renderDependency
()
{
var
warning_dom
=
$
(
'.missing-dependency'
,
element
),
data
=
warning_dom
.
data
();
if
(
data
.
missing
===
'True'
)
{
warning_dom
.
show
();
}
}
function
callIfExists
(
obj
,
fn
)
{
function
callIfExists
(
obj
,
fn
)
{
if
(
typeof
obj
!==
'undefined'
&&
typeof
obj
[
fn
]
==
'function'
)
{
if
(
typeof
obj
!==
'undefined'
&&
typeof
obj
[
fn
]
==
'function'
)
{
...
@@ -25,155 +13,83 @@ function MentoringBlock(runtime, element) {
...
@@ -25,155 +13,83 @@ function MentoringBlock(runtime, element) {
}
}
}
}
function
handleSubmitResults
(
results
)
{
function
renderAttempts
()
{
messagesDOM
.
empty
().
hide
();
var
data
=
$
(
'.attempts'
,
element
).
data
();
$
(
'.attempts'
,
element
).
html
(
attemptsTemplate
(
data
));
$
.
each
(
results
.
submitResults
||
[],
function
(
index
,
submitResult
)
{
var
input
=
submitResult
[
0
],
result
=
submitResult
[
1
],
child
=
getChildByName
(
element
,
input
);
var
options
=
{
max_attempts
:
results
.
max_attempts
,
num_attempts
:
results
.
num_attempts
}
}
callIfExists
(
child
,
'handleSubmit'
,
result
,
options
);
});
$
(
'.attempts'
,
element
).
data
(
'max_attempts'
,
results
.
max_attempts
);
function
renderDependency
()
{
$
(
'.attempts'
,
element
).
data
(
'num_attempts'
,
results
.
num_attempts
);
var
warning_dom
=
$
(
'.missing-dependency'
,
element
),
renderAttempts
();
data
=
warning_dom
.
data
();
// Messages should only be displayed upon hitting 'submit', not on page reload
if
(
data
.
missing
===
'True'
)
{
messagesDOM
.
append
(
results
.
message
);
warning_dom
.
show
();
if
(
messagesDOM
.
html
().
trim
())
{
messagesDOM
.
prepend
(
'<div class="title1">Feedback</div>'
);
messagesDOM
.
show
();
}
}
submitDOM
.
attr
(
'disabled'
,
'disabled'
);
}
}
function
getChildren
(
element
)
{
function
readChildren
(
element
)
{
if
(
!
_
.
isUndefined
(
children
))
var
doms
=
$
(
'.xblock-light-child'
,
element
);
return
children
;
var
children_dom
=
$
(
'.xblock-light-child'
,
element
);
$
.
each
(
doms
,
function
(
index
,
child_dom
)
{
children
=
[];
$
.
each
(
children_dom
,
function
(
index
,
child_dom
)
{
var
child_type
=
$
(
child_dom
).
attr
(
'data-type'
),
var
child_type
=
$
(
child_dom
).
attr
(
'data-type'
),
child
=
window
[
child_type
];
child
=
window
[
child_type
];
children_dom
.
push
(
child_dom
);
children
.
push
(
child
);
if
(
typeof
child
!==
'undefined'
)
{
if
(
typeof
child
!==
'undefined'
)
{
child
=
child
(
runtime
,
child_dom
);
child
=
child
(
runtime
,
child_dom
);
child
.
name
=
$
(
child_dom
).
attr
(
'name'
);
child
.
name
=
$
(
child_dom
).
attr
(
'name'
);
children
.
push
(
child
)
;
children
[
children
.
length
-
1
]
=
child
;
}
}
});
});
return
children
;
}
}
function
getChildByName
(
element
,
name
)
{
/* Init and display a child. */
var
children
=
getChildren
(
element
);
function
displayChild
(
index
,
options
)
{
var
options
=
options
||
{};
options
.
mode
=
data
.
mode
;
if
(
index
>=
children
.
length
)
return
children
.
length
;
for
(
var
i
=
0
;
i
<
children
.
length
;
i
++
)
{
var
template
=
$
(
'#light-child-template'
,
children_dom
[
index
]).
html
();
var
child
=
children
[
i
];
$
(
children_dom
[
index
]).
append
(
template
);
if
(
child
.
name
===
name
)
{
$
(
children_dom
[
index
]).
show
();
var
child
=
children
[
index
];
callIfExists
(
child
,
'init'
,
options
);
return
child
;
return
child
;
}
}
}
}
function
submit
()
{
var
success
=
true
;
var
data
=
{};
var
children
=
getChildren
(
element
);
for
(
var
i
=
0
;
i
<
children
.
length
;
i
++
)
{
var
child
=
children
[
i
];
if
(
child
.
name
!==
undefined
)
{
data
[
child
.
name
]
=
callIfExists
(
child
,
'submit'
);
}
}
var
handlerUrl
=
runtime
.
handlerUrl
(
element
,
'submit'
);
if
(
submitXHR
)
{
submitXHR
.
abort
();
}
submitXHR
=
$
.
post
(
handlerUrl
,
JSON
.
stringify
(
data
)).
success
(
handleSubmitResults
);
}
function
clearResults
()
{
messagesDOM
.
empty
().
hide
();
var
children
=
getChildren
(
element
);
for
(
var
i
=
0
;
i
<
children
.
length
;
i
++
)
{
callIfExists
(
children
[
i
],
'clearResult'
);
}
}
function
onChange
()
{
clearResults
();
validateXBlock
();
}
function
initXBlock
()
{
messagesDOM
=
$
(
element
).
find
(
'.messages'
);
submitDOM
=
$
(
element
).
find
(
'.submit .input-main'
);
submitDOM
.
bind
(
'click'
,
submit
);
// init children (especially mrq blocks)
function
displayChildren
(
options
)
{
var
children
=
getChildren
(
element
);
$
.
each
(
children_dom
,
function
(
index
)
{
var
options
=
{
displayChild
(
index
,
options
);
onChange
:
onChange
};
_
.
each
(
children
,
function
(
child
)
{
callIfExists
(
child
,
'init'
,
options
);
});
});
renderAttempts
();
renderDependency
();
validateXBlock
();
}
function
handleRefreshResults
(
results
)
{
$
(
element
).
html
(
results
.
html
);
initXBlock
();
}
function
refreshXBlock
()
{
var
handlerUrl
=
runtime
.
handlerUrl
(
element
,
'view'
);
$
.
post
(
handlerUrl
,
'{}'
).
success
(
handleRefreshResults
);
}
}
// validate all children
function
getChildByName
(
element
,
name
)
{
function
validateXBlock
()
{
var
is_valid
=
true
;
var
data
=
$
(
'.attempts'
,
element
).
data
();
var
children
=
getChildren
(
element
);
if
((
data
.
max_attempts
>
0
)
&&
(
data
.
num_attempts
>=
data
.
max_attempts
))
{
is_valid
=
false
;
}
else
{
for
(
var
i
=
0
;
i
<
children
.
length
;
i
++
)
{
for
(
var
i
=
0
;
i
<
children
.
length
;
i
++
)
{
var
child
=
children
[
i
];
var
child
=
children
[
i
];
if
(
child
.
name
!==
undefined
)
{
if
(
child
&&
child
.
name
===
name
)
{
var
child_validation
=
callIfExists
(
child
,
'validate'
);
return
child
;
if
(
_
.
isBoolean
(
child_validation
))
{
is_valid
=
is_valid
&&
child_validation
;
}
}
}
}
}
}
}
if
(
!
is_valid
)
{
var
mentoring
=
{
submitDOM
.
attr
(
'disabled'
,
'disabled'
);
callIfExists
:
callIfExists
,
renderAttempts
:
renderAttempts
,
renderDependency
:
renderDependency
,
readChildren
:
readChildren
,
children_dom
:
children_dom
,
children
:
children
,
displayChild
:
displayChild
,
displayChildren
:
displayChildren
,
getChildByName
:
getChildByName
,
step
:
step
}
}
else
{
submitDOM
.
removeAttr
(
"disabled"
);
if
(
data
.
mode
===
'standard'
)
{
MentoringStandardView
(
runtime
,
element
,
mentoring
);
}
}
else
if
(
data
.
mode
===
'assessment'
)
{
MentoringAssessmentView
(
runtime
,
element
,
mentoring
);
}
}
// We need to manually refresh, XBlocks are currently loaded together with the section
refreshXBlock
(
element
);
}
}
mentoring/public/js/mentoring_assessment_view.js
0 → 100644
View file @
a4f9ec66
function
MentoringAssessmentView
(
runtime
,
element
,
mentoring
)
{
var
gradeTemplate
=
_
.
template
(
$
(
'#xblock-grade-template'
).
html
());
var
submitDOM
,
nextDOM
,
reviewDOM
,
tryAgainDOM
;
var
submitXHR
;
var
checkmark
;
var
active_child
;
var
callIfExists
=
mentoring
.
callIfExists
;
function
cleanAll
()
{
// clean checkmark state
checkmark
.
removeClass
(
'checkmark-correct icon-ok fa-check'
);
checkmark
.
removeClass
(
'checkmark-incorrect icon-exclamation fa-exclamation'
);
/* hide all children */
$
(
':nth-child(2)'
,
mentoring
.
children_dom
).
remove
();
$
(
'.grade'
).
html
(
''
);
$
(
'.attempts'
).
html
(
''
);
}
function
renderGrade
()
{
var
data
=
$
(
'.grade'
,
element
).
data
();
cleanAll
();
$
(
'.grade'
,
element
).
html
(
gradeTemplate
(
data
));
reviewDOM
.
hide
()
submitDOM
.
hide
()
nextDOM
.
hide
();
tryAgainDOM
.
show
();
var
attempts_data
=
$
(
'.attempts'
,
element
).
data
();
if
(
attempts_data
.
num_attempts
>=
attempts_data
.
max_attempts
)
{
tryAgainDOM
.
attr
(
"disabled"
,
"disabled"
);
}
else
{
tryAgainDOM
.
removeAttr
(
"disabled"
);
}
mentoring
.
renderAttempts
();
}
function
handleTryAgain
(
result
)
{
if
(
result
.
result
!==
'success'
)
return
;
active_child
=
0
;
displayNextChild
();
tryAgainDOM
.
hide
();
submitDOM
.
show
().
removeAttr
(
'disabled'
);
nextDOM
.
show
();
}
function
tryAgain
()
{
var
success
=
true
;
var
handlerUrl
=
runtime
.
handlerUrl
(
element
,
'try_again'
);
if
(
submitXHR
)
{
submitXHR
.
abort
();
}
submitXHR
=
$
.
post
(
handlerUrl
,
JSON
.
stringify
({})).
success
(
handleTryAgain
);
}
function
initXBlockView
()
{
submitDOM
=
$
(
element
).
find
(
'.submit .input-main'
);
nextDOM
=
$
(
element
).
find
(
'.submit .input-next'
);
reviewDOM
=
$
(
element
).
find
(
'.submit .input-review'
);
tryAgainDOM
=
$
(
element
).
find
(
'.submit .input-try-again'
);
checkmark
=
$
(
'.assessment-checkmark'
,
element
);
submitDOM
.
show
();
submitDOM
.
bind
(
'click'
,
submit
);
nextDOM
.
bind
(
'click'
,
displayNextChild
);
nextDOM
.
show
();
reviewDOM
.
bind
(
'click'
,
renderGrade
);
tryAgainDOM
.
bind
(
'click'
,
tryAgain
);
active_child
=
mentoring
.
step
-
1
;
mentoring
.
readChildren
();
displayNextChild
();
mentoring
.
renderDependency
();
}
function
isLastChild
()
{
return
(
active_child
==
mentoring
.
children
.
length
-
1
);
}
function
isDone
()
{
return
(
active_child
==
mentoring
.
children
.
length
);
}
function
displayNextChild
()
{
var
options
=
{
onChange
:
onChange
};
cleanAll
();
// find the next real child block to display. HTMLBlock are always displayed
++
active_child
;
while
(
1
)
{
var
child
=
mentoring
.
displayChild
(
active_child
,
options
);
if
((
typeof
child
!==
'undefined'
)
||
active_child
==
mentoring
.
children
.
length
-
1
)
break
;
++
active_child
;
}
if
(
isDone
())
renderGrade
();
nextDOM
.
attr
(
'disabled'
,
'disabled'
);
reviewDOM
.
attr
(
'disabled'
,
'disabled'
);
validateXBlock
();
}
function
onChange
()
{
validateXBlock
();
}
function
handleSubmitResults
(
result
)
{
$
(
'.grade'
,
element
).
data
(
'score'
,
result
.
score
);
$
(
'.grade'
,
element
).
data
(
'correct_answer'
,
result
.
correct_answer
);
$
(
'.grade'
,
element
).
data
(
'incorrect_answer'
,
result
.
incorrect_answer
);
$
(
'.grade'
,
element
).
data
(
'max_attempts'
,
result
.
max_attempts
);
$
(
'.grade'
,
element
).
data
(
'num_attempts'
,
result
.
num_attempts
);
$
(
'.attempts'
,
element
).
data
(
'max_attempts'
,
result
.
max_attempts
);
$
(
'.attempts'
,
element
).
data
(
'num_attempts'
,
result
.
num_attempts
);
if
(
result
.
completed
)
{
checkmark
.
addClass
(
'checkmark-correct icon-ok fa-check'
);
}
else
{
checkmark
.
addClass
(
'checkmark-incorrect icon-exclamation fa-exclamation'
);
}
submitDOM
.
attr
(
'disabled'
,
'disabled'
);
/* Something went wrong with student submission, denied next question */
if
(
result
.
step
!=
active_child
+
1
)
{
active_child
=
result
.
step
-
1
;
displayNextChild
();
}
else
{
nextDOM
.
removeAttr
(
"disabled"
);
reviewDOM
.
removeAttr
(
"disabled"
);
}
}
function
submit
()
{
var
success
=
true
;
var
data
=
{};
var
child
=
mentoring
.
children
[
active_child
];
if
(
child
&&
child
.
name
!==
undefined
)
{
data
[
child
.
name
]
=
callIfExists
(
child
,
'submit'
);
}
var
handlerUrl
=
runtime
.
handlerUrl
(
element
,
'submit'
);
if
(
submitXHR
)
{
submitXHR
.
abort
();
}
submitXHR
=
$
.
post
(
handlerUrl
,
JSON
.
stringify
(
data
)).
success
(
handleSubmitResults
);
}
function
validateXBlock
()
{
var
is_valid
=
true
;
var
data
=
$
(
'.attempts'
,
element
).
data
();
var
children
=
mentoring
.
children
;
// if ((data.max_attempts > 0) && (data.num_attempts >= data.max_attempts)) {
// is_valid = false;
// }
var
child
=
mentoring
.
children
[
active_child
];
if
(
child
&&
child
.
name
!==
undefined
)
{
var
child_validation
=
callIfExists
(
child
,
'validate'
);
if
(
_
.
isBoolean
(
child_validation
))
{
is_valid
=
is_valid
&&
child_validation
;
}
}
if
(
!
is_valid
)
{
submitDOM
.
attr
(
'disabled'
,
'disabled'
);
}
else
{
submitDOM
.
removeAttr
(
"disabled"
);
}
if
(
isLastChild
())
{
nextDOM
.
hide
()
reviewDOM
.
show
();
}
}
initXBlockView
();
}
mentoring/public/js/mentoring_standard_view.js
0 → 100644
View file @
a4f9ec66
function
MentoringStandardView
(
runtime
,
element
,
mentoring
)
{
var
submitXHR
;
var
callIfExists
=
mentoring
.
callIfExists
;
function
handleSubmitResults
(
results
)
{
messagesDOM
.
empty
().
hide
();
$
.
each
(
results
.
submitResults
||
[],
function
(
index
,
submitResult
)
{
var
input
=
submitResult
[
0
],
result
=
submitResult
[
1
],
child
=
mentoring
.
getChildByName
(
element
,
input
);
var
options
=
{
max_attempts
:
results
.
max_attempts
,
num_attempts
:
results
.
num_attempts
}
callIfExists
(
child
,
'handleSubmit'
,
result
,
options
);
});
$
(
'.attempts'
,
element
).
data
(
'max_attempts'
,
results
.
max_attempts
);
$
(
'.attempts'
,
element
).
data
(
'num_attempts'
,
results
.
num_attempts
);
mentoring
.
renderAttempts
();
// Messages should only be displayed upon hitting 'submit', not on page reload
messagesDOM
.
append
(
results
.
message
);
if
(
messagesDOM
.
html
().
trim
())
{
messagesDOM
.
prepend
(
'<div class="title1">Feedback</div>'
);
messagesDOM
.
show
();
}
submitDOM
.
attr
(
'disabled'
,
'disabled'
);
}
function
submit
()
{
var
success
=
true
;
var
data
=
{};
var
children
=
mentoring
.
children
;
for
(
var
i
=
0
;
i
<
children
.
length
;
i
++
)
{
var
child
=
children
[
i
];
if
(
child
&&
child
.
name
!==
undefined
)
{
data
[
child
.
name
]
=
callIfExists
(
child
,
'submit'
);
}
}
var
handlerUrl
=
runtime
.
handlerUrl
(
element
,
'submit'
);
if
(
submitXHR
)
{
submitXHR
.
abort
();
}
submitXHR
=
$
.
post
(
handlerUrl
,
JSON
.
stringify
(
data
)).
success
(
handleSubmitResults
);
}
function
clearResults
()
{
messagesDOM
.
empty
().
hide
();
var
children
=
mentoring
.
children
;
for
(
var
i
=
0
;
i
<
children
.
length
;
i
++
)
{
callIfExists
(
children
[
i
],
'clearResult'
);
}
}
function
onChange
()
{
clearResults
();
validateXBlock
();
}
function
initXBlockView
()
{
messagesDOM
=
$
(
element
).
find
(
'.messages'
);
submitDOM
=
$
(
element
).
find
(
'.submit .input-main'
);
submitDOM
.
bind
(
'click'
,
submit
);
submitDOM
.
show
();
var
options
=
{
onChange
:
onChange
};
mentoring
.
displayChildren
(
options
);
mentoring
.
renderAttempts
();
mentoring
.
renderDependency
();
validateXBlock
();
}
function
handleRefreshResults
(
results
)
{
$
(
element
).
html
(
results
.
html
);
mentoring
.
readChildren
();
initXBlockView
();
}
function
refreshXBlock
()
{
var
handlerUrl
=
runtime
.
handlerUrl
(
element
,
'view'
);
$
.
post
(
handlerUrl
,
'{}'
).
success
(
handleRefreshResults
);
}
// validate all children
function
validateXBlock
()
{
var
is_valid
=
true
;
var
data
=
$
(
'.attempts'
,
element
).
data
();
var
children
=
mentoring
.
children
;
if
((
data
.
max_attempts
>
0
)
&&
(
data
.
num_attempts
>=
data
.
max_attempts
))
{
is_valid
=
false
;
}
else
{
for
(
var
i
=
0
;
i
<
children
.
length
;
i
++
)
{
var
child
=
children
[
i
];
if
(
child
&&
child
.
name
!==
undefined
)
{
var
child_validation
=
callIfExists
(
child
,
'validate'
);
if
(
_
.
isBoolean
(
child_validation
))
{
is_valid
=
is_valid
&&
child_validation
;
}
}
}
}
if
(
!
is_valid
)
{
submitDOM
.
attr
(
'disabled'
,
'disabled'
);
}
else
{
submitDOM
.
removeAttr
(
"disabled"
);
}
}
// We need to manually refresh, XBlocks are currently loaded together with the section
refreshXBlock
(
element
);
}
mentoring/public/js/questionnaire.js
View file @
a4f9ec66
...
@@ -52,7 +52,9 @@ function MessageView(element) {
...
@@ -52,7 +52,9 @@ function MessageView(element) {
function
MCQBlock
(
runtime
,
element
)
{
function
MCQBlock
(
runtime
,
element
)
{
return
{
return
{
mode
:
null
,
init
:
function
(
options
)
{
init
:
function
(
options
)
{
this
.
mode
=
options
.
mode
;
$
(
'input[type=radio]'
,
element
).
on
(
'change'
,
options
.
onChange
);
$
(
'input[type=radio]'
,
element
).
on
(
'change'
,
options
.
onChange
);
},
},
...
@@ -67,6 +69,9 @@ function MCQBlock(runtime, element) {
...
@@ -67,6 +69,9 @@ function MCQBlock(runtime, element) {
},
},
handleSubmit
:
function
(
result
)
{
handleSubmit
:
function
(
result
)
{
if
(
this
.
mode
===
'assessment'
)
return
;
var
messageView
=
MessageView
(
element
);
var
messageView
=
MessageView
(
element
);
messageView
.
clearResult
();
messageView
.
clearResult
();
...
@@ -132,7 +137,9 @@ function MCQBlock(runtime, element) {
...
@@ -132,7 +137,9 @@ function MCQBlock(runtime, element) {
function
MRQBlock
(
runtime
,
element
)
{
function
MRQBlock
(
runtime
,
element
)
{
return
{
return
{
mode
:
null
,
init
:
function
(
options
)
{
init
:
function
(
options
)
{
this
.
mode
=
options
.
mode
;
$
(
'input[type=checkbox]'
,
element
).
on
(
'change'
,
options
.
onChange
);
$
(
'input[type=checkbox]'
,
element
).
on
(
'change'
,
options
.
onChange
);
},
},
...
@@ -147,6 +154,9 @@ function MRQBlock(runtime, element) {
...
@@ -147,6 +154,9 @@ function MRQBlock(runtime, element) {
},
},
handleSubmit
:
function
(
result
,
options
)
{
handleSubmit
:
function
(
result
,
options
)
{
if
(
this
.
mode
===
'assessment'
)
return
;
var
messageView
=
MessageView
(
element
);
var
messageView
=
MessageView
(
element
);
if
(
result
.
message
)
{
if
(
result
.
message
)
{
...
...
mentoring/questionnaire.py
View file @
a4f9ec66
...
@@ -28,9 +28,9 @@ import logging
...
@@ -28,9 +28,9 @@ import logging
from
xblock.fragment
import
Fragment
from
xblock.fragment
import
Fragment
from
.choice
import
ChoiceBlock
from
.choice
import
ChoiceBlock
from
.light_children
import
LightChild
,
Scope
,
String
from
.light_children
import
LightChild
,
Scope
,
String
,
Float
from
.tip
import
TipBlock
from
.tip
import
TipBlock
from
.utils
import
render_template
from
.utils
import
render_template
,
render_js_template
# Globals ###########################################################
# Globals ###########################################################
...
@@ -51,6 +51,8 @@ class QuestionnaireAbstractBlock(LightChild):
...
@@ -51,6 +51,8 @@ class QuestionnaireAbstractBlock(LightChild):
type
=
String
(
help
=
"Type of questionnaire"
,
scope
=
Scope
.
content
,
default
=
"choices"
)
type
=
String
(
help
=
"Type of questionnaire"
,
scope
=
Scope
.
content
,
default
=
"choices"
)
question
=
String
(
help
=
"Question to ask the student"
,
scope
=
Scope
.
content
,
default
=
""
)
question
=
String
(
help
=
"Question to ask the student"
,
scope
=
Scope
.
content
,
default
=
""
)
message
=
String
(
help
=
"General feedback provided when submiting"
,
scope
=
Scope
.
content
,
default
=
""
)
message
=
String
(
help
=
"General feedback provided when submiting"
,
scope
=
Scope
.
content
,
default
=
""
)
weight
=
Float
(
help
=
"Defines the maximum total grade of the light child block."
,
default
=
1
,
scope
=
Scope
.
content
,
enforce_type
=
True
)
valid_types
=
(
'choices'
)
valid_types
=
(
'choices'
)
...
@@ -77,7 +79,7 @@ class QuestionnaireAbstractBlock(LightChild):
...
@@ -77,7 +79,7 @@ class QuestionnaireAbstractBlock(LightChild):
raise
ValueError
,
u'Invalid value for {}.type: `{}`'
.
format
(
name
,
self
.
type
)
raise
ValueError
,
u'Invalid value for {}.type: `{}`'
.
format
(
name
,
self
.
type
)
template_path
=
'templates/html/{}_{}.html'
.
format
(
name
.
lower
(),
self
.
type
)
template_path
=
'templates/html/{}_{}.html'
.
format
(
name
.
lower
(),
self
.
type
)
html
=
render_template
(
template_path
,
{
html
=
render_
js_
template
(
template_path
,
{
'self'
:
self
,
'self'
:
self
,
'custom_choices'
:
self
.
custom_choices
'custom_choices'
:
self
.
custom_choices
})
})
...
...
mentoring/table.py
View file @
a4f9ec66
...
@@ -29,7 +29,7 @@ import logging
...
@@ -29,7 +29,7 @@ import logging
from
xblock.fields
import
Scope
from
xblock.fields
import
Scope
from
.light_children
import
LightChild
,
String
from
.light_children
import
LightChild
,
String
from
.utils
import
load_resource
,
render_template
from
.utils
import
load_resource
,
render_
js_
template
# Globals ###########################################################
# Globals ###########################################################
...
@@ -66,7 +66,7 @@ class MentoringTableBlock(LightChild):
...
@@ -66,7 +66,7 @@ class MentoringTableBlock(LightChild):
else
:
else
:
raise
raise
fragment
.
add_content
(
render_template
(
'templates/html/mentoring-table.html'
,
{
fragment
.
add_content
(
render_
js_
template
(
'templates/html/mentoring-table.html'
,
{
'self'
:
self
,
'self'
:
self
,
'columns_frags'
:
columns_frags
,
'columns_frags'
:
columns_frags
,
'header_frags'
:
header_frags
,
'header_frags'
:
header_frags
,
...
@@ -102,7 +102,7 @@ class MentoringTableColumnBlock(LightChild):
...
@@ -102,7 +102,7 @@ class MentoringTableColumnBlock(LightChild):
fragment
,
named_children
=
self
.
get_children_fragment
(
context
,
fragment
,
named_children
=
self
.
get_children_fragment
(
context
,
view_name
=
'mentoring_table_view'
,
view_name
=
'mentoring_table_view'
,
not_instance_of
=
MentoringTableColumnHeaderBlock
)
not_instance_of
=
MentoringTableColumnHeaderBlock
)
fragment
.
add_content
(
render_template
(
'templates/html/mentoring-table-column.html'
,
{
fragment
.
add_content
(
render_
js_
template
(
'templates/html/mentoring-table-column.html'
,
{
'self'
:
self
,
'self'
:
self
,
'named_children'
:
named_children
,
'named_children'
:
named_children
,
}))
}))
...
@@ -115,7 +115,7 @@ class MentoringTableColumnBlock(LightChild):
...
@@ -115,7 +115,7 @@ class MentoringTableColumnBlock(LightChild):
fragment
,
named_children
=
self
.
get_children_fragment
(
context
,
fragment
,
named_children
=
self
.
get_children_fragment
(
context
,
view_name
=
'mentoring_table_header_view'
,
view_name
=
'mentoring_table_header_view'
,
instance_of
=
MentoringTableColumnHeaderBlock
)
instance_of
=
MentoringTableColumnHeaderBlock
)
fragment
.
add_content
(
render_template
(
'templates/html/mentoring-table-header.html'
,
{
fragment
.
add_content
(
render_
js_
template
(
'templates/html/mentoring-table-header.html'
,
{
'self'
:
self
,
'self'
:
self
,
'named_children'
:
named_children
,
'named_children'
:
named_children
,
}))
}))
...
...
mentoring/templates/html/mentoring.html
View file @
a4f9ec66
<div
class=
"mentoring"
>
<div
class=
"mentoring"
data-mode=
"{{ self.mode }}"
data-step=
"{{ self.step }}"
>
<div
class=
"missing-dependency warning"
data-missing=
"{{ self.has_missing_dependency }}"
>
<div
class=
"missing-dependency warning"
data-missing=
"{{ self.has_missing_dependency }}"
>
You need to complete
<a
href=
"{{ missing_dependency_url }}"
>
the previous step
</a>
before
You need to complete
<a
href=
"{{ missing_dependency_url }}"
>
the previous step
</a>
before
attempting this step.
attempting this step.
...
@@ -13,8 +13,27 @@
...
@@ -13,8 +13,27 @@
{{c.body_html|safe}}
{{c.body_html|safe}}
{% endfor %}
{% endfor %}
{% if self.display_submit %}
{% if self.display_submit %}
<div
class=
"grade"
data-score=
"{{ self.score.1 }}"
data-correct_answer=
"{{ self.score.2 }}"
data-incorrect_answer=
"{{ self.score.3 }}"
data-max_attempts=
"{{ self.max_attempts }}"
data-num_attempts=
"{{ self.num_attempts }}"
>
</div>
<div
class=
"submit"
>
<div
class=
"submit"
>
{% if self.mode == 'assessment' %}
<span
class=
"assessment-checkmark icon-2x"
></span>
{% endif %}
<input
type=
"button"
class=
"input-main"
value=
"Submit"
disabled=
"disabled"
></input>
<input
type=
"button"
class=
"input-main"
value=
"Submit"
disabled=
"disabled"
></input>
{% if self.mode == 'assessment' %}
<input
type=
"button"
class=
"input-next"
value=
"Next Question"
disabled=
"disabled"
></input>
<input
type=
"button"
class=
"input-review"
value=
"Review grade"
disabled=
"disabled"
></input>
<input
type=
"button"
class=
"input-try-again"
value=
"Try again"
disabled=
"disabled"
></input>
{% endif %}
<div
class=
"attempts"
data-max_attempts=
"{{ self.max_attempts }}"
data-num_attempts=
"{{ self.num_attempts }}"
></div>
<div
class=
"attempts"
data-max_attempts=
"{{ self.max_attempts }}"
data-num_attempts=
"{{ self.num_attempts }}"
></div>
</div>
</div>
{% endif %}
{% endif %}
...
...
mentoring/templates/html/mentoring_grade.html
0 → 100644
View file @
a4f9ec66
<script
type=
"text/template"
id=
"xblock-grade-template"
>
<%
if
(
_
.
isNumber
(
max_attempts
)
&&
max_attempts
>
0
&&
num_attempts
>=
max_attempts
)
{{
%>
<
p
>
Note
:
you
have
used
all
attempts
.
Continue
to
the
next
unit
.
<
/p
>
<%
}}
else
{{
%>
<
p
>
Note
:
if
you
retake
this
assessment
,
only
your
final
score
counts
.
<
/p
>
<%
}}
%>
<
h2
>
You
scored
<%=
score
%>%
on
this
assessment
.
<
/h2
>
<
hr
/>
<
span
class
=
"assessment-checkmark icon-2x checkmark-correct icon-ok fa-check"
><
/span
>
<
p
>
You
answered
<%=
correct_answer
%>
questions
correctly
.
<
/p
>
<
span
class
=
"assessment-checkmark icon-2x checkmark-incorrect icon-exclamation fa-exclamation"
><
/span
>
<
p
>
You
answered
<%=
incorrect_answer
%>
questions
incorrectly
.
<
/p
>
</script>
mentoring/templates/xml/mentoring_default.xml
View file @
a4f9ec66
<mentoring
url_name=
"{{ url_name }}"
display_name=
"Nav tooltip title"
weight=
"1"
>
<mentoring
url_name=
"{{ url_name }}"
display_name=
"Nav tooltip title"
weight=
"1"
mode=
"standard"
>
<title>
Default Title
</title>
<title>
Default Title
</title>
<html>
<html>
<p>
What is your goal?
</p>
<p>
What is your goal?
</p>
...
...
mentoring/utils.py
View file @
a4f9ec66
...
@@ -57,6 +57,16 @@ def render_template(template_path, context={}):
...
@@ -57,6 +57,16 @@ def render_template(template_path, context={}):
return
template
.
render
(
Context
(
context
))
return
template
.
render
(
Context
(
context
))
def
render_js_template
(
template_path
,
context
=
{},
id
=
'light-child-template'
):
"""
Render a js template.
"""
return
u"<script type='text/template' id='{}'>
\n
{}
\n
</script>"
.
format
(
id
,
render_template
(
template_path
,
context
)
)
def
list2csv
(
row
):
def
list2csv
(
row
):
"""
"""
Convert a list to a CSV string (single row)
Convert a list to a CSV string (single row)
...
...
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