Commit d74b6a6c by Usman Khalid

Merge branch 'usman/tnl708-multiple-prompts-training' into…

Merge branch 'usman/tnl708-multiple-prompts-training' into usman/tnl708-multiple-prompts-integration

Conflicts:
	openassessment/xblock/static/css/openassessment-ltr.css
	openassessment/xblock/static/css/openassessment-rtl.css
	openassessment/xblock/test/data/student_training_mixin.json
parents 9b43d54d eac87956
......@@ -33,6 +33,11 @@
<ol id="openassessment_training_example_criterion_template" class="is--hidden">
{% include "openassessmentblock/edit/oa_training_example_criterion.html" %}
</ol>
<ol id="openassessment_training_example_part_template" class="is--hidden">
<li class="openassessment_training_example_essay_part">
<textarea maxlength="100000"></textarea>
</li>
</ol>
</div>
</li>
......
......@@ -28,7 +28,13 @@
<div class="openassessment_training_example_essay_wrapper">
<h2>{% trans "Response" %}</h2>
<textarea class="openassessment_training_example_essay" maxlength="100000">{{ example.answer }}</textarea>
<ol class="openassessment_training_example_essay">
{% for part in example.answer.parts %}
<li class="openassessment_training_example_essay_part">
<textarea maxlength="100000">{{ part.text }}</textarea>
</li>
{% endfor %}
</ol>
</div>
</div>
</li>
......
......@@ -88,7 +88,7 @@ EDITOR_UPDATE_SCHEMA = Schema({
'must_be_graded_by': All(int, Range(min=0)),
'examples': [
Schema({
Required('answer'): utf8_validator,
Required('answer'): [utf8_validator],
Required('options_selected'): [
Schema({
Required('criterion'): utf8_validator,
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -112,9 +112,34 @@ OpenAssessment.Prompt.prototype = {
return OpenAssessment.Fields.stringField(sel, text);
},
addHandler: function (){},
addEventListeners: function() {},
removeHandler: function() {},
/**
Hook into the event handler for addition of a prompt.
*/
addHandler: function (){
this.notifier.notificationFired(
"promptAdd",
{
"index": this.element.index()
}
);
},
/**
Hook into the event handler for removal of a prompt.
*/
removeHandler: function (){
this.notifier.notificationFired(
"promptRemove",
{
"index": this.element.index()
}
);
},
updateHandler: function() {},
/**
......@@ -588,7 +613,7 @@ OpenAssessment.RubricCriterion.prototype = {
OpenAssessment.TrainingExample = function(element){
this.element = element;
this.criteria = $(".openassessment_training_example_criterion_option", this.element);
this.answer = $('.openassessment_training_example_essay', this.element).first();
this.answer = $('.openassessment_training_example_essay_part textarea', this.element)
};
OpenAssessment.TrainingExample.prototype = {
......@@ -609,7 +634,9 @@ OpenAssessment.TrainingExample.prototype = {
).get();
return {
answer: this.answer.prop('value'),
answer: this.answer.map(function() {
return $(this).prop('value');
}).get(),
options_selected: optionsSelected
};
},
......
......@@ -25,9 +25,14 @@ OpenAssessment.StudioView = function(runtime, element, server) {
// Initialize the validation alert
this.alert = new OpenAssessment.ValidationAlert().install();
var studentTrainingListener = new OpenAssessment.StudentTrainingListener();
// Initialize the prompt tab view
this.promptsView = new OpenAssessment.EditPromptsView(
$("#oa_prompts_editor_wrapper", this.element).get(0)
$("#oa_prompts_editor_wrapper", this.element).get(0),
new OpenAssessment.Notifier([
studentTrainingListener
])
);
// Initialize the settings tab view
......@@ -57,7 +62,7 @@ OpenAssessment.StudioView = function(runtime, element, server) {
this.rubricView = new OpenAssessment.EditRubricView(
$("#oa_rubric_editor_wrapper", this.element).get(0),
new OpenAssessment.Notifier([
new OpenAssessment.StudentTrainingListener()
studentTrainingListener
])
);
......
......@@ -421,7 +421,7 @@ OpenAssessment.EditStudentTrainingView.prototype = {
{
examples: [
{
answer: "I love pokemon",
answer: ("I love pokemon 1", "I love pokemon 2"),
options_selected: [
{
criterion: "brevity",
......
/**
Dynamically update student training examples based on
changes to the rubric.
changes to the prompts or the rubric.
**/
OpenAssessment.StudentTrainingListener = function() {
this.element = $('#oa_student_training_editor');
......@@ -8,6 +8,28 @@ OpenAssessment.StudentTrainingListener = function() {
};
OpenAssessment.StudentTrainingListener.prototype = {
/**
Add a answer part in the training examples when a prompt is added.
*/
promptAdd: function(data) {
var view = this.element;
var essay_part = $("#openassessment_training_example_part_template")
.children().first()
.clone()
.removeAttr('id')
.toggleClass('is--hidden', false)
.appendTo(".openassessment_training_example_essay", view);
},
/**
Remove the answer part in the training examples when a prompt is removed.
*/
promptRemove: function(data) {
var view = this.element;
$(".openassessment_training_example_essay li:nth-child(" + (data.index + 1) + ")", view).remove();
},
/**
Event handler for updating training examples when a criterion option has
been updated.
......
......@@ -8,7 +8,7 @@ Returns:
OpenAssessment.EditPromptsView
**/
OpenAssessment.EditPromptsView = function(element) {
OpenAssessment.EditPromptsView = function(element, notifier) {
this.element = element;
this.promptsContainer = new OpenAssessment.Container(
......@@ -17,7 +17,8 @@ OpenAssessment.EditPromptsView = function(element) {
templateElement: $("#openassessment_prompt_template", this.element).get(0),
addButtonElement: $("#openassessment_prompts_add_prompt", this.element).get(0),
removeButtonClass: "openassessment_prompt_remove_button",
containerItemClass: "openassessment_prompt"
containerItemClass: "openassessment_prompt",
notifier: notifier
}
);
this.promptsContainer.addEventListeners();
......
......@@ -934,7 +934,7 @@
.openassessment_training_example_body {
@include padding(0, 15px, 15px, 15px);
position: relative;
overflow: hidden;
overflow: scroll;
.openassessment_training_example_essay_wrapper {
width: 58%;
display: inline-block;
......
......@@ -14,7 +14,7 @@ from xblock.fields import List, Scope
from xblock.fragment import Fragment
from openassessment.xblock.defaults import DEFAULT_EDITOR_ASSESSMENTS_ORDER, DEFAULT_RUBRIC_FEEDBACK_TEXT
from openassessment.xblock.validation import validator
from openassessment.xblock.data_conversion import create_rubric_dict, make_django_template_key
from openassessment.xblock.data_conversion import create_rubric_dict, make_django_template_key, update_assessments_format
from openassessment.xblock.schema import EDITOR_UPDATE_SCHEMA
from openassessment.xblock.resolve_dates import resolve_dates
from openassessment.xblock.xml import serialize_examples_to_xml_str, parse_examples_from_xml_str
......@@ -191,6 +191,10 @@ class StudioMixin(object):
)}
# This is where we default to EASE for problems which are edited in the GUI
assessment['algorithm_id'] = 'ease'
if assessment['name'] == 'student-training':
for example in assessment['examples']:
example['answer'] = {'parts': [{'text': text} for text in example['answer']]}
xblock_validator = validator(self, self._)
success, msg = xblock_validator(
......@@ -269,13 +273,20 @@ class StudioMixin(object):
# could be accomplished within the template, we are opting to remove logic from the template.
student_training_module = self.get_assessment_module('student-training')
student_training_template = {'answer': ""}
student_training_template = {
'answer': {
'parts': [
{'text': ''} for prompt in self.prompts
]
}
}
criteria_list = copy.deepcopy(self.rubric_criteria_with_labels)
for criterion in criteria_list:
criterion['option_selected'] = ""
student_training_template['criteria'] = criteria_list
if student_training_module:
student_training_module = update_assessments_format([student_training_module])[0]
example_list = []
# Adds each example to a modified version of the student training module dictionary.
for example in student_training_module['examples']:
......
......@@ -1002,7 +1002,7 @@
"name": "student-training",
"examples": [
{
"answer": "Ṫḧïṡ ïṡ äṅ äṅṡẅëṛ",
"answer": ["Ṫḧïṡ ïṡ äṅ äṅṡẅëṛ 1", "Ṫḧïṡ ïṡ äṅ äṅṡẅëṛ 2"],
"options_selected": [
{ "criterion": "Not a criterion!", "option": "Ṅö" }
]
......
......@@ -90,7 +90,11 @@
"due": "2014-06-01T00:00:00",
"examples": [
{
"answer": "ẗëṡẗ äṅṡẅëṛ",
"answer": {
"parts": [
{"text": "ẗëṡẗ äṅṡẅëṛ"}
]
},
"options_selected": [
{
"criterion": "Test criterion",
......@@ -127,7 +131,11 @@
"due": "2014-06-01T00:00:00",
"examples": [
{
"answer": "ẗëṡẗ äṅṡẅëṛ",
"answer": {
"parts": [
{"text": "ẗëṡẗ äṅṡẅëṛ"}
]
},
"options_selected": [
{
"criterion": "Test criterion",
......@@ -140,7 +148,11 @@
]
},
{
"answer": "äṅöẗḧëṛ ẗëṡẗ äṅṡẅëṛ",
"answer": {
"parts": [
{"text": "äṅöẗḧëṛ ẗëṡẗ äṅṡẅëṛ"}
]
},
"options_selected": [
{
"criterion": "Another test criterion",
......
......@@ -10,7 +10,11 @@
],
"examples": [
{
"answer": "ẗëṡẗ äṅṡẅëṛ",
"answer": {
"parts": [
{"text": "ẗëṡẗ äṅṡẅëṛ"}
]
},
"options_selected": [
{
"criterion": "Test criterion",
......@@ -25,12 +29,18 @@
"xml": [
"<examples>",
"<example>",
"<answer>ẗëṡẗ äṅṡẅëṛ</answer>",
"<answer>",
"<part>ẗëṡẗ äṅṡẅëṛ 1</part>",
"<part>ẗëṡẗ äṅṡẅëṛ 2</part>",
"</answer>",
"<select criterion=\"Test criterion\" option=\"Yes\" />",
"<select criterion=\"Another test criterion\" option=\"No\" />",
"</example>",
"<example>",
"<answer>äṅöẗḧëṛ ẗëṡẗ äṅṡẅëṛ</answer>",
"<answer>",
"<part>äṅöẗḧëṛ ẗëṡẗ äṅṡẅëṛ 1</part>",
"<part>äṅöẗḧëṛ ẗëṡẗ äṅṡẅëṛ 2</part>",
"</answer>",
"<select criterion=\"Another test criterion\" option=\"Yes\" />",
"<select criterion=\"Test criterion\" option=\"No\" />",
"</example>",
......@@ -38,7 +48,12 @@
],
"examples": [
{
"answer": "ẗëṡẗ äṅṡẅëṛ",
"answer": {
"parts": [
{"text": "ẗëṡẗ äṅṡẅëṛ 1"},
{"text": "ẗëṡẗ äṅṡẅëṛ 2"}
]
},
"options_selected": [
{
"criterion": "Test criterion",
......@@ -51,7 +66,12 @@
]
},
{
"answer": "äṅöẗḧëṛ ẗëṡẗ äṅṡẅëṛ",
"answer": {
"parts": [
{"text": "äṅöẗḧëṛ ẗëṡẗ äṅṡẅëṛ 1"},
{"text": "äṅöẗḧëṛ ẗëṡẗ äṅṡẅëṛ 2"}
]
},
"options_selected": [
{
"criterion": "Another test criterion",
......
......@@ -1059,7 +1059,9 @@
"<assessments>",
"<assessment name=\"student-training\" start=\"2014-02-27T09:46:28\" due=\"2014-03-01T00:00:00\">",
"<example>",
"<answer>ẗëṡẗ äṅṡẅëṛ</answer>",
"<answer>",
"<part>ẗëṡẗ äṅṡẅëṛ</part>",
"</answer>",
"<select criterion=\"Test criterion\" option=\"No\" />",
"<select criterion=\"Another test criterion\" option=\"Yes\" />",
"</example>",
......@@ -1147,7 +1149,12 @@
"due": "2014-03-01T00:00:00",
"examples": [
{
"answer": "ẗëṡẗ äṅṡẅëṛ",
"answer": {
"parts": [
{"text": "ẗëṡẗ äṅṡẅëṛ 1"},
{"text": "ẗëṡẗ äṅṡẅëṛ 2"}
]
},
"options_selected": [
{
"criterion": "Test criterion",
......@@ -1160,7 +1167,12 @@
]
},
{
"answer": "śéćőńd téśt áńśẃéŕ",
"answer": {
"parts": [
{"text": "śéćőńd téśt áńśẃéŕ 1"},
{"text": "śéćőńd téśt áńśẃéŕ 2"}
]
},
"options_selected": [
{
"criterion": "Test criterion",
......@@ -1181,12 +1193,18 @@
"<assessments>",
"<assessment name=\"student-training\" start=\"2014-02-27T09:46:28\" due=\"2014-03-01T00:00:00\">",
"<example>",
"<answer>ẗëṡẗ äṅṡẅëṛ</answer>",
"<answer>",
"<part>ẗëṡẗ äṅṡẅëṛ 1</part>",
"<part>ẗëṡẗ äṅṡẅëṛ 2</part>",
"</answer>",
"<select criterion=\"Test criterion\" option=\"No\" />",
"<select criterion=\"Another test criterion\" option=\"Yes\" />",
"</example>",
"<example>",
"<answer>śéćőńd téśt áńśẃéŕ</answer>",
"<answer>",
"<part>śéćőńd téśt áńśẃéŕ 1</part>",
"<part>śéćőńd téśt áńśẃéŕ 2</part>",
"</answer>",
"<select criterion=\"Test criterion\" option=\"Yes\" />",
"<select criterion=\"Another test criterion\" option=\"No\" />",
"</example>",
......@@ -1292,11 +1310,15 @@
"<assessments>",
"<assessment name=\"example-based-assessment\" algorithm_id=\"sample-algorithm-id\">",
"<example>",
"<answer>тєѕт αηѕωєя</answer>",
"<answer>",
"<part>тєѕт αηѕωєя</part>",
"</answer>",
"<select criterion=\"Test criterion\" option=\"No\" />",
"</example>",
"<example>",
"<answer>тєѕт αηѕωєя TWO</answer>",
"<answer>",
"<part>тєѕт αηѕωєя TWO</part>",
"</answer>",
"<select criterion=\"Test criterion\" option=\"Yes\" />",
"</example>",
"</assessment>",
......
......@@ -8,9 +8,13 @@
"training_essay": { "answer": {
"parts": [
{
<<<<<<< HEAD
"prompt": {
"description": "Given the state of the world today, what do you think should be done to combat poverty?"
},
=======
"prompt": "Given the state of the world today, what do you think should be done to combat poverty?",
>>>>>>> usman/tnl708-multiple-prompts-training
"text": "This is my answer."
}
]}
......
......@@ -143,13 +143,13 @@
"name": "student-training",
"examples": [
{
"answer": "Ṫḧïṡ ïṡ äṅ äṅṡẅëṛ",
"answer": ["Ṫḧïṡ ïṡ äṅ äṅṡẅëṛ 1", "Ṫḧïṡ ïṡ äṅ äṅṡẅëṛ 2"],
"options_selected": [
{ "criterion": "тєѕт ¢яιтєяιση", "option": "Ṅö" }
]
},
{
"answer": "This is another answer",
"answer": ["This is another answer 1", "This is another answer 2"],
"options_selected": [
{ "criterion": "тєѕт ¢яιтєяιση", "option": "sǝʎ" }
]
......
......@@ -11,7 +11,7 @@ import dateutil.parser
from django.test import TestCase
import ddt
from openassessment.xblock.data_conversion import create_prompts_list
from openassessment.xblock.data_conversion import create_prompts_list, update_assessments_format
from openassessment.xblock.openassessmentblock import OpenAssessmentBlock
from openassessment.xblock.xml import (
serialize_content, parse_from_xml_str, _parse_prompts_xml, parse_rubric_xml,
......@@ -120,7 +120,9 @@ class TestSerializeContent(TestCase):
self.oa_block.submission_start = data.get('submission_start')
self.oa_block.submission_due = data.get('submission_due')
self.oa_block.rubric_criteria = data.get('criteria', copy.deepcopy(self.BASIC_CRITERIA))
self.oa_block.rubric_assessments = data.get('assessments', copy.deepcopy(self.BASIC_ASSESSMENTS))
self.oa_block.rubric_assessments = update_assessments_format(
data.get('assessments', copy.deepcopy(self.BASIC_ASSESSMENTS))
)
self.oa_block.allow_file_upload = data.get('allow_file_upload')
self.oa_block.allow_latex = data.get('allow_latex')
self.oa_block.leaderboard_show = data.get('leaderboard_show', 0)
......@@ -183,7 +185,8 @@ class TestSerializeContent(TestCase):
for assessment in data['assessments']:
if 'student-training' == assessment['name'] and assessment['examples']:
xml_str = serialize_examples_to_xml_str(assessment)
self.assertIn(assessment['examples'][0]['answer'], xml_str)
for part in assessment['examples'][0]['answer']['parts']:
self.assertIn(part['text'], xml_str)
@ddt.file_data('data/serialize.json')
def test_serialize_assessments(self, data):
......@@ -228,7 +231,7 @@ class TestSerializeContent(TestCase):
def test_mutated_assessments_dict(self):
self._configure_xblock({})
for assessment_dict in self.BASIC_ASSESSMENTS:
for assessment_dict in update_assessments_format(self.BASIC_ASSESSMENTS):
for mutated_dict in self._dict_mutations(assessment_dict):
self.oa_block.rubric_assessments = [mutated_dict]
xml = serialize_content(self.oa_block)
......
......@@ -464,11 +464,21 @@ def parse_examples_xml(examples):
for example_el in examples:
example_dict = dict()
# Retrieve the answer from the training example
# Retrieve the answers from the training example
answers_list = list()
answer_elements = example_el.findall('answer')
if len(answer_elements) != 1:
raise UpdateFromXmlError(u'Each "example" element must contain exactly one "answer" element')
example_dict['answer'] = _safe_get_text(answer_elements[0])
answer_part_elements = answer_elements[0].findall('part')
if len(answer_part_elements) > 0:
for answer_part_element in answer_part_elements:
answers_list.append(_safe_get_text(answer_part_element))
else:
# Initially example answers had only one part.
answers_list.append(_safe_get_text(answer_elements[0]))
example_dict['answer'] = {"parts": [{"text": text} for text in answers_list]}
# Retrieve the options selected from the training example
example_dict['options_selected'] = []
......@@ -595,7 +605,9 @@ def serialize_training_examples(examples, assessment_el):
# Answer provided in the example (default to empty string)
answer_el = etree.SubElement(example_el, 'answer')
answer_el.text = unicode(example_dict.get('answer', ''))
for part in example_dict.get('answer', {}).get('parts', []):
part_el = etree.SubElement(answer_el, 'part')
part_el.text = unicode(part.get('text', u''))
# Options selected from the rubric
options_selected = example_dict.get('options_selected', [])
......
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