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
9a8c20db
Commit
9a8c20db
authored
Apr 07, 2014
by
Xavier Antoviaque
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #12 from aboudreault/global-max-attempts
Global max attempts
parents
cb899cb7
e4831015
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
100 additions
and
88 deletions
+100
-88
mentoring/mentoring.py
+12
-2
mentoring/mrq.py
+7
-30
mentoring/public/css/mentoring.css
+9
-0
mentoring/public/css/questionnaire.css
+2
-10
mentoring/public/js/mentoring.js
+40
-13
mentoring/public/js/questionnaire.js
+19
-20
mentoring/templates/html/mentoring.html
+1
-0
mentoring/templates/html/mentoring_attempts.html
+5
-0
mentoring/templates/html/mrqblock_attempts.html
+0
-12
mentoring/templates/html/mrqblock_choices.html
+5
-1
No files found.
mentoring/mentoring.py
View file @
9a8c20db
...
...
@@ -30,7 +30,7 @@ from lxml import etree
from
StringIO
import
StringIO
from
xblock.core
import
XBlock
from
xblock.fields
import
Boolean
,
Scope
,
String
,
Float
from
xblock.fields
import
Boolean
,
Scope
,
String
,
Integer
,
Float
from
xblock.fragment
import
Fragment
from
.light_children
import
XBlockWithLightChildren
...
...
@@ -69,6 +69,10 @@ class MentoringBlock(XBlockWithLightChildren):
display_submit
=
Boolean
(
help
=
"Allow to submit current block?"
,
default
=
True
,
scope
=
Scope
.
content
)
xml_content
=
String
(
help
=
"XML content"
,
default
=
''
,
scope
=
Scope
.
content
)
weight
=
Float
(
help
=
"Defines the maximum total grade of the block."
,
default
=
0
,
scope
=
Scope
.
content
)
num_attempts
=
Integer
(
help
=
"Number of attempts a user has answered for this questions"
,
default
=
0
,
scope
=
Scope
.
user_state
)
max_attempts
=
Integer
(
help
=
"Number of max attempts for this questions"
,
default
=
0
,
scope
=
Scope
.
content
)
icon_class
=
'problem'
has_score
=
True
...
...
@@ -86,7 +90,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/m
rqblock
_attempts.html'
),
"text/html"
)
fragment
.
add_resource
(
load_resource
(
'templates/html/m
entoring
_attempts.html'
),
"text/html"
)
fragment
.
initialize_js
(
'MentoringBlock'
)
...
...
@@ -145,11 +149,17 @@ class MentoringBlock(XBlockWithLightChildren):
})
self
.
completed
=
bool
(
completed
)
if
not
self
.
completed
and
self
.
max_attempts
>
0
:
self
.
num_attempts
+=
1
return
{
'submitResults'
:
submit_results
,
'completed'
:
self
.
completed
,
'attempted'
:
self
.
attempted
,
'message'
:
message
,
'max_attempts'
:
self
.
max_attempts
,
'num_attempts'
:
self
.
num_attempts
}
def
get_message_fragment
(
self
,
message_type
):
...
...
mentoring/mrq.py
View file @
9a8c20db
...
...
@@ -26,7 +26,7 @@
import
logging
from
.light_children
import
Integer
,
List
,
Scope
from
.light_children
import
List
,
Scope
from
.questionnaire
import
QuestionnaireAbstractBlock
from
.utils
import
render_template
...
...
@@ -43,17 +43,6 @@ 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"
,
default
=
0
,
scope
=
Scope
.
content
)
num_attempts
=
Integer
(
help
=
"Number of attempts a user has answered for this questions"
,
default
=
0
,
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
)
...
...
@@ -85,26 +74,14 @@ class MRQBlock(QuestionnaireAbstractBlock):
}),
})
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
self
.
student_choices
=
submissions
result
=
{
'submissions'
:
submissions
,
'completed'
:
completed
,
'choices'
:
results
,
'message'
:
self
.
message
,
'max_attempts'
:
self
.
max_attempts
,
'num_attempts'
:
self
.
num_attempts
,
'score'
:
sum
(
1.0
for
r
in
results
if
r
[
'completed'
])
/
len
(
results
),
'submissions'
:
submissions
,
'completed'
:
completed
,
'choices'
:
results
,
'message'
:
self
.
message
,
'score'
:
sum
(
1.0
for
r
in
results
if
r
[
'completed'
])
/
len
(
results
)
}
log
.
debug
(
u'MRQ submissions result:
%
s'
,
result
)
...
...
mentoring/public/css/mentoring.css
View file @
9a8c20db
...
...
@@ -61,3 +61,12 @@
.mentoring
.progress
.indicator
.checkmark-incorrect
{
color
:
#ff0000
;
}
.mentoring
.attempts
{
margin-top
:
20px
;
display
:
inline-block
;
vertical-align
:
baseline
;
color
:
#777
;
font-style
:
italic
;
webkit-font-smoothing
:
antialiased
;
}
mentoring/public/css/questionnaire.css
View file @
9a8c20db
...
...
@@ -82,14 +82,6 @@
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
.show-answer
{
display
:
none
;
}
mentoring/public/js/mentoring.js
View file @
9a8c20db
function
MentoringBlock
(
runtime
,
element
)
{
var
progressTemplate
=
_
.
template
(
$
(
'#xblock-progress-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.
function
renderProgress
()
{
var
data
=
$
(
'.progress'
,
element
).
data
();
$
(
'.indicator'
,
element
).
html
(
progressTemplate
(
data
));
}
function
renderAttempts
()
{
var
data
=
$
(
'.attempts'
,
element
).
data
();
$
(
'.attempts'
,
element
).
html
(
attemptsTemplate
(
data
));
}
function
renderDependency
()
{
var
warning_dom
=
$
(
'.missing-dependency'
,
element
),
data
=
warning_dom
.
data
();
...
...
@@ -31,24 +38,37 @@ function MentoringBlock(runtime, element) {
var
input
=
submitResult
[
0
],
result
=
submitResult
[
1
],
child
=
getChildByName
(
element
,
input
);
callIfExists
(
child
,
'handleSubmit'
,
result
);
var
options
=
{
max_attempts
:
results
.
max_attempts
,
num_attempts
:
results
.
num_attempts
}
callIfExists
(
child
,
'handleSubmit'
,
result
,
options
);
});
$
(
'.progress'
,
element
).
data
(
'completed'
,
results
.
completed
?
'True'
:
'False'
);
$
(
'.progress'
,
element
).
data
(
'attempted'
,
results
.
attempted
?
'True'
:
'False'
);
renderProgress
();
$
(
'.attempts'
,
element
).
data
(
'max_attempts'
,
results
.
max_attempts
);
$
(
'.attempts'
,
element
).
data
(
'num_attempts'
,
results
.
num_attempts
);
renderAttempts
();
// Messages should only be displayed upon hitting 'submit', not on page reload
messages_dom
.
append
(
results
.
message
);
if
(
messages_dom
.
html
().
trim
())
{
messages_dom
.
prepend
(
'<div class="title1">Feedback</div>'
);
messages_dom
.
show
();
}
validateXBlock
();
}
function
getChildren
(
element
)
{
var
children_dom
=
$
(
'.xblock-light-child'
,
element
),
children
=
[];
if
(
!
_
.
isUndefined
(
children
))
return
children
;
var
children_dom
=
$
(
'.xblock-light-child'
,
element
);
children
=
[];
$
.
each
(
children_dom
,
function
(
index
,
child_dom
)
{
var
child_type
=
$
(
child_dom
).
attr
(
'data-type'
),
...
...
@@ -99,13 +119,15 @@ function MentoringBlock(runtime, element) {
callIfExists
(
child
,
'init'
,
options
);
});
validateXBlock
();
if
(
submit_dom
.
length
)
{
renderProgress
();
}
renderAttempts
();
renderDependency
();
validateXBlock
();
}
function
handleRefreshResults
(
results
)
{
...
...
@@ -121,21 +143,26 @@ function MentoringBlock(runtime, element) {
// validate all children
function
validateXBlock
()
{
var
submit_dom
=
$
(
element
).
find
(
'.submit .input-main'
);
var
children_are
_valid
=
true
;
var
data
=
{}
;
var
is
_valid
=
true
;
var
data
=
$
(
'.attempts'
,
element
).
data
()
;
var
children
=
getChildren
(
element
);
for
(
var
i
=
0
;
i
<
children
.
length
;
i
++
)
{
var
child
=
children
[
i
];
if
(
child
.
name
!==
undefined
)
{
var
child_validation
=
callIfExists
(
child
,
'validate'
);
if
(
_
.
isBoolean
(
child_validation
))
{
children_are_valid
=
children_are_valid
&&
child_validation
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
.
name
!==
undefined
)
{
var
child_validation
=
callIfExists
(
child
,
'validate'
);
if
(
_
.
isBoolean
(
child_validation
))
{
is_valid
=
is_valid
&&
child_validation
;
}
}
}
}
if
(
!
children_are
_valid
)
{
if
(
!
is
_valid
)
{
submit_dom
.
attr
(
'disabled'
,
'disabled'
);
}
else
{
...
...
mentoring/public/js/questionnaire.js
View file @
9a8c20db
...
...
@@ -91,23 +91,11 @@ function MCQBlock(runtime, element) {
}
function
MRQBlock
(
runtime
,
element
)
{
var
mrqAttemptsTemplate
=
_
.
template
(
$
(
'#xblock-mrq-attempts'
).
html
());
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
();
var
answerButton
=
$
(
'button'
,
element
);
answerButton
.
on
(
'click'
,
_
.
bind
(
this
.
toggleAnswers
,
this
));
this
.
showAnswerButton
();
},
submit
:
function
()
{
...
...
@@ -125,7 +113,7 @@ function MRQBlock(runtime, element) {
return
checkedValues
;
},
handleSubmit
:
function
(
result
)
{
handleSubmit
:
function
(
result
,
options
)
{
var
messageView
=
MessageView
(
element
);
if
(
result
.
message
)
{
...
...
@@ -149,8 +137,8 @@ function MRQBlock(runtime, element) {
});
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
)
{
/* show hint if checked or max_attempts is disabled */
if
(
result
.
completed
||
choiceInputDOM
.
prop
(
'checked'
)
||
options
.
max_attempts
<=
0
)
{
if
(
choice
.
completed
)
{
choiceResultDOM
.
addClass
(
'correct icon-ok'
);
}
else
if
(
!
choice
.
completed
)
{
...
...
@@ -170,10 +158,21 @@ function MRQBlock(runtime, element) {
});
});
this
.
answers
=
answers
;
this
.
showAnswerButton
(
options
);
},
showAnswerButton
:
function
(
options
)
{
var
button
=
$
(
'.show-answer'
,
element
);
var
is_enabled
=
options
&&
(
options
.
num_attempts
>=
options
.
max_attempts
);
$
(
'.mrq-attempts'
,
element
).
data
(
'num_attempts'
,
result
.
num_attempts
);
this
.
renderAttempts
();
if
(
is_enabled
&&
_
.
isArray
(
this
.
answers
))
{
button
.
show
();
}
else
{
button
.
hide
();
}
},
toggleAnswers
:
function
()
{
...
...
mentoring/templates/html/mentoring.html
View file @
9a8c20db
...
...
@@ -7,6 +7,7 @@
{{c.body_html|safe}}
{% endfor %}
{% if self.display_submit %}
<div
class=
"attempts"
data-max_attempts=
"{{ self.max_attempts }}"
data-num_attempts=
"{{ self.num_attempts }}"
></div>
<div
class=
"submit"
>
<input
type=
"button"
class=
"input-main"
value=
"Submit"
></input>
<span
class=
"progress"
data-completed=
"{{ self.completed }}"
data-attempted=
"{{ self.attempted }}"
>
...
...
mentoring/templates/html/mentoring_attempts.html
0 → 100644
View file @
9a8c20db
<script
type=
"text/template"
id=
"xblock-attempts-template"
>
<%
if
(
_
.
isNumber
(
max_attempts
)
&&
max_attempts
>
0
)
{{
%>
<
div
>
You
have
used
<%=
_
.
min
([
num_attempts
,
max_attempts
])
%>
of
<%=
max_attempts
%>
attempts
.
<
/div
>
<%
}}
%>
</script>
mentoring/templates/html/mrqblock_attempts.html
deleted
100644 → 0
View file @
cb899cb7
<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 @
9a8c20db
...
...
@@ -21,4 +21,8 @@
<div
class=
"choice-message"
></div>
</div>
</fieldset>
<div
class=
"mrq-attempts"
data-max_attempts=
"{{ self.max_attempts }}"
data-num_attempts=
"{{ self.num_attempts }}"
></div>
<div
class=
"show-answer"
>
<button
class=
"show"
>
<span
class=
"show-label"
>
Show Answer(s)
</span>
</button>
</div>
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