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
b3d47cb7
Commit
b3d47cb7
authored
Mar 27, 2014
by
Xavier Antoviaque
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #5 from aboudreault/mrq-max-attempts
Mrq max attempts
parents
7d794f08
c810ef22
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
375 additions
and
38 deletions
+375
-38
README.md
+22
-0
mentoring/answer.py
+2
-2
mentoring/light_children.py
+116
-10
mentoring/mentoring.py
+1
-0
mentoring/migrations/0001_initial.py
+1
-2
mentoring/migrations/0004_auto__add_lightchild__add_unique_lightchild_student_id_course_id_name.py
+58
-0
mentoring/models.py
+18
-0
mentoring/mrq.py
+30
-6
mentoring/public/css/mentoring.css
+1
-1
mentoring/public/css/questionnaire.css
+24
-5
mentoring/public/js/mentoring.js
+7
-1
mentoring/public/js/questionnaire.js
+69
-5
mentoring/templates/html/mentoring.html
+1
-1
mentoring/templates/html/mrqblock_attempts.html
+12
-0
mentoring/templates/html/mrqblock_choices.html
+11
-3
mentoring/utils.py
+2
-2
No files found.
README.md
View file @
b3d47cb7
...
...
@@ -75,6 +75,28 @@ Second XBlock instance:
</mentoring>
```
### Self-assessment MRQs
```
xml
<mentoring
url_name=
"mcq_1"
enforce_dependency=
"false"
>
<mrq
name=
"mrq_1_1"
type=
"choices"
max_attempts=
"3"
>
<question>
What do you like in this MRQ?
</question>
<choice
value=
"elegance"
>
Its elegance
</choice>
<choice
value=
"beauty"
>
Its beauty
</choice>
<choice
value=
"gracefulness"
>
Its gracefulness
</choice>
<choice
value=
"bugs"
>
Its bugs
</choice>
<tip
require=
"gracefulness"
>
This MRQ is indeed very graceful
</tip>
<tip
require=
"elegance,beauty"
>
This is something everyone has to like about this MRQ
</tip>
<tip
reject=
"bugs"
>
Nah, there isn't any!
</tip>
<message
type=
"on-submit"
>
Thank you for answering!
</message>
</mrq>
<message
type=
"completed"
>
All is good now...
<html><p>
Congratulations!
</p></html>
</message>
</mentoring>
### Tables
```
xml
...
...
mentoring/answer.py
View file @
b3d47cb7
...
...
@@ -55,7 +55,7 @@ class AnswerBlock(LightChild):
@lazy
def
student_input
(
self
):
"""
Use lazy property instead of XBlock field, as __init__() doesn't support
Use lazy property instead of XBlock field, as __init__() doesn't support
overwriting field values
"""
# Only attempt to locate a model object for this block when the answer has a name
...
...
@@ -79,7 +79,7 @@ class AnswerBlock(LightChild):
html
=
render_template
(
'templates/html/answer_read_only.html'
,
{
'self'
:
self
,
})
fragment
=
Fragment
(
html
)
fragment
.
add_css_url
(
self
.
runtime
.
local_resource_url
(
self
.
xblock_container
,
'public/css/answer.css'
))
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
.
xblock_container
,
...
...
mentoring/light_children.py
View file @
b3d47cb7
...
...
@@ -24,6 +24,10 @@
# Imports ###########################################################
import
logging
import
json
from
lazy
import
lazy
from
weakref
import
WeakKeyDictionary
from
cStringIO
import
StringIO
from
lxml
import
etree
...
...
@@ -34,6 +38,8 @@ from xblock.core import XBlock
from
xblock.fragment
import
Fragment
from
xblock.plugin
import
Plugin
from
.models
import
LightChild
as
LightChildModel
try
:
from
xmodule_modifiers
import
replace_jump_to_id_urls
except
:
...
...
@@ -133,6 +139,7 @@ class LightChildrenMixin(XBlockWithChildrenFragmentsMixin):
"""
Replacement for ```self.runtime.render_child()```
"""
frag
=
getattr
(
child
,
view_name
)(
context
)
frag
.
content
=
u'<div class="xblock-light-child" name="{}" data-type="{}">{}</div>'
.
format
(
child
.
name
,
child
.
__class__
.
__name__
,
frag
.
content
)
...
...
@@ -167,6 +174,7 @@ class XBlockWithLightChildren(LightChildrenMixin, XBlock):
"""
Current HTML view of the XBlock, for refresh by client
"""
frag
=
self
.
student_view
({})
frag
=
self
.
fragment_text_rewriting
(
frag
)
...
...
@@ -194,6 +202,7 @@ class XBlockWithLightChildren(LightChildrenMixin, XBlock):
fragment
=
replace_jump_to_id_urls
(
course_id
,
jump_to_url
,
self
,
'student_view'
,
fragment
,
{})
return
fragment
class
LightChild
(
Plugin
,
LightChildrenMixin
):
"""
Base class for the light children
...
...
@@ -203,6 +212,7 @@ class LightChild(Plugin, LightChildrenMixin):
def
__init__
(
self
,
parent
):
self
.
parent
=
parent
self
.
xblock_container
=
parent
.
xblock_container
self
.
_student_data_loaded
=
False
@property
def
runtime
(
self
):
...
...
@@ -220,30 +230,125 @@ class LightChild(Plugin, LightChildrenMixin):
xmodule_runtime
=
xmodule_runtime
()
return
xmodule_runtime
@lazy
def
student_data
(
self
):
"""
Use lazy property instead of XBlock field, as __init__() doesn't support
overwriting field values
"""
if
not
self
.
name
:
return
''
student_data
=
self
.
get_lightchild_model_object
()
.
student_data
return
student_data
def
load_student_data
(
self
):
"""
Load the student data from the database.
"""
if
self
.
_student_data_loaded
:
return
fields
=
self
.
get_fields_to_save
()
if
not
fields
or
not
self
.
student_data
:
return
student_data
=
json
.
loads
(
self
.
student_data
)
for
field
in
fields
:
if
field
in
student_data
:
setattr
(
self
,
field
,
student_data
[
field
])
self
.
_student_data_loaded
=
True
@classmethod
def
get_fields_to_save
(
cls
):
"""
Returns a list of all LightChildField of the class. Used for saving student data.
"""
return
[]
def
save
(
self
):
pass
"""
Replicate data changes on the related Django model used for sharing of data accross XBlocks
"""
# Save all children
for
child
in
self
.
get_children_objects
():
child
.
save
()
self
.
student_data
=
{}
# Get All LightChild fields to save
for
field
in
self
.
get_fields_to_save
():
self
.
student_data
[
field
]
=
getattr
(
self
,
field
)
if
self
.
name
:
lightchild_data
=
self
.
get_lightchild_model_object
()
if
lightchild_data
.
student_data
!=
self
.
student_data
:
lightchild_data
.
student_data
=
json
.
dumps
(
self
.
student_data
)
lightchild_data
.
save
()
def
get_lightchild_model_object
(
self
,
name
=
None
):
"""
Fetches the LightChild model object for the lightchild named `name`
"""
if
not
name
:
name
=
self
.
name
if
not
name
:
raise
ValueError
,
'LightChild.name field need to be set to a non-null/empty value'
student_id
=
self
.
xmodule_runtime
.
anonymous_student_id
course_id
=
self
.
xmodule_runtime
.
course_id
lightchild_data
,
created
=
LightChildModel
.
objects
.
get_or_create
(
student_id
=
student_id
,
course_id
=
course_id
,
name
=
name
,
)
return
lightchild_data
class
LightChildField
(
object
):
"""
Fake field with no persistence - allows to keep XBlocks fields definitions on LightChild
"""
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
value
=
kwargs
.
get
(
'default'
,
''
)
self
.
default
=
kwargs
.
get
(
'default'
,
''
)
self
.
data
=
WeakKeyDictionary
()
def
__get__
(
self
,
instance
,
name
):
def
__nonzero__
(
self
):
return
bool
(
self
.
value
)
# A LightChildField can depend on student_data
instance
.
load_student_data
(
)
return
self
.
data
.
get
(
instance
,
self
.
default
)
def
__set__
(
self
,
instance
,
value
):
self
.
data
[
instance
]
=
value
class
String
(
LightChildField
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
value
=
kwargs
.
get
(
'default'
,
''
)
or
''
super
(
String
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
default
=
kwargs
.
get
(
'default'
,
''
)
or
''
# def split(self, *args, **kwargs):
# return self.value.split(*args, **kwargs)
def
__str__
(
self
):
return
self
.
value
def
split
(
self
,
*
args
,
**
kwargs
):
return
self
.
value
.
split
(
*
args
,
**
kwargs
)
class
Integer
(
LightChildField
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
Integer
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
default
=
kwargs
.
get
(
'default'
,
0
)
def
__set__
(
self
,
instance
,
value
):
try
:
self
.
data
[
instance
]
=
int
(
value
)
except
(
TypeError
,
ValueError
):
# not an integer
self
.
data
[
instance
]
=
0
class
Boolean
(
LightChildField
):
...
...
@@ -252,7 +357,8 @@ class Boolean(LightChildField):
class
List
(
LightChildField
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
value
=
kwargs
.
get
(
'default'
,
[])
super
(
List
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
default
=
kwargs
.
get
(
'default'
,
[])
class
Scope
(
object
):
...
...
mentoring/mentoring.py
View file @
b3d47cb7
...
...
@@ -84,6 +84,7 @@ class MentoringBlock(XBlockWithLightChildren):
self
.
runtime
.
local_resource_url
(
self
,
'public/js/vendor/underscore-min.js'
))
fragment
.
add_javascript_url
(
self
.
runtime
.
local_resource_url
(
self
,
'public/js/mentoring.js'
))
fragment
.
add_resource
(
load_resource
(
'templates/html/mentoring_progress.html'
),
"text/html"
)
fragment
.
add_resource
(
load_resource
(
'templates/html/mrqblock_attempts.html'
),
"text/html"
)
fragment
.
initialize_js
(
'MentoringBlock'
)
...
...
mentoring/migrations/0001_initial.py
View file @
b3d47cb7
...
...
@@ -43,4 +43,4 @@ class Migration(SchemaMigration):
}
}
complete_apps
=
[
'mentoring'
]
\ No newline at end of file
complete_apps
=
[
'mentoring'
]
mentoring/migrations/0004_auto__add_lightchild__add_unique_lightchild_student_id_course_id_name.py
0 → 100644
View file @
b3d47cb7
# -*- coding: utf-8 -*-
import
datetime
from
south.db
import
db
from
south.v2
import
SchemaMigration
from
django.db
import
models
class
Migration
(
SchemaMigration
):
def
forwards
(
self
,
orm
):
# Adding model 'LightChild'
db
.
create_table
(
'mentoring_lightchild'
,
(
(
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'name'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
50
,
db_index
=
True
)),
(
'student_id'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
32
,
db_index
=
True
)),
(
'course_id'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
50
,
db_index
=
True
)),
(
'student_data'
,
self
.
gf
(
'django.db.models.fields.TextField'
)(
default
=
''
,
blank
=
True
)),
(
'created_on'
,
self
.
gf
(
'django.db.models.fields.DateTimeField'
)(
auto_now_add
=
True
,
blank
=
True
)),
(
'modified_on'
,
self
.
gf
(
'django.db.models.fields.DateTimeField'
)(
auto_now
=
True
,
blank
=
True
)),
))
db
.
send_create_signal
(
'mentoring'
,
[
'LightChild'
])
# Adding unique constraint on 'LightChild', fields ['student_id', 'course_id', 'name']
db
.
create_unique
(
'mentoring_lightchild'
,
[
'student_id'
,
'course_id'
,
'name'
])
def
backwards
(
self
,
orm
):
# Removing unique constraint on 'LightChild', fields ['student_id', 'course_id', 'name']
db
.
delete_unique
(
'mentoring_lightchild'
,
[
'student_id'
,
'course_id'
,
'name'
])
# Deleting model 'LightChild'
db
.
delete_table
(
'mentoring_lightchild'
)
models
=
{
'mentoring.answer'
:
{
'Meta'
:
{
'unique_together'
:
"(('student_id', 'course_id', 'name'),)"
,
'object_name'
:
'Answer'
},
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
,
'db_index'
:
'True'
}),
'created_on'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified_on'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now'
:
'True'
,
'blank'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
,
'db_index'
:
'True'
}),
'student_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'32'
,
'db_index'
:
'True'
}),
'student_input'
:
(
'django.db.models.fields.TextField'
,
[],
{
'default'
:
"''"
,
'blank'
:
'True'
})
},
'mentoring.lightchild'
:
{
'Meta'
:
{
'unique_together'
:
"(('student_id', 'course_id', 'name'),)"
,
'object_name'
:
'LightChild'
},
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
,
'db_index'
:
'True'
}),
'created_on'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'blank'
:
'True'
}),
'student_data'
:
(
'django.db.models.fields.TextField'
,
[],
{
'default'
:
"''"
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified_on'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now'
:
'True'
,
'blank'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
,
'db_index'
:
'True'
}),
'student_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'32'
,
'db_index'
:
'True'
})
}
}
complete_apps
=
[
'mentoring'
]
mentoring/models.py
View file @
b3d47cb7
...
...
@@ -49,3 +49,21 @@ class Answer(models.Model):
# Force validation of max_length
self
.
full_clean
()
super
(
Answer
,
self
)
.
save
(
*
args
,
**
kwargs
)
class
LightChild
(
models
.
Model
):
"""
Django model used to store LightChild student data that need to be shared and queried accross
XBlock instances (workaround). Since this is temporary, `data` are stored in json.
"""
class
Meta
:
app_label
=
'mentoring'
unique_together
=
((
'student_id'
,
'course_id'
,
'name'
),)
name
=
models
.
CharField
(
max_length
=
50
,
db_index
=
True
)
student_id
=
models
.
CharField
(
max_length
=
32
,
db_index
=
True
)
course_id
=
models
.
CharField
(
max_length
=
50
,
db_index
=
True
)
student_data
=
models
.
TextField
(
blank
=
True
,
default
=
''
)
created_on
=
models
.
DateTimeField
(
'created on'
,
auto_now_add
=
True
)
modified_on
=
models
.
DateTimeField
(
'modified on'
,
auto_now
=
True
)
mentoring/mrq.py
View file @
b3d47cb7
...
...
@@ -26,7 +26,7 @@
import
logging
from
.light_children
import
List
,
Scope
from
.light_children
import
Integer
,
List
,
Scope
from
.questionnaire
import
QuestionnaireAbstractBlock
from
.utils
import
render_template
...
...
@@ -43,11 +43,21 @@ class MRQBlock(QuestionnaireAbstractBlock):
An XBlock used to ask multiple-response questions
"""
student_choices
=
List
(
help
=
"Last submissions by the student"
,
default
=
[],
scope
=
Scope
.
user_state
)
max_attempts
=
Integer
(
help
=
"Number of max attempts for this questions"
,
scope
=
Scope
.
content
)
num_attempts
=
Integer
(
help
=
"Number of attempts a user has answered for this questions"
,
scope
=
Scope
.
user_state
)
# TODO REMOVE THIS, ONLY NEEDED FOR LIGHTCHILDREN
@classmethod
def
get_fields_to_save
(
cls
):
return
[
'num_attempts'
]
def
submit
(
self
,
submissions
):
log
.
debug
(
u'Received MRQ submissions: "
%
s"'
,
submissions
)
completed
=
True
results
=
[]
for
choice
in
self
.
custom_choices
:
choice_completed
=
True
...
...
@@ -73,12 +83,26 @@ class MRQBlock(QuestionnaireAbstractBlock):
}),
})
self
.
student_choices
=
submissions
self
.
message
=
u'Your answer is correct!'
if
completed
else
u'Your answer is incorrect.'
# Do not increase the counter is the answer is correct
if
not
completed
:
setattr
(
self
,
'num_attempts'
,
self
.
num_attempts
+
1
)
if
self
.
max_attempts
>
0
and
self
.
num_attempts
>=
self
.
max_attempts
:
completed
=
True
self
.
message
+=
u' You have reached the maximum number of attempts for this question. '
\
u'Your next answers won''t be saved. You can check the answer(s) using the "Show Answer(s)" button.'
else
:
self
.
student_choices
=
submissions
result
=
{
'submissions'
:
submissions
,
'completed'
:
completed
,
'choices'
:
results
,
'message'
:
self
.
message
,
'submissions'
:
submissions
,
'completed'
:
completed
,
'choices'
:
results
,
'message'
:
self
.
message
,
'max_attempts'
:
self
.
max_attempts
,
'num_attempts'
:
self
.
num_attempts
}
log
.
debug
(
u'MRQ submissions result:
%
s'
,
result
)
return
result
mentoring/public/css/mentoring.css
View file @
b3d47cb7
...
...
@@ -51,7 +51,7 @@
.mentoring
.progress
.indicator
{
display
:
inline-block
;
margin-top
:
5px
;
vertical-align
:
middle
;
}
.mentoring
.progress
.indicator
.checkmark-correct
{
...
...
mentoring/public/css/questionnaire.css
View file @
b3d47cb7
...
...
@@ -11,21 +11,28 @@
margin
:
10px
0
;
}
.mentoring
.choices
.choice-checkbox
{
display
:
inline-block
;
margin-top
:
5px
;
margin-bottom
:
5px
;
}
.mentoring
.choices
.choice-result
{
padding-right
:
10px
;
display
:
inline-block
;
width
:
40px
;
vertical-align
:
middle
;
cursor
:
pointer
;
}
.mentoring
.choices
.choice-result.correct
{
.mentoring
.choices
.choice-result.correct
,
.choice-answer.correct
{
cursor
:
pointer
;
color
:
#006600
;
position
:
relative
;
top
:
-3px
;
}
.mentoring
.choices
.choice-result.incorrect
{
margin-right
:
10px
;
padding-left
:
10px
;
padding-right
:
10px
;
text-align
:
center
;
color
:
#ff0000
;
}
...
...
@@ -78,3 +85,15 @@
.mentoring
.choices-list
.choice-selector
{
margin-right
:
5px
;
}
.mentoring
.mrq-attempts
{
display
:
inline-block
;
vertical-align
:
baseline
;
color
:
#777
;
font-style
:
italic
;
webkit-font-smoothing
:
antialiased
;
}
.mentoring
.mrq-attempts
div
{
display
:
inline-block
;
}
mentoring/public/js/mentoring.js
View file @
b3d47cb7
...
...
@@ -74,7 +74,7 @@ function MentoringBlock(runtime, element) {
}
function
initXBlock
()
{
var
submit_dom
=
$
(
element
).
find
(
'.submit'
);
var
submit_dom
=
$
(
element
).
find
(
'.submit
.input-main
'
);
submit_dom
.
bind
(
'click'
,
function
()
{
var
data
=
{};
...
...
@@ -89,6 +89,12 @@ function MentoringBlock(runtime, element) {
$
.
post
(
handlerUrl
,
JSON
.
stringify
(
data
)).
success
(
handleSubmitResults
);
});
// init children (especially mrq blocks)
var
children
=
getChildren
(
element
);
_
.
each
(
children
,
function
(
child
)
{
callIfExists
(
child
,
'init'
);
});
if
(
submit_dom
.
length
)
{
renderProgress
();
}
...
...
mentoring/public/js/questionnaire.js
View file @
b3d47cb7
// TODO: Split in two files
var
mrqAttemptsTemplate
=
_
.
template
(
$
(
'#xblock-mrq-attempts'
).
html
());
function
MCQBlock
(
runtime
,
element
)
{
return
{
...
...
@@ -25,7 +26,29 @@ function MCQBlock(runtime, element) {
function
MRQBlock
(
runtime
,
element
)
{
return
{
renderAttempts
:
function
()
{
var
data
=
$
(
'.mrq-attempts'
,
element
).
data
();
$
(
'.mrq-attempts'
,
element
).
html
(
mrqAttemptsTemplate
(
data
));
// bind show answer button
var
showAnswerButton
=
$
(
'button'
,
element
);
if
(
showAnswerButton
.
length
!=
0
)
{
if
(
_
.
isUndefined
(
this
.
answers
))
showAnswerButton
.
hide
();
else
showAnswerButton
.
on
(
'click'
,
_
.
bind
(
this
.
toggleAnswers
,
this
));
}
},
init
:
function
()
{
this
.
renderAttempts
();
},
submit
:
function
()
{
// hide answers
var
choiceInputDOM
=
$
(
'.choice input'
,
element
),
choiceResultDOM
=
$
(
'.choice-answer'
,
choiceInputDOM
.
closest
(
'.choice'
));
choiceResultDOM
.
removeClass
(
'incorrect icon-exclamation correct icon-ok'
);
var
checkedCheckboxes
=
$
(
'input[type=checkbox]:checked'
,
element
),
checkedValues
=
[];
...
...
@@ -58,17 +81,29 @@ function MRQBlock(runtime, element) {
showPopup
(
messageDOM
);
}
var
answers
=
[];
// used in displayAnswers
$
.
each
(
result
.
choices
,
function
(
index
,
choice
)
{
var
choiceInputDOM
=
$
(
'.choice input[value='
+
choice
.
value
+
']'
,
element
),
choiceDOM
=
choiceInputDOM
.
closest
(
'.choice'
),
choiceResultDOM
=
$
(
'.choice-result'
,
choiceDOM
),
choiceAnswerDOM
=
$
(
'.choice-answer'
,
choiceDOM
),
choiceTipsDOM
=
$
(
'.choice-tips'
,
choiceDOM
),
choiceTipsCloseDOM
;
if
(
choice
.
completed
)
{
choiceResultDOM
.
removeClass
(
'incorrect icon-exclamation'
).
addClass
(
'correct icon-ok'
);
}
else
{
choiceResultDOM
.
removeClass
(
'correct icon-ok'
).
addClass
(
'incorrect icon-exclamation'
);
/* update our answers dict */
answers
.
push
({
input
:
choiceInputDOM
,
answer
:
choice
.
completed
?
choiceInputDOM
.
attr
(
'checked'
)
:
!
choiceInputDOM
.
attr
(
'checked'
)
});
choiceResultDOM
.
removeClass
(
'incorrect icon-exclamation correct icon-ok'
);
/* show hint if checked or max_attempts is disabled */
if
(
result
.
completed
||
choiceInputDOM
.
prop
(
'checked'
)
||
result
.
max_attempts
<=
0
)
{
if
(
choice
.
completed
)
{
choiceResultDOM
.
addClass
(
'correct icon-ok'
);
}
else
if
(
!
choice
.
completed
)
{
choiceResultDOM
.
addClass
(
'incorrect icon-exclamation'
);
}
}
choiceTipsDOM
.
html
(
choice
.
tips
);
...
...
@@ -77,7 +112,36 @@ function MRQBlock(runtime, element) {
choiceResultDOM
.
off
(
'click'
).
on
(
'click'
,
function
()
{
showPopup
(
choiceTipsDOM
);
});
choiceAnswerDOM
.
off
(
'click'
).
on
(
'click'
,
function
()
{
showPopup
(
choiceTipsDOM
);
});
});
}
this
.
answers
=
answers
;
$
(
'.mrq-attempts'
,
element
).
data
(
'num_attempts'
,
result
.
num_attempts
);
this
.
renderAttempts
();
},
toggleAnswers
:
function
()
{
var
showAnswerButton
=
$
(
'button span'
,
element
);
var
answers_displayed
=
this
.
answers_displayed
=
!
this
.
answers_displayed
;
_
.
each
(
this
.
answers
,
function
(
answer
)
{
var
choiceResultDOM
=
$
(
'.choice-answer'
,
answer
.
input
.
closest
(
'.choice'
));
choiceResultDOM
.
removeClass
(
'correct icon-ok'
);
if
(
answers_displayed
)
{
if
(
answer
.
answer
)
choiceResultDOM
.
addClass
(
'correct icon-ok'
);
showAnswerButton
.
text
(
'Hide Answer(s)'
);
}
else
{
showAnswerButton
.
text
(
'Show Answer(s)'
);
}
});
}
};
}
mentoring/templates/html/mentoring.html
View file @
b3d47cb7
...
...
@@ -8,7 +8,7 @@
{% endfor %}
{% if self.display_submit %}
<div
class=
"submit"
>
<input
type=
"button"
value=
"Submit"
></input>
<input
type=
"button"
class=
"input-main"
value=
"Submit"
></input>
<span
class=
"progress"
data-completed=
"{{ self.completed }}"
data-attempted=
"{{ self.attempted }}"
>
<span
class=
'indicator'
></span>
</span>
...
...
mentoring/templates/html/mrqblock_attempts.html
0 → 100644
View file @
b3d47cb7
<script
type=
"text/template"
id=
"xblock-mrq-attempts"
>
<%
if
(
_
.
isNumber
(
max_attempts
)
&&
max_attempts
>
0
)
{{
%>
<%
if
(
num_attempts
>=
max_attempts
)
{{
%>
<
button
class
=
"show"
>
<
span
class
=
"show-label"
>
Show
Answer
(
s
)
<
/span
>
<
/button
>
<%
}}
%>
<
div
>
You
have
used
<%=
_
.
min
([
num_attempts
,
max_attempts
])
%>
of
<%=
max_attempts
%>
attempts
for
this
question
.
<
/div
>
<%
}}
%>
</script>
mentoring/templates/html/mrqblock_choices.html
View file @
b3d47cb7
...
...
@@ -5,12 +5,20 @@
{% for choice in custom_choices %}
<div
class=
"choice"
>
<span
class=
"choice-result icon-2x"
></span>
<label
class=
"choice-label"
>
<input
class=
"choice-selector"
type=
"checkbox"
name=
"{{ self.name }}"
value=
"{{ choice.value }}"
{%
if
choice
.
value
in
self
.
student_choices
%}
checked
{%
endif
%}
>
{{ choice.content }}
</label>
<div
class=
"choice-checkbox"
>
<label
class=
"choice-label"
>
<input
class=
"choice-selector"
type=
"checkbox"
name=
"{{ self.name }}"
value=
"{{ choice.value }}"
{%
if
choice
.
value
in
self
.
student_choices
%}
checked
{%
endif
%}
>
{{ choice.content }}
</input>
</label>
</div>
<span
class=
"choice-answer icon-2x"
></span>
<div
class=
"choice-tips"
></div>
</div>
{% endfor %}
<div
class=
"choice-message"
></div>
</div>
</fieldset>
<div
class=
"mrq-attempts"
data-max_attempts=
"{{ self.max_attempts }}"
data-num_attempts=
"{{ self.num_attempts }}"
></div>
mentoring/utils.py
View file @
b3d47cb7
...
...
@@ -105,9 +105,9 @@ class XBlockWithChildrenFragmentsMixin(object):
and a list of fragments, one per children
- `view_name` allows to select a specific view method on the children
- `instance_of` allows to only return fragments for children which are instances of
- `instance_of` allows to only return fragments for children which are instances of
the provided class
- `not_instance_of` allows to only return fragments for children which are *NOT*
- `not_instance_of` allows to only return fragments for children which are *NOT*
instances of the provided class
"""
fragment
=
Fragment
()
...
...
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