Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
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
edx
edx-platform
Commits
74bb976e
Commit
74bb976e
authored
Jun 24, 2013
by
Julian Arni
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Abort submission and alter user if gradefn throws an exception
parent
bc25defd
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
77 additions
and
35 deletions
+77
-35
common/lib/xmodule/xmodule/js/src/capa/display.coffee
+26
-5
common/static/js/capa/jsinput.js
+46
-30
doc/public/course_data_formats/jsinput.rst
+5
-0
No files found.
common/lib/xmodule/xmodule/js/src/capa/display.coffee
View file @
74bb976e
...
@@ -129,6 +129,30 @@ class @Problem
...
@@ -129,6 +129,30 @@ class @Problem
if
setupMethod
?
if
setupMethod
?
@
inputtypeDisplays
[
id
]
=
setupMethod
(
inputtype
)
@
inputtypeDisplays
[
id
]
=
setupMethod
(
inputtype
)
# If some function wants to be called before sending the answer to the
# server, give it a chance to do so.
#
# check_waitfor allows the callee to send alerts if the user's input is
# invalid. To do so, the callee must throw an exception named "Waitfor
# Exception". This and any other errors or exceptions that arise from the
# callee are rethrown and abort the submission.
#
# In order to use this feature, add a 'data-waitfor' attribute to the input,
# and specify the function to be called by the check button before sending
# off @answers
check_waitfor
:
=>
for
inp
in
@
inputs
if
not
(
$
(
inp
).
attr
(
"data-waitfor"
)
?
)
try
$
(
inp
).
data
(
"waitfor"
)()
catch
e
if
e
.
name
==
"Waitfor Exception"
alert
e
.
message
else
alert
"Could not grade your answer. The submission was aborted."
throw
e
@
refreshAnswers
()
###
###
# 'check_fd' uses FormData to allow file submissions in the 'problem_check' dispatch,
# 'check_fd' uses FormData to allow file submissions in the 'problem_check' dispatch,
...
@@ -140,11 +164,7 @@ class @Problem
...
@@ -140,11 +164,7 @@ class @Problem
check_fd
:
=>
check_fd
:
=>
Logger
.
log
'problem_check'
,
@
answers
Logger
.
log
'problem_check'
,
@
answers
# If some function wants to be called before sending the answer to the
# server, give it a chance to do so.
if
$
(
'input[waitfor]'
).
length
!=
0
(
$
(
lcall
).
data
(
"waitfor"
).
call
()
for
lcall
in
$
(
'input[waitfor]'
))
@
refreshAnswers
()
# If there are no file inputs in the problem, we can fall back on @check
# If there are no file inputs in the problem, we can fall back on @check
if
$
(
'input:file'
).
length
==
0
if
$
(
'input:file'
).
length
==
0
@
check
()
@
check
()
...
@@ -217,6 +237,7 @@ class @Problem
...
@@ -217,6 +237,7 @@ class @Problem
$
.
ajaxWithPrefix
(
"
#{
@
url
}
/problem_check"
,
settings
)
$
.
ajaxWithPrefix
(
"
#{
@
url
}
/problem_check"
,
settings
)
check
:
=>
check
:
=>
@
check_waitfor
()
Logger
.
log
'problem_check'
,
@
answers
Logger
.
log
'problem_check'
,
@
answers
$
.
postWithPrefix
"
#{
@
url
}
/problem_check"
,
@
answers
,
(
response
)
=>
$
.
postWithPrefix
"
#{
@
url
}
/problem_check"
,
@
answers
,
(
response
)
=>
switch
response
.
success
switch
response
.
success
...
...
common/static/js/capa/jsinput.js
View file @
74bb976e
...
@@ -10,16 +10,15 @@
...
@@ -10,16 +10,15 @@
// Use this array to keep track of the elements that have already been
// Use this array to keep track of the elements that have already been
// initialized.
// initialized.
jsinput
.
jsinputarr
=
jsinput
.
jsinputarr
||
[];
jsinput
.
jsinputarr
=
jsinput
.
jsinputarr
||
[];
if
(
isFirst
)
{
jsinput
.
jsinputarr
.
exists
=
function
(
id
)
{
jsinput
.
jsinputarr
.
exists
=
function
(
id
)
{
this
.
filter
(
function
(
e
,
i
,
a
)
{
this
.
filter
(
function
(
e
,
i
,
a
)
{
return
e
.
id
=
id
;
return
e
.
id
=
id
;
});
});
};
};
}
function
jsinputConstructor
(
spec
)
{
function
jsinputConstructor
(
spec
)
{
// Define an class that will be instantiated for each
.
jsinput element
// Define an class that will be instantiated for each
jsinput element
// of the DOM
// of the DOM
// 'that' is the object returned by the constructor. It has a single
// 'that' is the object returned by the constructor. It has a single
...
@@ -35,6 +34,11 @@
...
@@ -35,6 +34,11 @@
return
parent
.
find
(
'input[id^="input_"]'
);
return
parent
.
find
(
'input[id^="input_"]'
);
}
}
// For the state and grade functions below, use functions instead of
// storing their return values since we might need to call them
// repeatedly, and they might change (e.g., they might not be defined
// when we first try calling them).
// Get the grade function name
// Get the grade function name
function
getgradefn
()
{
function
getgradefn
()
{
return
$
(
sect
).
attr
(
"data"
);
return
$
(
sect
).
attr
(
"data"
);
...
@@ -54,24 +58,24 @@
...
@@ -54,24 +58,24 @@
return
$
(
sect
).
attr
(
"data-stored"
);
return
$
(
sect
).
attr
(
"data-stored"
);
}
}
var
thisIFrame
=
$
(
spec
.
elem
).
find
(
'iframe[name^="iframe_"]'
).
get
(
0
);
var
cWindow
=
thisIFrame
.
contentWindow
;
// Put the return value of gradefn in the hidden inputfield.
// Put the return value of gradefn in the hidden inputfield.
// If passed an argument, does not call gradefn, and instead directly
// If passed an argument, does not call gradefn, and instead directly
// updates the inputfield with the passed value.
// updates the inputfield with the passed value.
var
update
=
function
(
answer
)
{
var
update
=
function
(
answer
)
{
var
ans
;
var
ans
;
ans
=
$
(
spec
.
elem
).
ans
=
cWindow
[
gradefn
]();
find
(
'iframe[name^="iframe_"]'
).
get
(
0
).
// jquery might not be available in the iframe
contentWindow
[
gradefn
]();
// setstate presumes getstate, so don't getstate unless setstate is
// setstate presumes getstate, so don't getstate unless setstate is
// defined.
// defined.
if
(
getgetstate
()
&&
getsetstate
())
{
if
(
getgetstate
()
&&
getsetstate
())
{
var
state
,
store
;
var
state
,
store
;
state
=
$
(
spec
.
elem
).
state
=
cWindow
[
getgetstate
()]();
find
(
'iframe[name^="iframe_"]'
).
get
(
0
).
contentWindow
[
getgetstate
()]();
store
=
{
store
=
{
answer
:
ans
,
answer
:
ans
,
state
:
state
state
:
state
...
@@ -91,8 +95,6 @@
...
@@ -91,8 +95,6 @@
$
(
updatebutton
).
click
(
update
);
$
(
updatebutton
).
click
(
update
);
}
}
/* Public methods */
/* Public methods */
that
.
update
=
update
;
that
.
update
=
update
;
...
@@ -116,19 +118,30 @@
...
@@ -116,19 +118,30 @@
updateHandler
();
updateHandler
();
bindCheck
();
bindCheck
();
// Check whether application takes in state and there is a saved
// Check whether application takes in state and there is a saved
// state to give it
// state to give it. If getsetstate is specified but calling it
// fails, wait and try again, since the iframe might still be
// loading.
if
(
getsetstate
()
&&
getstoredstate
())
{
if
(
getsetstate
()
&&
getstoredstate
())
{
console
.
log
(
"Using stored state..."
);
var
sval
;
var
sval
;
if
(
typeof
(
getstoredstate
())
===
"object"
)
{
if
(
typeof
(
getstoredstate
())
===
"object"
)
{
sval
=
getstoredstate
()[
"state"
];
sval
=
getstoredstate
()[
"state"
];
}
else
{
}
else
{
sval
=
getstoredstate
();
sval
=
getstoredstate
();
}
}
$
(
spec
.
elem
).
function
whileloop
(
n
)
{
find
(
'iframe[name^="iframe_"]'
).
if
(
n
<
10
){
get
(
0
).
try
{
contentWindow
[
getsetstate
()](
sval
);
cWindow
[
getsetstate
()](
sval
);
}
catch
(
err
)
{
setTimeout
(
whileloop
(
n
+
1
),
200
);
}
}
else
{
console
.
log
(
"Error: could not set state"
);
}
}
whileloop
(
0
);
}
}
}
else
{
}
else
{
// NOT CURRENTLY SUPPORTED
// NOT CURRENTLY SUPPORTED
...
@@ -171,11 +184,13 @@
...
@@ -171,11 +184,13 @@
all
.
each
(
function
()
{
all
.
each
(
function
()
{
// Get just the mako variable 'id' from the id attribute
// Get just the mako variable 'id' from the id attribute
newid
=
$
(
this
).
attr
(
"id"
).
replace
(
/^inputtype_/
,
""
);
newid
=
$
(
this
).
attr
(
"id"
).
replace
(
/^inputtype_/
,
""
);
var
newJsElem
=
jsinputConstructor
({
if
(
!
jsinput
.
jsinputarr
.
exists
(
newid
)){
id
:
newid
,
var
newJsElem
=
jsinputConstructor
({
elem
:
this
,
id
:
newid
,
passive
:
false
elem
:
this
,
});
passive
:
false
});
}
});
});
}
}
...
@@ -193,5 +208,6 @@
...
@@ -193,5 +208,6 @@
//}
//}
//};
//};
setTimeout
(
walkDOM
,
200
);
setTimeout
(
walkDOM
,
100
);
})(
window
.
jsinput
=
window
.
jsinput
||
{})
})(
window
.
jsinput
=
window
.
jsinput
||
{})
doc/public/course_data_formats/jsinput.rst
View file @
74bb976e
...
@@ -87,6 +87,11 @@ attributes are also used) be passed as a string to the enclosing response type.
...
@@ -87,6 +87,11 @@ attributes are also used) be passed as a string to the enclosing response type.
In the customresponse example above, this means cfn will be passed this answer
In the customresponse example above, this means cfn will be passed this answer
as `ans`.
as `ans`.
If the `gradefn` function throws an exception when a student attempts to
submit a problem, the submission is aborted, and the student receives a generic
alert. The alert can be customised by making the exception name `Waitfor
Exception`; in that case, the alert message will be the exception message.
**IMPORTANT** : the `gradefn` function should not be at all asynchronous, since
**IMPORTANT** : the `gradefn` function should not be at all asynchronous, since
this could result in the student's latest answer not being passed correctly.
this could result in the student's latest answer not being passed correctly.
Moreover, the function should also return promptly, since currently the student
Moreover, the function should also return promptly, since currently the student
...
...
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