Merge pull request #14134 from edx/christina/jsinput-newtemplate
New jsinput example problem
This commit is contained in:
@@ -1,28 +0,0 @@
|
||||
require('coffee-script');
|
||||
var importAll = function(modulePath) {
|
||||
module = require(modulePath);
|
||||
for (key in module) {
|
||||
global[key] = module[key];
|
||||
}
|
||||
};
|
||||
|
||||
importAll('mersenne-twister-min');
|
||||
importAll('xproblem');
|
||||
|
||||
generatorModulePath = process.argv[2];
|
||||
dependencies = JSON.parse(process.argv[3]);
|
||||
seed = JSON.parse(process.argv[4]);
|
||||
params = JSON.parse(process.argv[5]);
|
||||
|
||||
if (seed == null) {
|
||||
seed = 4;
|
||||
}
|
||||
|
||||
for (var i = 0; i < dependencies.length; i++) {
|
||||
importAll(dependencies[i]);
|
||||
}
|
||||
|
||||
generatorModule = require(generatorModulePath);
|
||||
generatorClass = generatorModule.generatorClass;
|
||||
generator = new generatorClass(seed, params);
|
||||
console.log(JSON.stringify(generator.generate()));
|
||||
@@ -1,26 +0,0 @@
|
||||
require('coffee-script');
|
||||
var importAll = function(modulePath) {
|
||||
module = require(modulePath);
|
||||
for (key in module) {
|
||||
global[key] = module[key];
|
||||
}
|
||||
};
|
||||
|
||||
importAll('xproblem');
|
||||
|
||||
graderModulePath = process.argv[2];
|
||||
dependencies = JSON.parse(process.argv[3]);
|
||||
submission = JSON.parse(process.argv[4]);
|
||||
problemState = JSON.parse(process.argv[5]);
|
||||
params = JSON.parse(process.argv[6]);
|
||||
|
||||
for (var i = 0; i < dependencies.length; i++) {
|
||||
importAll(dependencies[i]);
|
||||
}
|
||||
|
||||
graderModule = require(graderModulePath);
|
||||
graderClass = graderModule.graderClass;
|
||||
grader = new graderClass(submission, problemState, params);
|
||||
console.log(JSON.stringify(grader.grade()));
|
||||
console.log(JSON.stringify(grader.evaluation));
|
||||
console.log(JSON.stringify(grader.solution));
|
||||
@@ -11,7 +11,7 @@
|
||||
% endif
|
||||
/>
|
||||
|
||||
<p class="status">
|
||||
<p class="indicator-container">
|
||||
${value|h}
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
</p>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
style="display:none;"/>
|
||||
|
||||
|
||||
<p class="status drag-and-drop--status" aria-describedby="input_${id}">
|
||||
<p class="indicator-container drag-and-drop--status" aria-describedby="input_${id}">
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<input type="hidden" name="genex_problem_number" id="genex_problem_number" value ="${genex_problem_number}"></input>
|
||||
<input type="hidden" name="input_${id}" aria-describedby="answer_${id}" id="input_${id}" value="${value|h}"/>
|
||||
|
||||
<p class="status" aria-describedby="input_${id}">
|
||||
<p class="indicator-container" aria-describedby="input_${id}">
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<p id="answer_${id}" class="answer"></p>
|
||||
|
||||
<p class="status" aria-describedby="input_${id}">
|
||||
<p class="indicator-container" aria-describedby="input_${id}">
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -43,9 +43,9 @@
|
||||
<br/>
|
||||
<p id="answer_${id}" class="answer"></p>
|
||||
|
||||
<p class="status">
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
</p>
|
||||
<div class="indicator-container">
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
</div>
|
||||
|
||||
<div class="error_message" style="padding: 5px 5px 5px 5px; background-color:#FA6666; height:60px;width:400px; display: none"></div>
|
||||
|
||||
|
||||
@@ -389,64 +389,31 @@ div.problem {
|
||||
}
|
||||
}
|
||||
|
||||
.unanswered {
|
||||
p.status.drag-and-drop--status {
|
||||
@include margin(8px, 0, 0, ($baseline/2));
|
||||
text-indent: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&.correct, &.ui-icon-check {
|
||||
p.status {
|
||||
@include status-icon($correct, $checkmark-icon);
|
||||
}
|
||||
|
||||
input {
|
||||
border-color: $correct;
|
||||
}
|
||||
}
|
||||
|
||||
&.partially-correct, &.ui-icon-check {
|
||||
p.status {
|
||||
@include status-icon($partially-correct, $asterisk-icon);
|
||||
}
|
||||
|
||||
input {
|
||||
border-color: $partially-correct;
|
||||
}
|
||||
}
|
||||
|
||||
&.processing {
|
||||
p.status {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: url('#{$static-path}/images/spinner.gif') center center no-repeat;
|
||||
}
|
||||
|
||||
input {
|
||||
border-color: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-icon-close {
|
||||
p.status {
|
||||
@include status-icon($incorrect, $cross-icon);
|
||||
}
|
||||
|
||||
input {
|
||||
border-color: $incorrect;
|
||||
}
|
||||
}
|
||||
|
||||
&.incorrect, &.incomplete {
|
||||
|
||||
p.status {
|
||||
@include status-icon($incorrect, $cross-icon);
|
||||
}
|
||||
|
||||
input {
|
||||
border-color: $incorrect;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
metadata:
|
||||
display_name: Custom Javascript Display and Grading
|
||||
display_name: Custom JavaScript Display and Grading
|
||||
markdown: !!null
|
||||
showanswer: never
|
||||
data: |
|
||||
@@ -8,8 +8,8 @@ data: |
|
||||
<p>
|
||||
In these problems (also called custom JavaScript problems or JS Input
|
||||
problems), you add a problem or tool that uses JavaScript in Studio.
|
||||
Studio embeds the problem in an IFrame so that your students can
|
||||
interact with it in the LMS. You can grade your students' work using
|
||||
Studio embeds the problem in an IFrame so that your learners can
|
||||
interact with it in the LMS. You can grade your learners' work using
|
||||
JavaScript and some basic Python, and the grading is integrated into the
|
||||
edX grading system.
|
||||
</p>
|
||||
@@ -31,42 +31,47 @@ data: |
|
||||
<p>
|
||||
When you add the problem, be sure to select <strong>Settings</strong>
|
||||
to specify a <strong>Display Name</strong> and other values that apply.
|
||||
Also, be sure to specify a <strong>title</strong> attribute on the <strong>jsinput</strong> tag;
|
||||
this title is used for the title attribute on the generated IFrame. Generally,
|
||||
the title attribute on the IFrame should match the title tag of the HTML hosted
|
||||
within the IFrame, which is specified by the <strong>html_file</strong> attribute.
|
||||
</p>
|
||||
<p>You can use the following example problem as a model.</p>
|
||||
|
||||
<customresponse cfn="vglcfn">
|
||||
<customresponse cfn="check_function">
|
||||
<script type="loncapa/python">
|
||||
<![CDATA[
|
||||
import json
|
||||
def vglcfn(e, ans):
|
||||
'''
|
||||
par is a dictionary that contains two keys, "answer" and "state".
|
||||
def check_function(e, ans):
|
||||
"""
|
||||
"response" is a dictionary that contains two keys, "answer" and "state".
|
||||
The value of "answer" is the JSON string that "getGrade" returns.
|
||||
The value of "state" is the JSON string that "getState" returns.
|
||||
Clicking either "Submit" or "Save" registers the current state.
|
||||
"""
|
||||
response = json.loads(ans)
|
||||
|
||||
'''
|
||||
par = json.loads(ans)
|
||||
# You can use the value of the answer key to grade:
|
||||
answer = json.loads(par["answer"])
|
||||
return answer["cylinder"] and not answer["cube"]
|
||||
'''
|
||||
answer = json.loads(response["answer"])
|
||||
return answer == "correct"
|
||||
|
||||
# Or you can use the value of the state key to grade:
|
||||
state = json.loads(par["state"])
|
||||
selectedObjects = state["selectedObjects"]
|
||||
return selectedObjects["cylinder"] and not selectedObjects["cube"]
|
||||
'''
|
||||
"""
|
||||
state = json.loads(response["state"])
|
||||
return state["selectedChoice"] == "correct"
|
||||
"""
|
||||
]]>
|
||||
</script>
|
||||
<p>In the following image, click the objects until the cone is yellow and the cube is blue.</p>
|
||||
<jsinput gradefn="WebGLDemo.getGrade"
|
||||
get_statefn="WebGLDemo.getState"
|
||||
set_statefn="WebGLDemo.setState"
|
||||
initial_state='{"selectedObjects":{"cube":true,"cylinder":false}}'
|
||||
width="400"
|
||||
height="400"
|
||||
html_file="https://studio.edx.org/c4x/edX/DemoX/asset/webGLDemo.html"
|
||||
title="Spinning Cone and Cube"
|
||||
<p>This is paragraph text displayed before the IFrame.</p>
|
||||
<jsinput
|
||||
gradefn="JSInputDemo.getGrade"
|
||||
get_statefn="JSInputDemo.getState"
|
||||
set_statefn="JSInputDemo.setState"
|
||||
initial_state='{"selectedChoice": "incorrect1", "availableChoices": ["incorrect1", "correct", "incorrect2"]}'
|
||||
width="600"
|
||||
height="100"
|
||||
html_file="https://files.edx.org/custom-js-example/jsinput_example.html"
|
||||
title="Dropdown with Dynamic Text"
|
||||
sop="false"/>
|
||||
</customresponse>
|
||||
</problem>
|
||||
|
||||
9
common/static/js/capa/jsinput/jsinput_example.css
Normal file
9
common/static/js/capa/jsinput/jsinput_example.css
Normal file
@@ -0,0 +1,9 @@
|
||||
.directions {
|
||||
font-size: large
|
||||
}
|
||||
|
||||
.feedback {
|
||||
font-size: medium;
|
||||
border: 2px solid cornflowerblue;
|
||||
padding: 5px;
|
||||
}
|
||||
15
common/static/js/capa/jsinput/jsinput_example.html
Normal file
15
common/static/js/capa/jsinput/jsinput_example.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Dropdown with Dynamic Text</title>
|
||||
<link rel="stylesheet" type="text/css" href="https://files.edx.org/custom-js-example/jsinput_example.css">
|
||||
</head>
|
||||
<body>
|
||||
<script src="https://files.edx.org/custom-js-example/jschannel.js"></script>
|
||||
<script src="https://files.edx.org/custom-js-example/jsinput_example.js" defer></script>
|
||||
<label class="directions">Select an option from the list:
|
||||
<select class="choices"></select>
|
||||
</label>
|
||||
<p aria-live="polite" class="feedback"></p>
|
||||
</body>
|
||||
</html>
|
||||
86
common/static/js/capa/jsinput/jsinput_example.js
Normal file
86
common/static/js/capa/jsinput/jsinput_example.js
Normal file
@@ -0,0 +1,86 @@
|
||||
/* globals Channel */
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// state will be populated via initial_state via the `setState` method. Defining dummy values here
|
||||
// to make the expected structure clear.
|
||||
var state = {
|
||||
availableChoices: [],
|
||||
selectedChoice: ''
|
||||
},
|
||||
channel,
|
||||
select = document.getElementsByClassName('choices')[0],
|
||||
feedback = document.getElementsByClassName('feedback')[0];
|
||||
|
||||
function populateSelect() {
|
||||
// Populate the select from `state.availableChoices`.
|
||||
var i, option;
|
||||
|
||||
// Clear out any pre-existing options.
|
||||
while (select.firstChild) {
|
||||
select.removeChild(select.firstChild);
|
||||
}
|
||||
|
||||
// Populate the select with the available choices.
|
||||
for (i = 0; i < state.availableChoices.length; i++) {
|
||||
option = document.createElement('option');
|
||||
option.value = i;
|
||||
option.innerHTML = state.availableChoices[i];
|
||||
if (state.availableChoices[i] === state.selectedChoice) {
|
||||
option.selected = true;
|
||||
}
|
||||
select.appendChild(option);
|
||||
}
|
||||
feedback.innerText = "The currently selected answer is '" + state.selectedChoice + "'.";
|
||||
}
|
||||
|
||||
function getGrade() {
|
||||
// The following return value may or may not be used to grade server-side.
|
||||
// If getState and setState are used, then the Python grader also gets access
|
||||
// to the return value of getState and can choose it instead to grade.
|
||||
return JSON.stringify(state.selectedChoice);
|
||||
}
|
||||
|
||||
function getState() {
|
||||
// Returns the current state (which can be used for grading).
|
||||
return JSON.stringify(state);
|
||||
}
|
||||
|
||||
// This function will be called with 1 argument when JSChannel is not used,
|
||||
// 2 otherwise. In the latter case, the first argument is a transaction
|
||||
// object that will not be used here
|
||||
// (see http://mozilla.github.io/jschannel/docs/)
|
||||
function setState() {
|
||||
var stateString = arguments.length === 1 ? arguments[0] : arguments[1];
|
||||
state = JSON.parse(stateString);
|
||||
populateSelect();
|
||||
}
|
||||
|
||||
// Establish a channel only if this application is embedded in an iframe.
|
||||
// This will let the parent window communicate with this application using
|
||||
// RPC and bypass SOP restrictions.
|
||||
if (window.parent !== window) {
|
||||
channel = Channel.build({
|
||||
window: window.parent,
|
||||
origin: '*',
|
||||
scope: 'JSInput'
|
||||
});
|
||||
|
||||
channel.bind('getGrade', getGrade);
|
||||
channel.bind('getState', getState);
|
||||
channel.bind('setState', setState);
|
||||
}
|
||||
|
||||
select.addEventListener('change', function() {
|
||||
state.selectedChoice = select.options[select.selectedIndex].text;
|
||||
feedback.innerText = "You have selected '" + state.selectedChoice +
|
||||
"'. Click Submit to grade your answer.";
|
||||
});
|
||||
|
||||
return {
|
||||
getState: getState,
|
||||
setState: setState,
|
||||
getGrade: getGrade
|
||||
};
|
||||
}());
|
||||
@@ -53,7 +53,7 @@ git+https://github.com/edx/MongoDBProxy.git@25b99097615bda06bd7cdfe5669ed80dc2a7
|
||||
git+https://github.com/edx/nltk.git@2.0.6#egg=nltk==2.0.6
|
||||
-e git+https://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev
|
||||
-e git+https://github.com/appliedsec/pygeoip.git@95e69341cebf5a6a9fbf7c4f5439d458898bdc3b#egg=pygeoip
|
||||
-e git+https://github.com/jazkarta/edx-jsme.git@0908b4db16168382be5685e7e9b7b4747ac410e0#egg=edx-jsme
|
||||
-e git+https://github.com/jazkarta/edx-jsme.git@690dbf75441fa91c7c4899df0b83d77f7deb5458#egg=edx-jsme
|
||||
git+https://github.com/edx/django-pyfs.git@1.0.3#egg=django-pyfs==1.0.3
|
||||
git+https://github.com/mitodl/django-cas.git@v2.1.1#egg=django-cas
|
||||
-e git+https://github.com/dgrtwo/ParsePy.git@7949b9f754d1445eff8e8f20d0e967b9a6420639#egg=parse_rest
|
||||
|
||||
Reference in New Issue
Block a user