Commit 59d63178 by Will Daly

Merge pull request #485 from edx/will/js-refactor-assessment-editing

JS editor refactor
parents 64506f31 2c3e8690
{% load i18n %}
{% spaceless %}
<li class="openassessment_criterion is-collapsible">
<div class="openassessment_criterion_header view-outline">
<a class="action expand-collapse collapse"><i class="icon-caret-down ui-toggle-expansion"></i></a>
<h6 class="openassessment_criterion_header_title">{% trans "Criterion" %}</h6>
<div class="openassessment_rubric_remove_button"><h2>{% trans "Remove" %}</h2></div>
</div>
<div class="openassessment_criterion_body wrapper-comp-settings">
<ul class="list-input settings-list openassessment_criterion_basic_editor">
<li class="field comp-setting-entry">
<div class="wrapper-comp-settings">
<label class="openassessment_criterion_name_label setting-label">
{% trans "Criterion Name" %}
<input
class="openassessment_criterion_name input setting-input"
type="text"
value="{{ criterion_name }}"
>
</label>
</div>
</li>
<li class="field comp-setting-entry">
<div class="wrapper-comp-settings">
<label class="openassessment_criterion_prompt_label setting-label">
{% trans "Criterion Prompt" %}
<textarea class="openassessment_criterion_prompt setting-input">{{ criterion_prompt }}</textarea>
</label>
</div>
</li>
</ul>
<ul class="openassessment_criterion_option_list">
{% for option in criterion_options %}
{% include "openassessmentblock/edit/oa_edit_option.html" with option_name=option.name option_points=option.points option_explanation=option.explanation %}
{% endfor %}
</ul>
<div class="openassessment_criterion_add_option openassessment_option_header">
<h2>{% trans "Add Another Option"%}</h2>
</div>
<div class="openassessment_criterion_feedback_wrapper wrapper-comp-settings">
<ul class="list-input settings-list">
<li class="field comp-setting-entry">
<div class="wrapper-comp-setting">
<label class="setting-label">
{% trans "Criterion Feedback" %}
<select class="openassessment_criterion_feedback input setting-input">
<option value="disabled">{% trans "Disabled" %}</option>
<option value="optional" {% if criterion_feedback == "optional" %} selected="true" {% endif %}>{% trans "Optional" %}</option>
<option value="required" {% if criterion_feedback == "required" %} selected="true" {% endif %}>{% trans "Required" %}</option>
</select>
</label>
</div>
<p class="setting-help">
{% trans "Select one of the options above. This describes whether or not the student will have to provide criterion feedback." %}
</p>
</li>
</ul>
</div>
</div>
</li>
{% endspaceless %}
\ No newline at end of file
{% load i18n %}
{% spaceless %}
<li class="openassessment_criterion_option">
<div class="openassessment_option_header">
<span class="openassessment_option_header_title">{% trans "Option" %}</span>
<div class="openassessment_rubric_remove_button">
<h2>{% trans "Remove" %}</h2>
</div>
</div>
<div class="wrapper-comp-settings">
<ul class="list-input settings-list">
<li class="field comp-setting-entry openassessment_criterion_option_name_wrapper">
<div class="wrapper-comp-setting">
<label class="openassessment_criterion_option_name_label setting-label">
{% trans "Option Name"%}
<input
class="openassessment_criterion_option_name input input-label"
type="text"
value="{{ option_name }}"
>
</label>
</div>
</li>
<li class="field comp-setting-entry openassessment_criterion_option_point_wrapper">
<div class="wrapper-comp-setting">
<label class="openassessment_criterion_option_points_label setting-label">
{% trans "Option Points"%}
<input
class="openassessment_criterion_option_points input setting-input"
type="number"
value="{{ option_points }}"
min="0"
>
</label>
</div>
</li>
<li class="field comp-setting-entry openassessment_criterion_option_explanation_wrapper">
<div class="wrapper-comp-setting">
<label class="openassessment_criterion_option_explanation_label setting-label">
{% trans "Option Explanation"%}
<textarea class="openassessment_criterion_option_explanation setting-input">{{ option_explanation }}</textarea>
</label>
</div>
</li>
</ul>
</div>
</li>
{% endspaceless %}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -385,44 +385,46 @@ ...@@ -385,44 +385,46 @@
"title": "The most important of all questions.", "title": "The most important of all questions.",
"submission_due": "2014-10-1T10:00:00", "submission_due": "2014-10-1T10:00:00",
"criteria": [ "criteria": [
{ {
"name": "Criterion 1", "name": "Criterion with two options",
"prompt": "Prompt 1", "prompt": "Prompt for criterion with two options",
"order_num": 0, "order_num": 0,
"feedback": "optional", "feedback": "disabled",
"options": [ "options": [
{ {
"order_num": 2, "order_num": 0,
"points": 1,
"name": "Fair",
"explanation": "Fair explanation"
},
{
"order_num": 1,
"points": 2, "points": 2,
"name": "Good" "name": "Good",
"explanation": "Good explanation"
} }
], ],
"points_possible": 2 "points_possible": 2
}, },
{ {
"name": "Criterion 2", "name": "Criterion with no options",
"prompt": "Prompt 2", "prompt": "Prompt for criterion with no options",
"order_num": 1, "order_num": 0,
"options": [ "options": [],
{ "feedback": "required",
"order_num": 1, "points_possible": 0
"points": 1,
"name": "Fair"
}
],
"points_possible": 2
}, },
{ {
"name": "Criterion 3", "name": "Criterion with optional feedback",
"prompt": "Prompt 3", "prompt": "Prompt for criterion with optional feedback",
"order_num": 2, "order_num": 2,
"feedback": "optional", "feedback": "optional",
"options": [ "options": [
{ {
"order_num": 2, "order_num": 0,
"points": 2, "points": 2,
"name": "Good" "name": "Good",
"explanation": "Good explanation"
} }
], ],
"points_possible": 2 "points_possible": 2
......
/**
Tests for the Openassessment Container Object.
**/
describe("OpenAssessment.Container", function () {
var counter = 0;
var StubContainerItem = function(element) {
// Assign an ID to the item if it doesn't already have one.
if ($(element).attr("test_id") === "") {
$(element).attr("test_id", counter);
counter += 1;
}
this.getFieldValues = function() {
var testIdNum = parseInt($(element).attr("test_id"), 10);
return { id: testIdNum };
};
};
var container = null;
var createContainer = function() {
return new OpenAssessment.Container(
StubContainerItem, {
containerElement: $("#container").get(0),
templateElement: $("#template").get(0),
addButtonElement: $("#add_button").get(0),
removeButtonClass: "remove_button",
containerItemClass: "test_item",
}
);
};
beforeEach(function () {
// Reset the counter before each test
counter = 0;
// Install a minimal fixture
// We don't need to use a full ORA2 template for this,
// so we just define the fixture inline.
setFixtures(
'<div id="container" />' +
'<div id="template" test_id="">' +
'<div class="remove_button" />' +
'</div>' +
'<div id="add_button" />'
);
// Create the container and configure it
// to use the stub container item.
container = createContainer();
});
it("adds and removes items", function() {
// Initially, there should be no items
expect(container.getItemValues()).toEqual([]);
// Add an item
container.add();
expect(container.getItemValues()).toEqual([
{ id: 0 }
]);
// Add a second item
container.add();
expect(container.getItemValues()).toEqual([
{ id: 0 },
{ id: 1 }
]);
// Add a third item
container.add();
expect(container.getItemValues()).toEqual([
{ id: 0 },
{ id: 1 },
{ id: 2 }
]);
// Remove the second item
container.remove(container.getItemElement(1));
expect(container.getItemValues()).toEqual([
{ id: 0 },
{ id: 2 },
]);
// Remove the first item
container.remove(container.getItemElement(0));
expect(container.getItemValues()).toEqual([
{ id: 2 },
]);
// Remove the last item
container.remove(container.getItemElement(0));
expect(container.getItemValues()).toEqual([]);
});
it("ignores unrecognized DOM elements", function() {
// Add some items to the container
container.add();
container.add();
expect(container.getItemValues().length).toEqual(2);
// Add an extra element to the container in the DOM
$("<p>Not a container item!</p>").appendTo("#parent_element");
// Expect the count to remain the same
expect(container.getItemValues().length).toEqual(2);
// Add another element
container.add();
expect(container.getItemValues().length).toEqual(3);
// Remove the first element
container.remove(container.getItemElement(0));
expect(container.getItemValues().length).toEqual(2);
});
it("adds an element when the add button is pressed", function() {
// Press the add button
expect(container.getItemValues().length).toEqual(0);
$("#add_button").click();
expect(container.getItemValues().length).toEqual(1);
});
it("removes an element when the remove button is pressed", function() {
// Add some items
container.add();
container.add();
container.add();
expect(container.getItemValues().length).toEqual(3);
// Press the button to delete the second item
$(".remove_button", container.getItemElement(1)).click();
expect(container.getItemValues().length).toEqual(2);
expect(container.getItemValues()).toEqual([
{ id: 0 },
{ id: 2 }
]);
});
it("configures remove buttons for pre-existing items", function() {
// Add an item directly to the container element in the DOM,
// before initializing the container object.
$("#container").append(
'<div class="test_item" test_id="0">' +
'<div class="remove_button" />' +
'<div>'
);
// Initialize the container object
container = createContainer();
// Verify that the container recognizes the pre-existing item
expect(container.getItemValues()).toEqual([{ id: 0 }]);
// Expect that we can click the "remove" button
// to remove the item.
$(".remove_button", container.getItemElement(0)).click();
expect(container.getItemValues().length).toEqual(0);
});
});
\ No newline at end of file
...@@ -57,23 +57,6 @@ describe("OpenAssessment.StudioView", function() { ...@@ -57,23 +57,6 @@ describe("OpenAssessment.StudioView", function() {
view = new OpenAssessment.StudioView(runtime, el, server); view = new OpenAssessment.StudioView(runtime, el, server);
}); });
it("saves the editor context definition", function() {
// Update the context
view.settingsFieldSelectors.titleField.val('THIS IS THE NEW TITLE');
view.settingsFieldSelectors.submissionStartField.val('2014-01-01');
// Save the updated editor definition
view.save();
// Expect the saving notification to start/end
expect(runtime.notify).toHaveBeenCalledWith('save', {state: 'start'});
expect(runtime.notify).toHaveBeenCalledWith('save', {state: 'end'});
// Expect the server's context to have been updated
expect(server.receivedData.title).toEqual('THIS IS THE NEW TITLE');
expect(server.receivedData.submissionStart).toEqual('2014-01-01T00:00:00.000Z');
});
it("confirms changes for a released problem", function() { it("confirms changes for a released problem", function() {
// Simulate an XBlock that has been released // Simulate an XBlock that has been released
server.isReleased = true; server.isReleased = true;
......
/**
Tests for assessment editing views.
**/
describe("OpenAssessment edit assessment views", function() {
var testEnableAndDisable = function(view) {
view.isEnabled(false);
expect(view.isEnabled()).toBe(false);
view.isEnabled(true);
expect(view.isEnabled()).toBe(true);
};
var testLoadXMLExamples = function(view) {
var xml = "XML DEFINITIONS WOULD BE HERE";
view.exampleDefinitions(xml);
expect(view.description()).toEqual({ examples: xml });
};
beforeEach(function() {
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_edit.html');
});
describe("OpenAssessment.EditPeerAssessmentView", function() {
var view = null;
beforeEach(function() {
var element = $("#oa_peer_assessment_editor").get(0);
view = new OpenAssessment.EditPeerAssessmentView(element);
});
it("Enables and disables", function() { testEnableAndDisable(view); });
it("Loads a description", function() {
view.mustGradeNum(1);
view.mustBeGradedByNum(2);
view.startDatetime("2014-01-01T00:00");
view.dueDatetime("2014-03-04T00:00");
expect(view.description()).toEqual({
must_grade: 1,
must_be_graded_by: 2,
start: "2014-01-01T00:00",
due: "2014-03-04T00:00"
});
});
it("Handles default dates", function() {
view.startDatetime("");
view.dueDatetime("");
expect(view.description().start).toBe(null);
expect(view.description().due).toBe(null);
});
});
describe("OpenAssessment.EditSelfAssessmentView", function() {
var view = null;
beforeEach(function() {
var element = $("#oa_self_assessment_editor").get(0);
view = new OpenAssessment.EditSelfAssessmentView(element);
});
it("Enables and disables", function() { testEnableAndDisable(view); });
it("Loads a description", function() {
view.startDatetime("2014-01-01T00:00");
view.dueDatetime("2014-03-04T00:00");
expect(view.description()).toEqual({
start: "2014-01-01T00:00",
due: "2014-03-04T00:00"
});
});
it("Handles default dates", function() {
view.startDatetime("");
view.dueDatetime("");
expect(view.description().start).toBe(null);
expect(view.description().due).toBe(null);
});
});
describe("OpenAssessment.EditStudentTrainingView", function() {
var view = null;
beforeEach(function() {
var element = $("#oa_student_training_editor").get(0);
view = new OpenAssessment.EditStudentTrainingView(element);
});
it("Enables and disables", function() { testEnableAndDisable(view); });
it("Loads a description", function() { testLoadXMLExamples(view); });
});
describe("OpenAssessment.EditExampleBasedAssessmentView", function() {
var view = null;
beforeEach(function() {
var element = $("#oa_ai_assessment_editor").get(0);
view = new OpenAssessment.EditExampleBasedAssessmentView(element);
});
it("Enables and disables", function() { testEnableAndDisable(view); });
it("Loads a description", function() { testLoadXMLExamples(view); });
});
});
\ No newline at end of file
/**
Tests for OpenAssessment prompt editing view.
**/
describe("OpenAssessment.EditPromptView", function() {
var view = null;
beforeEach(function() {
// Load the DOM fixture
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_edit.html');
// Create the view
var element = $("#oa_prompt_editor_wrapper").get(0);
view = new OpenAssessment.EditPromptView(element);
});
it("sets and loads prompt text", function() {
view.promptText("");
expect(view.promptText()).toEqual("");
view.promptText("This is a test prompt!");
expect(view.promptText()).toEqual("This is a test prompt!");
});
});
\ No newline at end of file
/**
Tests for the rubric editing view.
**/
describe("OpenAssessment.EditRubricView", function() {
var view = null;
beforeEach(function() {
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_edit.html');
var el = $("#oa_rubric_editor_wrapper").get(0);
view = new OpenAssessment.EditRubricView(el);
});
it("reads a criteria definition from the editor", function() {
// This assumes a particular structure of the DOM,
// which is set by the HTML fixture.
var criteria = view.criteriaDefinition();
expect(criteria.length).toEqual(3);
// Criterion with two options, feedback disabled
expect(criteria[0]).toEqual({
name: "Criterion with two options",
prompt: "Prompt for criterion with two options",
order_num: 0,
feedback: "disabled",
options: [
{
order_num: 0,
points: 1,
name: "Fair",
explanation: "Fair explanation"
},
{
order_num: 1,
points: 2,
name: "Good",
explanation: "Good explanation"
}
],
});
// Criterion with no options, feedback required
expect(criteria[1]).toEqual({
name: "Criterion with no options",
prompt: "Prompt for criterion with no options",
order_num: 1,
feedback: "required",
options: []
});
// Criterion with one option, feeback optional
expect(criteria[2]).toEqual({
name: "Criterion with optional feedback",
prompt: "Prompt for criterion with optional feedback",
order_num: 2,
feedback: "optional",
options: [
{
order_num: 0,
points: 2,
name: "Good",
explanation: "Good explanation"
}
]
});
});
it("reads the feedback prompt from the editor", function() {
view.feedbackPrompt("");
expect(view.feedbackPrompt()).toEqual("");
var prompt = "How do you think the student did overall?";
view.feedbackPrompt(prompt);
expect(view.feedbackPrompt()).toEqual(prompt);
});
});
\ No newline at end of file
/**
Tests for the edit settings view.
**/
describe("OpenAssessment.EditSettingsView", function() {
var StubView = function(name, descriptionText) {
this.name = name;
this.description = function() {
return { dummy: descriptionText };
};
var _enabled = true;
this.isEnabled = function(isEnabled) {
if (typeof(isEnabled) !== "undefined") { this._enabled = isEnabled; }
return this._enabled;
};
};
var view = null;
var assessmentViews = null;
beforeEach(function() {
// Load the DOM fixture
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_edit.html');
// Create the stub assessment views
assessmentViews = [
new StubView("self-assessment", "Self assessment description"),
new StubView("peer-assessment", "Peer assessment description")
];
// Create the view
var element = $("#oa_basic_settings_editor").get(0);
view = new OpenAssessment.EditSettingsView(element, assessmentViews);
});
it("sets and loads display name", function() {
view.displayName("");
expect(view.displayName()).toEqual("");
view.displayName("This is the name of the problem!");
expect(view.displayName()).toEqual("This is the name of the problem!");
});
it("sets and loads the submission start/due dates", function() {
view.submissionStart("");
expect(view.submissionStart()).toBe(null);
view.submissionStart("2014-04-01T00:00.0000Z");
expect(view.submissionStart()).toEqual("2014-04-01T00:00.0000Z");
view.submissionDue("");
expect(view.submissionDue()).toBe(null);
view.submissionDue("2014-05-02T00:00.0000Z");
expect(view.submissionDue()).toEqual("2014-05-02T00:00.0000Z");
});
it("sets and loads the image enabled state", function() {
view.imageSubmissionEnabled(true);
expect(view.imageSubmissionEnabled()).toBe(true);
view.imageSubmissionEnabled(false);
expect(view.imageSubmissionEnabled()).toBe(false);
});
it("builds a description of enabled assessments", function() {
// Disable all assessments, and expect an empty description
assessmentViews[0].isEnabled(false);
assessmentViews[1].isEnabled(false);
expect(view.assessmentsDescription()).toEqual([]);
// Enable the first assessment only
assessmentViews[0].isEnabled(true);
assessmentViews[1].isEnabled(false);
expect(view.assessmentsDescription()).toEqual([
{
name: "self-assessment",
dummy: "Self assessment description"
}
]);
// Enable the second assessment only
assessmentViews[0].isEnabled(false);
assessmentViews[1].isEnabled(true);
expect(view.assessmentsDescription()).toEqual([
{
name: "peer-assessment",
dummy: "Peer assessment description"
}
]);
// Enable both assessments
assessmentViews[0].isEnabled(true);
assessmentViews[1].isEnabled(true);
expect(view.assessmentsDescription()).toEqual([
{
name: "self-assessment",
dummy: "Self assessment description"
},
{
name: "peer-assessment",
dummy: "Peer assessment description"
}
]);
});
});
/**
Container that handles addition / deletion of arbitrary items.
An item is any object that has a `getFieldValues()` method,
which should return a JSON-serializable representation
of the item.
Containers copy "template" elements to create new items.
For example, to create a container for an item called "test_item",
the DOM should look something like:
<div id="test_container" />
<div id="test_item_template">
<div class="test_item_remove_button">Remove</div>
<p>This is the default value for the item.</p>
</div>
<div id="test_item_add_button">Add</div>
You can then initialize the container:
>>> var container = $("#test_container").get(0);
>>> var template = $("#test_item_template").get(0);
>>> var addButton = $("#test_item_add_button").get(0);
>>>
>>> container = OpenAssessment.Container(
>>> ContainerItem, {
>>> containerElement: container,
>>> templateElement: template,
>>> addButtonElement: addButton,
>>> removeButtonClass: "test_item_remove_button"
>>> containerItemClass: "test_item"
>>> }
>>> );
The container is responsible for initializing the "add" and "remove" buttons,
including for pre-existing items in the container element.
Templates elements are never deleted, so they should be hidden using CSS styles.
Args:
containerItem (object): The container item object used to access
the contents of items in the container.
Kwargs:
containerElement (DOM element): The element representing the container.
templateElement (DOM element): The element containing the template for creating new items.
addButtonElement (DOM element): The element of the button used to add new items to the container.
removeButtonClass (string): The CSS class of the button that removes an item from the container.
There may be one of these for each item in the container.
containerItemClass (string): The CSS class of items in the container.
New items will be assigned this class.
**/
OpenAssessment.Container = function(containerItem, kwargs) {
this.containerItem = containerItem;
this.containerElement = kwargs.containerElement;
this.templateElement = kwargs.templateElement;
this.addButtonElement = kwargs.addButtonElement;
this.removeButtonClass = kwargs.removeButtonClass;
this.containerItemClass = kwargs.containerItemClass;
// Install a click handler for the add button
$(this.addButtonElement).click($.proxy(this.add, this));
// Find items already in the container and install click
// handlers for the delete buttons.
var container = this;
$("." + this.removeButtonClass, this.containerElement).click(
function(eventData) { container.remove(eventData.target); }
);
// Initialize existing items, in case they need to install their
// own event handlers.
$("." + this.containerItemClass, this.containerElement).each(
function(index, element) { new container.containerItem(element); }
);
};
OpenAssessment.Container.prototype = {
/**
Adds a new item to the container.
**/
add: function() {
// Copy the template into the container
// Remove any CSS IDs (since now the element is not unique)
// and add the item class so we can find it later.
$(this.templateElement)
.clone()
.removeAttr('id')
.toggleClass('is--hidden', false)
.toggleClass(this.containerItemClass, true)
.appendTo($(this.containerElement));
// Install a click handler for the delete button
// Since we just added the new element to the container,
// it should be the last one.
var container = this;
var containerItem = $("." + this.containerItemClass, this.containerElement).last()
containerItem.find('.' + this.removeButtonClass)
.click(function(eventData) { container.remove(eventData.target); } );
// Initialize the item, allowing it to install event handlers.
new this.containerItem(containerItem.get(0));
},
/**
Remove the item associated with an element.
If the element is not itself an item, traverse up the
DOM tree until an item is found.
Args:
element (DOM element): An element representing the container item
or an element within the container item.
**/
remove: function(element) {
$(element).closest("." + this.containerItemClass).remove();
},
/**
Retrieves the values that each container defines for itself, in the order in which they are
presented in the DOM.
Returns:
array: The values representing each container item.
**/
getItemValues: function () {
var values = [];
var container = this;
$("." + this.containerItemClass, this.containerElement).each(
function(index, element) {
var containerItem = new container.containerItem(element);
var fieldValues = containerItem.getFieldValues();
values.push(fieldValues);
}
);
return values;
},
/**
Retrieve the element representing an item in this container.
Args:
index (int): The index of the item, starting from 0.
Returns:
DOM element if the item is found, otherwise null.
**/
getItemElement: function(index) {
var element = $("." + this.containerItemClass, this.containerElement).get(index);
return (element !== undefined) ? element : null;
},
};
/**
The RubricOption Class used to construct and maintain references to rubric options from within an options
container object. Constructs a new RubricOption element.
Args:
element (OpenAssessment.Container): The container that the option is a member of.
Returns:
OpenAssessment.RubricOption
**/
OpenAssessment.RubricOption = function(element) {
this.element = element;
};
OpenAssessment.RubricOption.prototype = {
/**
Finds the values currently entered in the Option's fields, and returns them.
Returns:
object literal of the form:
{
'name': 'Real Bad',
'points': 1,
'explanation': 'Essay was primarily composed of emojis.'
}
**/
getFieldValues: function () {
return {
name: OpenAssessment.Fields.stringField(
$('.openassessment_criterion_option_name', this.element)
),
points: OpenAssessment.Fields.intField(
$('.openassessment_criterion_option_points', this.element)
),
explanation: OpenAssessment.Fields.stringField(
$('.openassessment_criterion_option_explanation', this.element)
)
};
}
};
/**
The RubricCriterion Class is used to construct and get information from a rubric element within
the DOM.
Args:
element (JQuery Object): The selection which describes the scope of the criterion.
Returns:
OpenAssessment.RubricCriterion
**/
OpenAssessment.RubricCriterion = function(element) {
this.element = element;
this.optionContainer = new OpenAssessment.Container(
OpenAssessment.RubricOption, {
containerElement: $(".openassessment_criterion_option_list", this.element).get(0),
templateElement: $("#openassessment_option_template").get(0),
addButtonElement: $(".openassessment_criterion_add_option", this.element).get(0),
removeButtonClass: "openassessment_rubric_remove_button",
containerItemClass: "openassessment_criterion_option",
}
);
};
OpenAssessment.RubricCriterion.prototype = {
/**
Finds the values currently entered in the Criterion's fields, and returns them.
Returns:
object literal of the form:
{
'name': 'Emoji Content',
'prompt': 'How expressive was the author with their words, and how much did they rely on emojis?',
'feedback': 'optional',
'options': [
{
'name': 'Real Bad',
'points': 1,
'explanation': 'Essay was primarily composed of emojis.'
}
...
]
}
**/
getFieldValues: function () {
return {
name: OpenAssessment.Fields.stringField(
$('.openassessment_criterion_name', this.element)
),
prompt: OpenAssessment.Fields.stringField(
$('.openassessment_criterion_prompt', this.element)
),
feedback: OpenAssessment.Fields.stringField(
$('.openassessment_criterion_feedback', this.element)
),
options: this.optionContainer.getItemValues()
};
}
};
\ No newline at end of file
/**
Utilities for reading / writing fields.
**/
OpenAssessment.Fields = {
stringField: function(sel, value) {
if (typeof(value) !== "undefined") { sel.val(value); }
return sel.val();
},
datetimeField: function(sel, value) {
if (typeof(value) !== "undefined") { sel.val(value); }
var fieldValue = sel.val();
return (fieldValue !== "") ? fieldValue : null;
},
intField: function(sel, value) {
if (typeof(value) !== "undefined") { sel.val(value); }
return parseInt(sel.val(), 10);
},
booleanField: function(sel, value) {
if (typeof(value) !== "undefined") { sel.prop("checked", value); }
return sel.prop("checked");
},
};
/**
Editing interface for the rubric prompt.
Args:
element (DOM element): The DOM element representing this view.
Returns:
OpenAssessment.EditPromptView
**/
OpenAssessment.EditPromptView = function(element) {
this.element = element;
};
OpenAssessment.EditPromptView.prototype = {
/**
Get or set the text of the prompt.
Args:
text (string, optional): If provided, set the text of the prompt.
Returns:
string
**/
promptText: function(text) {
var sel = $('#openassessment_prompt_editor', this.element);
return OpenAssessment.Fields.stringField(sel, text);
},
};
\ No newline at end of file
/**
Interface for editing rubric definitions.
**/
OpenAssessment.EditRubricView = function(element) {
this.element = element;
this.criteriaContainer = new OpenAssessment.Container(
OpenAssessment.RubricCriterion, {
containerElement: $("#openassessment_criterion_list", this.element).get(0),
templateElement: $("#openassessment_criterion_template", this.element).get(0),
addButtonElement: $("#openassessment_rubric_add_criterion", this.element).get(0),
removeButtonClass: "openassessment_rubric_remove_button",
containerItemClass: "openassessment_criterion",
}
);
};
OpenAssessment.EditRubricView.prototype = {
/**
Construct a list of criteria definitions from the editor UI.
Returns:
list of criteria objects
Example usage:
>>> editRubricView.criteriaDefinition();
[
{
name: "Criterion",
prompt: "Prompt",
order_num: 0,
feedback: "disabled",
options: [
{
order_num: 0,
points: 1,
name: "Good",
explanation: "Explanation"
},
...
]
},
...
]
**/
criteriaDefinition: function() {
var criteria = this.criteriaContainer.getItemValues();
// Add order_num fields for criteria and options
for (var criterion_idx = 0; criterion_idx < criteria.length; criterion_idx++) {
var criterion = criteria[criterion_idx];
criterion.order_num = criterion_idx;
for (var option_idx = 0; option_idx < criterion.options.length; option_idx++) {
var option = criterion.options[option_idx];
option.order_num = option_idx;
}
}
return criteria;
},
/**
Get or set the feedback prompt in the editor.
This is the prompt shown to students when giving "overall" feedback
on a submission.
Args:
text (string, optional): If provided, set the feedback prompt to this value.
Returns:
string
**/
feedbackPrompt: function(text) {
var sel = $("#openassessment_rubric_feedback", this.element);
return OpenAssessment.Fields.stringField(sel, text);
}
};
\ No newline at end of file
/**
Editing interface for OpenAssessment settings (including assessments).
Args:
element (DOM element): The DOM element representing this view.
assessmentViews (array): List of assessment view objects.
Returns:
OpenAssessment.EditSettingsView
**/
OpenAssessment.EditSettingsView = function(element, assessmentViews) {
this.element = element;
this.assessmentViews = assessmentViews;
};
OpenAssessment.EditSettingsView.prototype = {
/**
Get or set the display name of the problem.
Args:
name (string, optional): If provided, set the display name.
Returns:
string
**/
displayName: function(name) {
var sel = $("#openassessment_title_editor", this.element);
return OpenAssessment.Fields.stringField(sel, name);
},
/**
Get or set the submission start date.
Args:
datetime (string, optional): If provided, set the datetime.
Returns:
string (ISO-format UTC datetime)
**/
submissionStart: function(datetime) {
var sel = $("#openassessment_submission_start_editor", this.element);
return OpenAssessment.Fields.datetimeField(sel, datetime);
},
/**
Get or set the submission end date.
Args:
datetime (string, optional): If provided, set the datetime.
Returns:
string (ISO-format UTC datetime)
**/
submissionDue: function(datetime) {
var sel = $("#openassessment_submission_start_editor", this.element);
return OpenAssessment.Fields.datetimeField(sel, datetime);
},
/**
Enable / disable image submission.
Args:
isEnabled (boolean, optional): If provided, enable/disable image submission.
Returns:
boolean
**/
imageSubmissionEnabled: function(isEnabled) {
var sel = $("#openassessment_submission_image_editor", this.element);
if (typeof(isEnabled) !== "undefined") {
if (isEnabled) { sel.val(1); }
else { sel.val(0); }
}
return (sel.val() == 1);
},
/**
Construct a list of enabled assessments and their properties.
Returns:
list of object literals representing the assessments.
Example usage:
>>> editSettingsView.assessmentsDescription()
[
{
name: "peer-assessment",
start: "2014-04-01T00:00",
due: null
must_grade: 5,
must_be_graded_by: 2,
},
{
name: "self-assessment",
start: null,
due: null
}
]
**/
assessmentsDescription: function() {
assessmentDescList = [];
for (var idx in this.assessmentViews) {
var asmntView = this.assessmentViews[idx];
if (asmntView.isEnabled()) {
var description = asmntView.description();
description["name"] = asmntView.name;
assessmentDescList.push(description);
}
}
return assessmentDescList;
},
};
\ No newline at end of file
...@@ -20,6 +20,9 @@ logger = logging.getLogger(__name__) ...@@ -20,6 +20,9 @@ logger = logging.getLogger(__name__)
class StudioMixin(object): class StudioMixin(object):
"""
Studio editing view for OpenAssessment XBlock.
"""
DEFAULT_CRITERIA = [ DEFAULT_CRITERIA = [
{ {
...@@ -30,10 +33,6 @@ class StudioMixin(object): ...@@ -30,10 +33,6 @@ class StudioMixin(object):
} }
] ]
"""
Studio editing view for OpenAssessment XBlock.
"""
def studio_view(self, context=None): def studio_view(self, context=None):
""" """
Render the OpenAssessment XBlock for editing in Studio. Render the OpenAssessment XBlock for editing in Studio.
......
#!/usr/bin/env bash #!/usr/bin/env bash
cd `dirname $BASH_SOURCE` && cd ../openassessment/xblock/static cd `dirname $BASH_SOURCE` && cd ../openassessment/xblock/static
sass --update sass:css --force sass --update sass:css --force --style compressed
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment