Commit 9db810a4 by Will Daly

Merge pull request #469 from edx/will/authoring/Rubric-GUI

Will/authoring/rubric gui
parents 061a4d93 29946f2e
...@@ -17,29 +17,182 @@ ...@@ -17,29 +17,182 @@
</div> </div>
<div id="oa_rubric_editor_wrapper" class="oa_editor_content_wrapper"> <div id="oa_rubric_editor_wrapper" class="oa_editor_content_wrapper">
<textarea id="openassessment_rubric_editor"></textarea>
</div>
<div id="oa_settings_editor_wrapper" class="oa_editor_content_wrapper"> <div id="openassessment_rubric_instructions">
<p class = openassessment_description>
{% trans "For open response problems, assessment is rubric-based. Rubric criterion have point breakdowns and explanations to help students with peer and self assessment steps. For more information on how to build your rubric, see our online help documentation."%}
</p>
</div>
<div id="oa_basic_settings_editor"> <ul id="openassessment_criterion_list" >
<div id="openassessment_title_editor_wrapper"> <li class="openassessment_criterion" id="openassessment_criterion_1">
<label for="openassessment_title_editor">{% trans "Display Name "%}</label> <div class="openassessment_criterion_header" id="openassessment_criterion_header_1">
<input type="text" id="openassessment_title_editor"> <input class="openassessment_display_criterion>" id="openassessment_display_criterion_1" type="checkbox" checked="1">
</div> <h6 class="openassessment_criterion_header_title">
<p class="openassessment_description">{% trans "This name appears when you hover over the unit in the course ribbon at the top of the page." %}</p> {% trans "Criterion C-C-C"%}
<hr> </h6>
<div class="openassessment_due_date_editor"> <div class="openassessment_rubric_remove_button" id="openassessment_criterion_1_remove">
<div class="openassessment_left_text_field_wrapper"> <h2>{% trans "Remove" %}</h2>
<label for="openassessment_submission_start_editor">{% trans "Response Submission Start Date"%} </label> </div>
<input type="text" class="openassessment_date_field" id="openassessment_submission_start_editor">
</div> </div>
<div class="openassessment_right_text_field_wrapper"> <div class="openassessment_criterion_body wrapper-comp-settings" id="openassessment_criterion_body_1">
<label for="openassessment_submission_due_editor">{% trans "Response Submission Due Date" %}</label> <ul class="list-input settings-list openassessment_criterion_basic_editor">
<input type="text" class="openassessment_date_field" id="openassessment_submission_due_editor"> <li class="field comp-setting-entry">
<div class="wrapper-comp-settings">
<label for="openassessment_criterion_1_name" class="openassessment_criterion_name_label setting-label">
{% trans "Criterion Name"%}
</label>
<input id="openassessment_criterion_1_name" class="openassessment_criterion_name input setting-input" type="text">
</div>
</li>
<li class="field comp-setting-entry">
<div class="wrapper-comp-settings">
<label for="openassessment_criterion_1_prompt" class="openassessment_criterion_prompt_label setting-label">
{% trans "Criterion Prompt"%}
</label>
<textarea id="openassessment_criterion_1_prompt" class="openassessment_criterion_prompt setting-input"></textarea>
</div>
</li>
</ul>
<ul id="openassessment_criterion_1_options" class="openassessment_criterion_option_list">
<li id=openassessment_criterion_1_option_1 class="openassessment_criterion_option">
<div class="openassessment_option_header">
<span class="openassessment_option_header_title">
{% trans "Option O-O-O" %}
</span>
<div class="openassessment_rubric_remove_button" id="openassessment_criterion_1_option_1_remove">
<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 for="openassessment_criterion_1_option_1_name" class="openassessment_criterion_option_name_label setting-label">
{% trans "Option Name"%}
</label>
<input id="openassessment_criterion_1_option_1_name" class="openassessment_criterion_option_name input input-label" type="text">
</div>
</li>
<li class="field comp-setting-entry openassessment_criterion_option_point_wrapper">
<div class="wrapper-comp-setting">
<label for="openassessment_criterion_1_option_1_points" class="openassessment_criterion_option_points_label setting-label">
{% trans "Option Points"%}
</label>
<input id="openassessment_criterion_1_option_1_points" class="openassessment_criterion_option_points input setting-input" type="number">
</div>
</li>
<li class="field comp-setting-entry openassessment_criterion_option_explanation_wrapper">
<div class="wrapper-comp-setting">
<label for="openassessment_criterion_1_option_1_explanation" class="openassessment_criterion_option_explanation_label setting-label">
{% trans "Option Explanation"%}
</label>
<textarea id="openassessment_criterion_1_option_1_explanation" class="openassessment_criterion_option_explanation setting-input"></textarea>
</div>
</li>
</ul>
</div>
</li>
</ul>
<div id="openassessment_criterion_1_add_option" class="openassessment_criterion_add_option openassessment_option_header">
<h2>{% trans "Add Another Option"%}</h2>
</div>
<div id="openassessment_criterion_1_feedback_wrapper" 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 for="openassessment_criterion_1_feedback" class="setting-label">{% trans "Criterion Feeedback" %}</label>
<select id="openassessment_criterion_1_feedback" class="input setting-input">
<option value="disabled">{% trans "Disabled" %}</option>
<option value="optional">{% trans "Optional" %}</option>
<option value="required">{% trans "Required" %}</option>
</select>
</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>
</ul>
<div id="openassessment_rubric_add_criterion">
<h6>
{% trans "Add Another Criterion"%}
</h6>
</div>
<div id="openassessment_rubric_feedback_wrapper" class="wrapper-comp-settings">
<div id="openassessment_rubric_feedback_header_open">
<span>
{% trans "Rubric Feedback" %}
</span>
<div class="openassessment_rubric_remove_button" id="openassessment_rubric_feedback_remove">
<h2>{% trans "Remove" %}</h2>
</div> </div>
<div id="openassessment_rubric_feedback_header_closed">
<h2>
{% trans "Add Rubric Feedback" %}
</h2>
</div>
</div> </div>
<ul class="list-input settings-list">
<li class="field comp-setting-entry">
<div class="wrapper-comp-setting" id="openassessment_rubric_feedback_input_wrapper">
<label for="openassessment_rubric_feedback" class="setting-label">{% trans "Feedback Direction" %}</label>
<textarea id="openassessment_rubric_feedback" class="input setting-input"></textarea>
</div>
</li>
<p class="setting-help">
{% trans "If you would like your students to be able to provide feedback on the rubric, add a prompt to ask them for it." %}
</p>
</ul>
</div> </div>
</div>
<div id="oa_settings_editor_wrapper" class="oa_editor_content_wrapper wrapper-comp-settings">
<ul id="oa_basic_settings_editor" class="list-input settings-list">
<li id="openassessment_title_editor_wrapper" class="field comp-setting-entry">
<div class="wrapper-comp-setting">
<label for="openassessment_title_editor" class="setting-label">{% trans "Display Name "%}</label>
<input type="text" id="openassessment_title_editor" class="input setting-input">
</div>
<p class="setting-help">{% trans "This name appears when you hover over the unit in the course ribbon at the top of the page." %}</p>
</li>
<li class="openassessment_date_editor field comp-setting-entry">
<div class="wrapper-comp-setting">
<label for="openassessment_submission_start_editor" class="setting-label">{% trans "Response Submission Start Date"%} </label>
<input type="datetime-local" class="input setting-input" id="openassessment_submission_start_editor">
</div>
<p class="setting-help">{% trans "The date at which submissions will first be accepted." %}</p>
</li>
<li class="openassessment_date_editor field comp-setting-entry">
<div class="wrapper-comp-setting">
<label for="openassessment_submission_due_editor" class="setting-label">{% trans "Response Submission Due Date" %}</label>
<input type="datetime-local" class="input setting-input" id="openassessment_submission_due_editor">
</div>
<p class="setting-help">{% trans "The date at which submissions will stop being accepted." %}</p>
</li>
<li id="openassessment_sumbission_image_wrapper" class="field comp-setting-entry">
<div class="wrapper-comp-setting">
<label for="openassessment_submission_image_editor" class="setting-label">{% trans "Allow Image Submissions"%}</label>
<select id="openassessment_submission_image_editor" class = "input setting-input" name="image submission">
<option value="0">{% trans "Disabled"%}</option>
<option value="1">{% trans "Enabled"%}</option>
</select>
</div>
<p class="setting-help">{% trans "If enabled, students will be able to submit an image along with their open response." %}</p>
</li>
</ul>
<p class="openassessment_description" id="openassessment_step_select_description"> <p class="openassessment_description" id="openassessment_step_select_description">
{% trans "Select the steps that students must complete. All steps are optional, but every assignment must include at least one step." %} {% trans "Select the steps that students must complete. All steps are optional, but every assignment must include at least one step." %}
...@@ -50,15 +203,16 @@ ...@@ -50,15 +203,16 @@
<input type="checkbox" id="include_student_training"> <input type="checkbox" id="include_student_training">
<label for="include_student_training">{% trans "Step: Student Training" %}</label> <label for="include_student_training">{% trans "Step: Student Training" %}</label>
</div> </div>
<p id="student_training_description_closed" class="openassessment_description_closed"> <div class = "openassessment_assessment_module_editor">
{% trans "Students learn to assess responses by scoring pre-assessed sample responses that the instructor provides. Students move to the next step when the scores they give match the instructor's scores." %} <p id="student_training_description_closed" class="openassessment_description_closed">
</p> {% trans "Students learn to assess responses by scoring pre-assessed sample responses that the instructor provides. Students move to the next step when the scores they give match the instructor's scores. Note that Student Training Requires that the Peer Assessment module is also selected." %}
<div id="student_training_settings_editor" class="assessment_settings_wrapper">
<p class="openassessment_description">
{% trans "Enter one or more sample responses that you've created, together with the scores you would give those responses. Be sure to format the responses and scores according to the placeholder text below." %}
</p> </p>
<label for="student_training_examples">{% trans "Student Training Responses" %}</label> <div id="student_training_settings_editor" class="assessment_settings_wrapper">
<textarea id="student_training_examples"></textarea> <p class="openassessment_description">
{% trans "Enter one or more sample responses that you've created, together with the scores you would give those responses. Be sure to format the responses and scores according to the placeholder text below." %}
</p>
<textarea id="student_training_examples"></textarea>
</div>
</div> </div>
</div> </div>
...@@ -67,30 +221,44 @@ ...@@ -67,30 +221,44 @@
<input type="checkbox" id="include_peer_assessment"> <input type="checkbox" id="include_peer_assessment">
<label for="include_peer_assessment">{% trans "Step: Peer Assessment" %}</label> <label for="include_peer_assessment">{% trans "Step: Peer Assessment" %}</label>
</div> </div>
<p id="peer_assessment_description_closed" class="openassessment_description_closed"> <div class = "openassessment_assessment_module_editor">
{% trans "Students assess a specified number of other students' responses using the rubric for the assignment." %} <p id="peer_assessment_description_closed" class="openassessment_description_closed">
</p> {% trans "Students assess a specified number of other students' responses using the rubric for the assignment." %}
<div id="peer_assessment_settings_editor" class="assessment_settings_wrapper">
<p class="openassessment_description">
{% trans "Specify the following values for the peer assessment step. The numeric grading requirements must be given a value." %}
</p> </p>
<div class="openassessment_indent_line_input"> <div id="peer_assessment_settings_editor" class="assessment_settings_wrapper">
<label for="peer_assessment_must_grade">{% trans "Each student must assess X peer responses" %}</label> <p class="openassessment_description">
<input id="peer_assessment_must_grade" class="openassessment_number_field" type="text"> {% trans "Specify the following values for the peer assessment step. The numeric grading requirements must be given a value." %}
</div> </p>
<div class="openassessment_indent_line_input"> <ul class="list-input settings-list">
<label for="peer_assessment_graded_by"> {% trans "Each response must be assessed by at least X students" %}</label> <li class="field comp-setting-entry">
<input id="peer_assessment_graded_by" class="openassessment_number_field" type="text"> <div class="wrapper-comp-setting">
</div> <label for="peer_assessment_must_grade" class="setting-label">{% trans "Must Grade" %}</label>
<div class="openassessment_due_date_editor"> <input id="peer_assessment_must_grade" class="input setting-input" type="number">
<div class="openassessment_left_text_field_wrapper"> </div>
<label for="peer_assessment_start_date">{% trans "Start Date" %}</label> <p class="setting-help">{% trans "Each student must assess this number of peer responses in order to recieve a grade."%}</p>
<input id="peer_assessment_start_date" type="text" class="openassessment_date_field"> </li>
</div> <li class="field comp-setting-entry">
<div class="openassessment_right_text_field_wrapper"> <div class="wrapper-comp-setting">
<label for="peer_assessment_due_date">{% trans "Due Date" %}</label> <label for="peer_assessment_graded_by" class="setting-label"> {% trans "Graded By" %}</label>
<input id="peer_assessment_due_date" type="text" class="openassessment_date_field"> <input id="peer_assessment_graded_by" class="input setting-input" type="number">
</div> </div>
<p class="setting-help">{% trans "Each response must be assessed by at least this many students in order to tabulate a score."%}</p>
</li>
<li class="field comp-setting-entry">
<div class="wrapper-comp-setting">
<label for="peer_assessment_start_date" class="setting-label">{% trans "Start Date" %}</label>
<input id="peer_assessment_start_date" type="datetime-local" class="input setting-input">
</div>
<p class="setting-help">{% trans "If desired, specify a start date for the peer assessment period. If no date is specified, peer assessment can begin when submissions begin."%}</p>
</li>
<li class="field comp-setting-entry">
<div class="wrapper-comp-setting">
<label for="peer_assessment_due_date" class="setting-label">{% trans "Due Date" %}</label>
<input id="peer_assessment_due_date" type="datetime-local" class="input setting-input">
</div>
<p class="setting-help">{% trans "If desired, specify a due date for the peer assessment period. If no date is specified, peer assessment can run as long as the problem is open."%}</p>
</li>
</ul>
</div> </div>
</div> </div>
</div> </div>
...@@ -100,22 +268,30 @@ ...@@ -100,22 +268,30 @@
<input id="include_self_assessment" type="checkbox"> <input id="include_self_assessment" type="checkbox">
<label for="include_self_assessment">{% trans "Step: Self Assessment" %}</label> <label for="include_self_assessment">{% trans "Step: Self Assessment" %}</label>
</div> </div>
<p id="self_assessment_description_closed" class="openassessment_description_closed"> <div class="openassessment_assessment_module_editor">
{% trans "Students assess their own responses using the rubric for the assignment." %} <p id="self_assessment_description_closed" class="openassessment_description_closed">
</p> {% trans "Students assess their own responses using the rubric for the assignment." %}
<div id="self_assessment_settings_editor" class="assessment_settings_wrapper">
<p class="openassessment_description">
{% trans "Specify start and due dates for the self assessment step. To allow self assessment to run as long as the assignment is open, leave both fields blank." %}
</p> </p>
<div class="openassessment_due_date_editor"> <div id="self_assessment_settings_editor" class="assessment_settings_wrapper">
<div class="openassessment_left_text_field_wrapper"> <p class="openassessment_description">
<label for="self_assessment_start_date">{% trans "Start Date" %}</label> {% trans "Specify start and due dates for the self assessment step. To allow self assessment to run as long as the assignment is open, leave both fields blank." %}
<input id="self_assessment_start_date" type="text" class="openassessment_date_field"> </p>
</div> <ul class="list-input settings-list">
<div class="openassessment_right_text_field_wrapper"> <li class="field comp-setting-entry">
<label for="self_assessment_due_date">{% trans "Due Date" %}</label> <div class="wrapper-comp-setting">
<input id="self_assessment_due_date" type="text" class="openassessment_date_field"> <label for="self_assessment_start_date" class="setting-label">{% trans "Start Date" %}</label>
</div> <input id="self_assessment_start_date" type="datetime-local" class="input setting-input">
</div>
<p class="setting-help">{% trans "If desired, specify a start date for the self assessment period. If no date is specified, self assessment can begin when submissions begin."%}</p>
</li>
<li class="field comp-setting-entry">
<div class="wrapper-comp-setting">
<label for="self_assessment_due_date" class="setting-label">{% trans "Due Date" %}</label>
<input id="self_assessment_due_date" type="datetime-local" class="input setting-input">
</div>
<p class="setting-help">{% trans "If desired, specify a due date for the self assessment period. If no date is specified, self assessment can run as long as the problem is open."%}</p>
</li>
</ul>
</div> </div>
</div> </div>
</div> </div>
...@@ -125,19 +301,21 @@ ...@@ -125,19 +301,21 @@
<input id="include_ai_assessment" type="checkbox"> <input id="include_ai_assessment" type="checkbox">
<label for="include_ai_assessment">{% trans "Step: Example-Based Assessment" %}</label> <label for="include_ai_assessment">{% trans "Step: Example-Based Assessment" %}</label>
</div> </div>
<p id="ai_assessment_description_closed" class="openassessment_description_closed"> <div class="openassessment_assessment_module_editor">
{% trans "An algorithm assesses students' responses by comparing the responses to pre-assessed sample responses that the instructor provides."%} <p id="ai_assessment_description_closed" class="openassessment_description_closed">
</p> {% trans "An algorithm assesses students' responses by comparing the responses to pre-assessed sample responses that the instructor provides."%}
<div id="ai_assessment_settings_editor" class="assessment_settings_wrapper">
<p class="openassessment_description">
{% trans "Enter one or more sample responses that you've created, together with the scores you would give those responses. Be sure to format the responses and scores according to the placeholder text below. The algorithm assesses students' responses by comparing them to the sample responses and scores that you provide."%}
</p> </p>
<label for="ai_training_examples">{% trans "Sample Responses" %}</label> <div id="ai_assessment_settings_editor" class="assessment_settings_wrapper">
<textarea id="ai_training_examples"></textarea> <p class="openassessment_description">
{% trans "Enter one or more sample responses that you've created, together with the scores you would give those responses. Be sure to format the responses and scores according to the placeholder text below. The algorithm assesses students' responses by comparing them to the sample responses and scores that you provide."%}
</p>
<textarea id="ai_training_examples"></textarea>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="openassessment_editor_buttons xblock-actions"> <div class="openassessment_editor_buttons xblock-actions">
......
...@@ -411,7 +411,6 @@ hr.divider, ...@@ -411,7 +411,6 @@ hr.divider,
100% { 100% {
opacity: 0.50; } } opacity: 0.50; } }
@-moz-keyframes pulse { @-moz-keyframes pulse {
0% { 0% {
opacity: 0.50; } opacity: 0.50; }
...@@ -421,7 +420,6 @@ hr.divider, ...@@ -421,7 +420,6 @@ hr.divider,
100% { 100% {
opacity: 0.50; } } opacity: 0.50; } }
@-o-keyframes pulse { @-o-keyframes pulse {
0% { 0% {
opacity: 0.50; } opacity: 0.50; }
...@@ -431,7 +429,6 @@ hr.divider, ...@@ -431,7 +429,6 @@ hr.divider,
100% { 100% {
opacity: 0.50; } } opacity: 0.50; } }
@keyframes pulse { @keyframes pulse {
0% { 0% {
opacity: 0.50; } opacity: 0.50; }
...@@ -441,7 +438,6 @@ hr.divider, ...@@ -441,7 +438,6 @@ hr.divider,
100% { 100% {
opacity: 0.50; } } opacity: 0.50; } }
.wrapper--xblock .problem__header { .wrapper--xblock .problem__header {
margin-bottom: 20px; margin-bottom: 20px;
color: #aeafb3; } color: #aeafb3; }
...@@ -2144,7 +2140,7 @@ hr.divider, ...@@ -2144,7 +2140,7 @@ hr.divider,
margin-bottom: 0; } margin-bottom: 0; }
#openassessment-editor .openassessment_editor_content_and_tabs { #openassessment-editor .openassessment_editor_content_and_tabs {
width: 100%; width: 100%;
height: 370px; } height: 373px; }
#openassessment-editor #openassessment_editor_header { #openassessment-editor #openassessment_editor_header {
background-color: #e5e5e5; background-color: #e5e5e5;
width: 100%; width: 100%;
...@@ -2161,18 +2157,30 @@ hr.divider, ...@@ -2161,18 +2157,30 @@ hr.divider,
#openassessment-editor .oa_editor_content_wrapper { #openassessment-editor .oa_editor_content_wrapper {
height: 100%; height: 100%;
width: 100%; width: 100%;
padding: 5px 10px; } border-radius: 3px;
border: 1px solid #838486;
background-color: #f5f5f5;
overflow-y: scroll; }
#openassessment-editor #openassessment_prompt_editor { #openassessment-editor #openassessment_prompt_editor {
width: 100%; width: 100%;
height: 100%; height: 100%;
resize: none; resize: none;
border: none; } border: none;
border-radius: 4px;
padding: 10px; }
#openassessment-editor #openassessment_prompt_editor textarea {
font-size: 14px;
border: none;
overflow: auto;
outline: none;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none; }
#openassessment-editor #openassessment_rubric_editor { #openassessment-editor #openassessment_rubric_editor {
width: 100%; width: 100%;
height: 100%; } height: 100%; }
#openassessment-editor #oa_basic_settings_editor { #openassessment-editor #oa_basic_settings_editor {
padding: 20px 20px; border-bottom: 1px solid #838486; }
border-bottom: 1px solid #414243; }
#openassessment-editor #oa_basic_settings_editor #openassessment_title_editor_wrapper label { #openassessment-editor #oa_basic_settings_editor #openassessment_title_editor_wrapper label {
width: 25%; width: 25%;
text-align: left; } text-align: left; }
...@@ -2180,39 +2188,20 @@ hr.divider, ...@@ -2180,39 +2188,20 @@ hr.divider,
width: 45%; width: 45%;
min-width: 100px; } min-width: 100px; }
#openassessment-editor #openassessment_step_select_description { #openassessment-editor #openassessment_step_select_description {
margin: 10px 0; } padding: 10px 10px 0 10px;
text-align: center;
font-size: 80%; }
#openassessment-editor .openassessment_assessment_module_settings_editor { #openassessment-editor .openassessment_assessment_module_settings_editor {
margin-bottom: 10px; overflow-y: scroll;
padding-bottom: 10px; padding: 5px;
border-bottom: 1px solid #dadbdc; } margin: 10px;
#openassessment-editor .openassessment_indent_line_input { border: 1px solid lightgray;
padding: 5px 20px; } border-radius: 3px; }
#openassessment-editor #oa_settings_editor_wrapper {
overflow-y: scroll; }
#openassessment-editor #openassessment_title_editor {
width: 300px;
margin-left: 50px; }
#openassessment-editor .openassessment_description, #openassessment-editor .openassessment_description_closed { #openassessment-editor .openassessment_description, #openassessment-editor .openassessment_description_closed {
font-size: 75%; font-size: 75%;
margin: 0; } margin: 0; }
#openassessment-editor .openassessment_date_field {
width: 130px; }
#openassessment-editor .openassessment_number_field {
width: 25px; }
#openassessment-editor .openassessment_text_field_wrapper, #openassessment-editor .openassessment_right_text_field_wrapper, #openassessment-editor .openassessment_left_text_field_wrapper {
width: 50%;
text-align: center; }
#openassessment-editor .openassessment_right_text_field_wrapper {
float: right; }
#openassessment-editor .openassessment_left_text_field_wrapper {
float: left; }
#openassessment-editor .openassessment_due_date_editor {
height: 30px; }
#openassessment-editor .openassessment_inclusion_wrapper { #openassessment-editor .openassessment_inclusion_wrapper {
background-color: #dadbdc; margin: 2.5px 5px; }
padding: 2.5px 5px;
margin: 2.5px 5px;
border-radius: 2.5px; }
#openassessment-editor .openassessment_inclusion_wrapper input[type="checkbox"] { #openassessment-editor .openassessment_inclusion_wrapper input[type="checkbox"] {
display: none; } display: none; }
#openassessment-editor .openassessment_inclusion_wrapper input[type="checkbox"] + label:before { #openassessment-editor .openassessment_inclusion_wrapper input[type="checkbox"] + label:before {
...@@ -2223,7 +2212,8 @@ hr.divider, ...@@ -2223,7 +2212,8 @@ hr.divider,
height: auto; height: auto;
content: "\f096"; } content: "\f096"; }
#openassessment-editor .openassessment_inclusion_wrapper input[type="checkbox"]:checked + label:before { #openassessment-editor .openassessment_inclusion_wrapper input[type="checkbox"]:checked + label:before {
content: "\f046"; } content: "\f046";
color: #009fe6; }
#openassessment-editor label { #openassessment-editor label {
padding-right: 10px; } padding-right: 10px; }
#openassessment-editor .xblock_actions { #openassessment-editor .xblock_actions {
...@@ -2244,6 +2234,203 @@ hr.divider, ...@@ -2244,6 +2234,203 @@ hr.divider,
color: whitesmoke; color: whitesmoke;
text-transform: uppercase; text-transform: uppercase;
outline-color: transparent; } outline-color: transparent; }
#openassessment-editor .openassessment_assessment_module_editor {
padding: 2.5px 0px; }
#openassessment-editor .openassessment_assessment_module_editor .openassessment_description, #openassessment-editor .openassessment_assessment_module_editor .openassessment_description_closed {
padding-left: 15px; }
#openassessment-editor #oa_rubric_editor_wrapper .wrapper-comp-settings {
display: initial; }
#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_instructions {
background-color: #c8c9ca;
border-bottom: 1px solid #414243;
padding: 10px; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion {
padding-bottom: 10px; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_header {
margin: 10px;
padding: 5px;
border-bottom: 1px solid #414243;
overflow: auto; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_header input[type="checkbox"] {
display: none; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_header input[type="checkbox"] + h6:before {
font-family: "FontAwesome";
display: inline-block;
margin-right: 10px;
width: auto;
height: auto;
content: "\f054"; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_header input[type="checkbox"]:checked + label:before {
content: "\f078"; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_header .openassessment_criterion_header_title {
text-transform: uppercase;
width: 50%;
display: inline-block;
float: left; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_add_option {
background-color: #838486;
padding: 5px;
margin: 0px 10px 10px 10px;
border-radius: 3px; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_add_option h2:before {
font-family: FontAwesome;
content: "\f067";
display: inline-block;
margin: 0 5px; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry {
padding-right: 0;
margin-right: 10px;
overflow: auto; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings input {
font-size: 11px;
float: right;
width: 70%; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt {
padding: 10px;
width: 70%;
float: right; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings label {
padding: 0;
margin: 0; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper .openassessment_criterion_feedback_header {
background-color: #c8c9ca;
padding: 5px;
margin: 10px;
border-radius: 3px; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper .openassessment_criterion_feedback_header .openassessment_criterion_feedback_header_closed:before {
font-family: "FontAwesome";
display: inline-block;
margin-right: 10px;
margin-left: 5px;
width: auto;
height: auto;
content: "\f067"; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_feedback_wrapper .openassessment_criterion_feedback_direction label {
margin-left: 15px; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option {
padding: 5px; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header {
background-color: #c8c9ca;
padding: 5px 5px 5px 10px;
margin: 5px 5px 8px 5px;
border-radius: 3px; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper {
width: 40%;
border-top: none;
padding: 5px 5px 5px 0px;
float: left;
margin: 0; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper label {
width: 40%;
vertical-align: middle;
padding: 0;
margin: 0; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_point_wrapper input {
padding: 10px;
float: right;
width: 55%;
font-size: 11px; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_name_wrapper {
float: left;
width: 60%;
padding: 5px 10px 5px 20px;
border-top: 0;
margin: 0; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_name_wrapper label {
width: 25%;
vertical-align: middle;
padding: 0;
margin: 0; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_name_wrapper input {
padding: 10px;
font-size: 11px;
width: 60%;
float: right; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper {
padding: 10px 5px 0px 20px;
width: 100%;
display: inline-block;
margin: 0; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper label {
width: 25%;
text-align: left; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper textarea {
padding: 10px;
width: 70%;
float: right; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_line_input {
padding: 10px;
overflow: auto; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_line_input label {
width: 30%;
text-align: left; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_line_input input {
width: 70%;
min-width: auto;
float: right; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_line_input textarea {
width: 70%;
float: right; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_large_text_input, #openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt, #openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper textarea, #openassessment-editor #oa_rubric_editor_wrapper .openassessment_line_input textarea {
height: 70px;
width: 70%; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_input_styling, #openassessment-editor #oa_rubric_editor_wrapper .openassessment_large_text_input, #openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_basic_editor .comp-setting-entry .wrapper-comp-settings .openassessment_criterion_prompt, #openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_criterion_option_explanation_wrapper textarea, #openassessment-editor #oa_rubric_editor_wrapper .openassessment_line_input textarea {
resize: none;
box-sizing: border-box;
border: 1px solid #b2b2b2;
border-radius: 2px;
padding: 6px 8px 8px;
background-color: #f2f2f2;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
font-family: 'Open Sans', sans-serif;
font-size: 11px;
color: #4c4c4c;
outline: 0; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_rubric_remove_button, #openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_header .openassessment_criterion_header_remove, #openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove {
margin: 0 5px;
float: right; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_rubric_remove_button h2:after, #openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_header .openassessment_criterion_header_remove h2:after, #openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove h2:after {
font-family: FontAwesome;
content: "\f00d";
display: inline-block;
color: inherit;
margin: 0 5px; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_rubric_remove_button h2, #openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion .openassessment_criterion_header .openassessment_criterion_header_remove h2, #openassessment-editor #oa_rubric_editor_wrapper .openassessment_criterion_option .openassessment_option_header .openassessment_option_header_remove h2 {
text-transform: uppercase;
font-size: 80%;
float: right;
display: inline-block; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_rubric_feedback_wrapper {
padding: 0 10px; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_rubric_feedback_wrapper .openassessment_rubric_feedback_header {
margin-top: 10px;
border-bottom: 1px solid #414243;
font-size: 125%;
padding: 10px;
padding-right: 20px; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_rubric_feedback_wrapper .openassessment_feedback_radio_toggle input[type="radio"] {
display: none; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_rubric_feedback_wrapper .openassessment_feedback_radio_toggle input[type="radio"] + label:before {
font-family: "FontAwesome";
display: inline-block;
margin-right: 10px;
width: auto;
height: auto;
content: "\f10c"; }
#openassessment-editor #oa_rubric_editor_wrapper .openassessment_rubric_feedback_wrapper .openassessment_feedback_radio_toggle input[type="radio"]:checked + label:before {
content: "\f05d"; }
#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_add_criterion {
background-color: #838486;
padding: 10px;
margin: 10px, 0; }
#openassessment-editor #oa_rubric_editor_wrapper #openassessment_rubric_add_criterion h6:before {
font-family: "FontAwesome";
display: inline-block;
margin-left: 5px;
margin-right: 10px;
width: auto;
height: auto;
content: "\f067"; }
#openassessment-editor hr { #openassessment-editor hr {
background-color: transparent; background-color: transparent;
color: #414243; color: #414243;
...@@ -2252,7 +2439,8 @@ hr.divider, ...@@ -2252,7 +2439,8 @@ hr.divider,
clear: both; } clear: both; }
.modal-content { .modal-content {
height: 470px !important; } height: 470px !important;
background-color: #e5e5e5; }
.openassessment .self-assessment__display__header, .openassessment .peer-assessment__display__header, .openassessment .step__header { .openassessment .self-assessment__display__header, .openassessment .peer-assessment__display__header, .openassessment .step__header {
margin-bottom: 0 !important; margin-bottom: 0 !important;
......
if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}OpenAssessment.BaseView=function(runtime,element,server){this.runtime=runtime;this.element=element;this.server=server;this.responseView=new OpenAssessment.ResponseView(this.element,this.server,this);this.trainingView=new OpenAssessment.StudentTrainingView(this.element,this.server,this);this.selfView=new OpenAssessment.SelfView(this.element,this.server,this);this.peerView=new OpenAssessment.PeerView(this.element,this.server,this);this.gradeView=new OpenAssessment.GradeView(this.element,this.server,this);this.messageView=new OpenAssessment.MessageView(this.element,this.server,this);this.staffInfoView=new OpenAssessment.StaffInfoView(this.element,this.server,this)};OpenAssessment.BaseView.prototype={scrollToTop:function(){if($.scrollTo instanceof Function){$(window).scrollTo($("#openassessment__steps"),800,{offset:-50})}},setUpCollapseExpand:function(parentSel,onExpand){parentSel.find(".ui-toggle-visibility__control").click(function(eventData){var sel=$(eventData.target).closest(".ui-toggle-visibility");if(sel.hasClass("is--collapsed")&&onExpand!==undefined){onExpand()}sel.toggleClass("is--collapsed")})},load:function(){this.responseView.load();this.loadAssessmentModules();this.staffInfoView.load()},loadAssessmentModules:function(){this.trainingView.load();this.peerView.load();this.selfView.load();this.gradeView.load()},loadMessageView:function(){this.messageView.load()},toggleActionError:function(type,msg){var element=this.element;var container=null;if(type=="save"){container=".response__submission__actions"}else if(type=="submit"||type=="peer"||type=="self"||type=="student-training"){container=".step__actions"}else if(type=="feedback_assess"){container=".submission__feedback__actions"}if(container===null){if(msg!==null){console.log(msg)}}else{var msgHtml=msg===null?"":msg;$(container+" .message__content",element).html("<p>"+msgHtml+"</p>");$(container,element).toggleClass("has--error",msg!==null)}},showLoadError:function(step){var container="#openassessment__"+step;$(container).toggleClass("has--error",true);$(container+" .step__status__value i").removeClass().addClass("ico icon-warning-sign");$(container+" .step__status__value .copy").html(gettext("Unable to Load"))}};function OpenAssessmentBlock(runtime,element){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.BaseView(runtime,element,server);view.load()}OpenAssessment.StudioView=function(runtime,element,server){this.runtime=runtime;this.server=server;var liveElement=$(element);this.promptBox=$("#openassessment_prompt_editor",liveElement).get(0);this.titleField=$("#openassessment_title_editor",liveElement).first().get(0);this.submissionStartField=$("#openassessment_submission_start_editor",liveElement).first().get(0);this.submissionDueField=$("#openassessment_submission_due_editor",liveElement).first().get(0);this.hasPeer=$("#include_peer_assessment",liveElement);this.hasSelf=$("#include_self_assessment",liveElement);this.hasAI=$("#include_ai_assessment",liveElement);this.hasTraining=$("#include_student_training",liveElement);this.peerMustGrade=$("#peer_assessment_must_grade",liveElement);this.peerGradedBy=$("#peer_assessment_graded_by",liveElement);this.peerStart=$("#peer_assessment_start_date",liveElement);this.peerDue=$("#peer_assessment_due_date",liveElement);this.selfStart=$("#self_assessment_start_date",liveElement);this.selfDue=$("#self_assessment_due_date",liveElement);this.rubricXmlBox=CodeMirror.fromTextArea($("#openassessment_rubric_editor",liveElement).first().get(0),{mode:"xml",lineNumbers:true,lineWrapping:true});this.aiTrainingExamplesCodeBox=CodeMirror.fromTextArea($("#ai_training_examples",liveElement).first().get(0),{mode:"xml",lineNumbers:true,lineWrapping:true});this.studentTrainingExamplesCodeBox=CodeMirror.fromTextArea($("#student_training_examples",liveElement).first().get(0),{mode:"xml",lineNumbers:true,lineWrapping:true});var view=this;$(".openassessment_save_button",liveElement).click(function(eventData){view.save()});$(".openassessment_cancel_button",liveElement).click(function(eventData){view.cancel()});$(".openassessment_editor_content_and_tabs",liveElement).tabs({activate:function(event,ui){view.rubricXmlBox.refresh()}});$("#include_peer_assessment",liveElement).change(function(){if(this.checked){$("#peer_assessment_description_closed",liveElement).fadeOut("fast");$("#peer_assessment_settings_editor",liveElement).fadeIn()}else{$("#peer_assessment_settings_editor",liveElement).fadeOut("fast");$("#peer_assessment_description_closed",liveElement).fadeIn()}});$("#include_self_assessment",liveElement).change(function(){if(this.checked){$("#self_assessment_description_closed",liveElement).fadeOut("fast");$("#self_assessment_settings_editor",liveElement).fadeIn()}else{$("#self_assessment_settings_editor",liveElement).fadeOut("fast");$("#self_assessment_description_closed",liveElement).fadeIn()}});$("#include_ai_assessment",liveElement).change(function(){if(this.checked){$("#ai_assessment_description_closed",liveElement).fadeOut("fast");$("#ai_assessment_settings_editor",liveElement).fadeIn()}else{$("#ai_assessment_settings_editor",liveElement).fadeOut("fast");$("#ai_assessment_description_closed",liveElement).fadeIn()}});$("#include_student_training",liveElement).change(function(){if(this.checked){$("#student_training_description_closed",liveElement).fadeOut("fast");$("#student_training_settings_editor",liveElement).fadeIn()}else{$("#student_training_settings_editor",liveElement).fadeOut("fast");$("#student_training_description_closed",liveElement).fadeIn()}})};OpenAssessment.StudioView.prototype={load:function(){var view=this;this.server.loadEditorContext().done(function(prompt,rubricXml,title,subStart,subDue,assessments){view.rubricXmlBox.setValue(rubricXml);view.submissionStartField.value=subStart;view.submissionDueField.value=subDue;view.promptBox.value=prompt;view.titleField.value=title;view.hasTraining.prop("checked",false).change();view.hasPeer.prop("checked",false).change();view.hasSelf.prop("checked",false).change();view.hasAI.prop("checked",false).change();for(var i=0;i<assessments.length;i++){var assessment=assessments[i];if(assessment.name=="peer-assessment"){view.peerMustGrade.prop("value",assessment.must_grade);view.peerGradedBy.prop("value",assessment.must_be_graded_by);view.peerStart.prop("value",assessment.start);view.peerDue.prop("value",assessment.due);view.hasPeer.prop("checked",true).change()}else if(assessment.name=="self-assessment"){view.selfStart.prop("value",assessment.start);view.selfDue.prop("value",assessment.due);view.hasSelf.prop("checked",true).change()}else if(assessment.name=="example-based-assessment"){view.aiTrainingExamplesCodeBox.setValue(assessment.examples);view.hasAI.prop("checked",true).change()}else if(assessment.name=="student-training"){view.studentTrainingExamplesCodeBox.setValue(assessment.examples);view.hasTraining.prop("checked",true).change()}}}).fail(function(msg){view.showError(msg)})},save:function(){var view=this;this.server.checkReleased().done(function(isReleased){if(isReleased){view.confirmPostReleaseUpdate($.proxy(view.updateEditorContext,view))}else{view.updateEditorContext()}}).fail(function(errMsg){view.showError(msg)})},confirmPostReleaseUpdate:function(onConfirm){var msg=gettext("This problem has already been released. Any changes will apply only to future assessments.");if(confirm(msg)){onConfirm()}},updateEditorContext:function(){this.runtime.notify("save",{state:"start"});var prompt=this.promptBox.value;var rubricXml=this.rubricXmlBox.getValue();var title=this.titleField.value;var subStart=this.submissionStartField.value;var subDue=this.submissionDueField.value;var assessments=[];if(this.hasTraining.prop("checked")){assessments[assessments.length]={name:"student-training",examples:this.studentTrainingExamplesCodeBox.getValue()}}if(this.hasPeer.prop("checked")){var assessment={name:"peer-assessment",must_grade:parseInt(this.peerMustGrade.prop("value")),must_be_graded_by:parseInt(this.peerGradedBy.prop("value"))};var startStr=this.peerStart.prop("value");var dueStr=this.peerDue.prop("value");if(startStr){assessment=$.extend(assessment,{start:startStr})}if(dueStr){assessment=$.extend(assessment,{due:dueStr})}assessments[assessments.length]=assessment}if(this.hasSelf.prop("checked")){assessment={name:"self-assessment"};startStr=this.selfStart.prop("value");dueStr=this.selfDue.prop("value");if(startStr){assessment=$.extend(assessment,{start:startStr})}if(dueStr){assessment=$.extend(assessment,{due:dueStr})}assessments[assessments.length]=assessment}if(this.hasAI.prop("checked")){assessments[assessments.length]={name:"example-based-assessment",examples:this.aiTrainingExamplesCodeBox.getValue()}}var view=this;this.server.updateEditorContext(prompt,rubricXml,title,subStart,subDue,assessments).done(function(){view.runtime.notify("save",{state:"end"});view.load()}).fail(function(msg){view.showError(msg)})},cancel:function(){this.runtime.notify("cancel",{})},showError:function(errorMsg){this.runtime.notify("error",{msg:errorMsg})}};function OpenAssessmentEditor(runtime,element){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.StudioView(runtime,element,server);view.load()}OpenAssessment.GradeView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.GradeView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("grade").done(function(html){$("#openassessment__grade",view.element).replaceWith(html);view.installHandlers()}).fail(function(errMsg){baseView.showLoadError("grade",errMsg)})},installHandlers:function(){var sel=$("#openassessment__grade",this.element);this.baseView.setUpCollapseExpand(sel);var view=this;sel.find("#feedback__submit").click(function(eventObject){eventObject.preventDefault();view.submitFeedbackOnAssessment()})},feedbackText:function(text){if(typeof text==="undefined"){return $("#feedback__remarks__value",this.element).val()}else{$("#feedback__remarks__value",this.element).val(text)}},feedbackOptions:function(options){var view=this;if(typeof options==="undefined"){return $.map($(".feedback__overall__value:checked",view.element),function(element,index){return $(element).val()})}else{$(".feedback__overall__value",this.element).prop("checked",false);$.each(options,function(index,opt){$("#feedback__overall__value--"+opt,view.element).prop("checked",true)})}},setHidden:function(sel,hidden){sel.toggleClass("is--hidden",hidden);sel.attr("aria-hidden",hidden?"true":"false")},isHidden:function(sel){return sel.hasClass("is--hidden")&&sel.attr("aria-hidden")=="true"},feedbackState:function(newState){var containerSel=$(".submission__feedback__content",this.element);var instructionsSel=containerSel.find(".submission__feedback__instructions");var fieldsSel=containerSel.find(".submission__feedback__fields");var actionsSel=containerSel.find(".submission__feedback__actions");var transitionSel=containerSel.find(".transition__status");var messageSel=containerSel.find(".message--complete");if(typeof newState==="undefined"){var isSubmitting=containerSel.hasClass("is--transitioning")&&containerSel.hasClass("is--submitting")&&!this.isHidden(transitionSel)&&this.isHidden(messageSel)&&this.isHidden(instructionsSel)&&this.isHidden(fieldsSel)&&this.isHidden(actionsSel);var hasSubmitted=containerSel.hasClass("is--submitted")&&this.isHidden(transitionSel)&&!this.isHidden(messageSel)&&this.isHidden(instructionsSel)&&this.isHidden(fieldsSel)&&this.isHidden(actionsSel);var isOpen=!containerSel.hasClass("is--submitted")&&!containerSel.hasClass("is--transitioning")&&!containerSel.hasClass("is--submitting")&&this.isHidden(transitionSel)&&this.isHidden(messageSel)&&!this.isHidden(instructionsSel)&&!this.isHidden(fieldsSel)&&!this.isHidden(actionsSel);if(isOpen){return"open"}else if(isSubmitting){return"submitting"}else if(hasSubmitted){return"submitted"}else{throw"Invalid feedback state"}}else{if(newState=="open"){containerSel.toggleClass("is--transitioning",false);containerSel.toggleClass("is--submitting",false);containerSel.toggleClass("is--submitted",false);this.setHidden(instructionsSel,false);this.setHidden(fieldsSel,false);this.setHidden(actionsSel,false);this.setHidden(transitionSel,true);this.setHidden(messageSel,true)}else if(newState=="submitting"){containerSel.toggleClass("is--transitioning",true);containerSel.toggleClass("is--submitting",true);containerSel.toggleClass("is--submitted",false);this.setHidden(instructionsSel,true);this.setHidden(fieldsSel,true);this.setHidden(actionsSel,true);this.setHidden(transitionSel,false);this.setHidden(messageSel,true)}else if(newState=="submitted"){containerSel.toggleClass("is--transitioning",false);containerSel.toggleClass("is--submitting",false);containerSel.toggleClass("is--submitted",true);this.setHidden(instructionsSel,true);this.setHidden(fieldsSel,true);this.setHidden(actionsSel,true);this.setHidden(transitionSel,true);this.setHidden(messageSel,false)}}},submitFeedbackOnAssessment:function(){var view=this;var baseView=this.baseView;$("#feedback__submit",this.element).toggleClass("is--disabled",true);view.feedbackState("submitting");this.server.submitFeedbackOnAssessment(this.feedbackText(),this.feedbackOptions()).done(function(){view.feedbackState("submitted")}).fail(function(errMsg){baseView.toggleActionError("feedback_assess",errMsg)})}};OpenAssessment.MessageView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.MessageView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("message").done(function(html){$("#openassessment__message",view.element).replaceWith(html)}).fail(function(errMsg){baseView.showLoadError("message",errMsg)})}};OpenAssessment.PeerView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.PeerView.prototype={load:function(){var view=this;this.server.render("peer_assessment").done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.installHandlers(false)}).fail(function(errMsg){view.baseView.showLoadError("peer-assessment")});view.baseView.loadMessageView()},loadContinuedAssessment:function(){var view=this;this.server.renderContinuedPeer().done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.installHandlers(true)}).fail(function(errMsg){view.baseView.showLoadError("peer-assessment")})},installHandlers:function(isContinuedAssessment){var sel=$("#openassessment__peer-assessment",this.element);var view=this;this.baseView.setUpCollapseExpand(sel,$.proxy(view.loadContinuedAssessment,view));var rubricSelector=$("#peer-assessment--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(view.peerSubmitEnabled,view))}sel.find("#peer-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();if(!isContinuedAssessment){view.peerAssess()}else{view.continuedPeerAssess()}})},peerSubmitEnabled:function(enabled){var button=$("#peer-assessment--001__assessment__submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled)}},peerAssess:function(){var view=this;var baseView=view.baseView;this.peerAssessRequest(function(){view.load();baseView.loadAssessmentModules();baseView.scrollToTop()})},continuedPeerAssess:function(){var view=this;var gradeView=this.baseView.gradeView;var baseView=view.baseView;view.peerAssessRequest(function(){view.loadContinuedAssessment();gradeView.load();baseView.scrollToTop()})},peerAssessRequest:function(successFunction){var view=this;view.baseView.toggleActionError("peer",null);view.peerSubmitEnabled(false);this.server.peerAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.overallFeedback()).done(successFunction).fail(function(errMsg){view.baseView.toggleActionError("peer",errMsg);view.peerSubmitEnabled(true)})},overallFeedback:function(overallFeedback){var selector="#assessment__rubric__question--feedback__value";if(typeof overallFeedback==="undefined"){return $(selector,this.element).val()}else{$(selector,this.element).val(overallFeedback)}}};OpenAssessment.ResponseView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.savedResponse="";this.lastChangeTime=Date.now();this.errorOnLastSave=false;this.autoSaveTimerId=null};OpenAssessment.ResponseView.prototype={AUTO_SAVE_POLL_INTERVAL:2e3,AUTO_SAVE_WAIT:3e4,load:function(){var view=this;this.server.render("submission").done(function(html){$("#openassessment__response",view.element).replaceWith(html);view.installHandlers();view.setAutoSaveEnabled(true)}).fail(function(errMsg){view.baseView.showLoadError("response")})},installHandlers:function(){var sel=$("#openassessment__response",this.element);var view=this;this.baseView.setUpCollapseExpand(sel);this.savedResponse=this.response();var handleChange=function(eventData){view.handleResponseChanged()};sel.find("#submission__answer__value").on("change keyup drop paste",handleChange);sel.find("#step--response__submit").click(function(eventObject){eventObject.preventDefault();view.submit()});sel.find("#submission__save").click(function(eventObject){eventObject.preventDefault();view.save()})},setAutoSaveEnabled:function(enabled){if(enabled){if(this.autoSaveTimerId===null){this.autoSaveTimerId=setInterval($.proxy(this.autoSave,this),this.AUTO_SAVE_POLL_INTERVAL)}}else{if(this.autoSaveTimerId!==null){clearInterval(this.autoSaveTimerId)}}},submitEnabled:function(enabled){var sel=$("#step--response__submit",this.element);if(typeof enabled==="undefined"){return!sel.hasClass("is--disabled")}else{sel.toggleClass("is--disabled",!enabled)}},saveEnabled:function(enabled){var sel=$("#submission__save",this.element);if(typeof enabled==="undefined"){return!sel.hasClass("is--disabled")}else{sel.toggleClass("is--disabled",!enabled)}},saveStatus:function(msg){var sel=$("#response__save_status h3",this.element);if(typeof msg==="undefined"){return sel.text()}else{var label=gettext("Status of Your Response");sel.html('<span class="sr">'+label+":"+"</span>\n"+msg)}},unsavedWarningEnabled:function(enabled){if(typeof enabled==="undefined"){return window.onbeforeunload!==null}else{if(enabled){window.onbeforeunload=function(){return gettext("If you leave this page without saving or submitting your response, you'll lose any work you've done on the response.")}}else{window.onbeforeunload=null}}},response:function(text){var sel=$("#submission__answer__value",this.element);if(typeof text==="undefined"){return sel.val()}else{sel.val(text)}},responseChanged:function(){var currentResponse=$.trim(this.response());var savedResponse=$.trim(this.savedResponse);return savedResponse!==currentResponse},autoSave:function(){var timeSinceLastChange=Date.now()-this.lastChangeTime;if(this.responseChanged()&&timeSinceLastChange>this.AUTO_SAVE_WAIT&&!this.errorOnLastSave){this.save()}},handleResponseChanged:function(){var isBlank=$.trim(this.response())!=="";this.submitEnabled(isBlank);if(this.responseChanged()){this.saveEnabled(isBlank);this.saveStatus(gettext("This response has not been saved."));this.unsavedWarningEnabled(true)}this.lastChangeTime=Date.now()},save:function(){this.errorOnLastSave=false;this.saveStatus(gettext("Saving..."));this.baseView.toggleActionError("save",null);this.unsavedWarningEnabled(false);var view=this;var savedResponse=this.response();this.server.save(savedResponse).done(function(){view.savedResponse=savedResponse;var currentResponse=view.response();view.submitEnabled(currentResponse!=="");if(currentResponse==savedResponse){view.saveEnabled(false);view.saveStatus(gettext("This response has been saved but not submitted."))}}).fail(function(errMsg){view.saveStatus(gettext("Error"));view.baseView.toggleActionError("save",errMsg);view.errorOnLastSave=true})},submit:function(){this.submitEnabled(false);var view=this;var baseView=this.baseView;this.confirmSubmission().pipe(function(){var submission=$("#submission__answer__value",view.element).val();baseView.toggleActionError("response",null);return view.server.submit(submission)}).done($.proxy(view.moveToNextStep,view)).fail(function(errCode,errMsg){if(errCode=="ENOMULTI"){view.moveToNextStep()}else{if(errMsg){baseView.toggleActionError("submit",errMsg)}view.submitEnabled(true)}})},moveToNextStep:function(){this.load();this.baseView.loadAssessmentModules();this.unsavedWarningEnabled(false)},confirmSubmission:function(){var msg="You're about to submit your response for this assignment. "+"After you submit this response, you can't change it or submit a new response.";return $.Deferred(function(defer){if(confirm(msg)){defer.resolve()}else{defer.reject()}})}};OpenAssessment.Rubric=function(element){this.element=element};OpenAssessment.Rubric.prototype={criterionFeedback:function(criterionFeedback){var selector="textarea.answer__value";var feedback={};$(selector,this.element).each(function(index,sel){if(typeof criterionFeedback!=="undefined"){$(sel).val(criterionFeedback[sel.name]);feedback[sel.name]=criterionFeedback[sel.name]}else{feedback[sel.name]=$(sel).val()}});return feedback},optionsSelected:function(optionsSelected){var selector="input[type=radio]";if(typeof optionsSelected==="undefined"){var options={};$(selector+":checked",this.element).each(function(index,sel){options[sel.name]=sel.value});return options}else{$(selector,this.element).prop("checked",false);$(selector,this.element).each(function(index,sel){if(optionsSelected.hasOwnProperty(sel.name)){if(sel.value==optionsSelected[sel.name]){$(sel).prop("checked",true)}}})}},canSubmitCallback:function(callback){$(this.element).change(function(){var numChecked=$("input[type=radio]:checked",this).length;var numAvailable=$(".field--radio.assessment__rubric__question",this).length;var canSubmit=numChecked==numAvailable;callback(canSubmit)})},showCorrections:function(corrections){var selector="input[type=radio]";var hasErrors=false;$(selector,this.element).each(function(index,sel){var listItem=$(sel).parents(".assessment__rubric__question");if(corrections.hasOwnProperty(sel.name)){hasErrors=true;listItem.find(".message--incorrect").removeClass("is--hidden");listItem.find(".message--correct").addClass("is--hidden")}else{listItem.find(".message--correct").removeClass("is--hidden");listItem.find(".message--incorrect").addClass("is--hidden")}});return hasErrors}};OpenAssessment.SelfView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.SelfView.prototype={load:function(){var view=this;this.server.render("self_assessment").done(function(html){$("#openassessment__self-assessment",view.element).replaceWith(html);view.installHandlers()}).fail(function(errMsg){view.showLoadError("self-assessment")})},installHandlers:function(){var view=this;var sel=$("#openassessment__self-assessment",view.element);this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#self-assessment--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(this.selfSubmitEnabled,this))}sel.find("#self-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.selfAssess()})},selfSubmitEnabled:function(enabled){var button=$("#self-assessment--001__assessment__submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled)}},selfAssess:function(){var view=this;var baseView=this.baseView;baseView.toggleActionError("self",null);view.selfSubmitEnabled(false);var options=this.rubric.optionsSelected();this.server.selfAssess(options).done(function(){baseView.loadAssessmentModules();baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("self",errMsg);view.selfSubmitEnabled(true)})}};OpenAssessment.Server=function(runtime,element){this.runtime=runtime;this.element=element};OpenAssessment.Server.prototype={url:function(handler){return this.runtime.handlerUrl(this.element,handler)},render:function(component){var url=this.url("render_"+component);return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(this,[data])}).fail(function(data){defer.rejectWith(this,[gettext("This section could not be loaded.")])})}).promise()},renderContinuedPeer:function(){var url=this.url("render_peer_assessment");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:{continue_grading:true}}).done(function(data){defer.resolveWith(this,[data])}).fail(function(data){defer.rejectWith(this,[gettext("This section could not be loaded.")])})}).promise()},studentInfo:function(student_id){var url=this.url("render_student_info");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:{student_id:student_id}}).done(function(data){defer.resolveWith(this,[data])}).fail(function(data){defer.rejectWith(this,[gettext("This section could not be loaded.")])})}).promise()},submit:function(submission){var url=this.url("submit");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission})}).done(function(data){var success=data[0];if(success){var studentId=data[1];var attemptNum=data[2];defer.resolveWith(this,[studentId,attemptNum])}else{var errorNum=data[1];var errorMsg=data[2];defer.rejectWith(this,[errorNum,errorMsg])}}).fail(function(data){defer.rejectWith(this,["AJAX",gettext("This response could not be submitted.")])})}).promise()},save:function(submission){var url=this.url("save_submission");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission})}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This response could not be saved.")])})}).promise()},submitFeedbackOnAssessment:function(text,options){var url=this.url("submit_feedback");var payload=JSON.stringify({feedback_text:text,feedback_options:options});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This feedback could not be submitted.")])})}).promise()},peerAssess:function(optionsSelected,criterionFeedback,overallFeedback){var url=this.url("peer_assess");var payload=JSON.stringify({options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})}).promise()},selfAssess:function(optionsSelected){var url=this.url("self_assess");var payload=JSON.stringify({options_selected:optionsSelected});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},trainingAssess:function(optionsSelected){var url=this.url("training_assess");var payload=JSON.stringify({options_selected:optionsSelected});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolveWith(this,[data.corrections])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},scheduleTraining:function(){var url=this.url("schedule_training");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""'}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},rescheduleUnfinishedTasks:function(){var url=this.url("reschedule_unfinished_tasks");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""'}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("One or more rescheduling tasks failed.")])})})},loadEditorContext:function(){var url=this.url("editor_context");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""'}).done(function(data){if(data.success){defer.resolveWith(this,[data.prompt,data.rubric,data.title,data.submission_start,data.submission_due,data.assessments])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This problem could not be loaded.")])})}).promise()},updateEditorContext:function(prompt,rubricXml,title,sub_start,sub_due,assessments){var url=this.url("update_editor_context");var payload=JSON.stringify({prompt:prompt,rubric:rubricXml,title:title,submission_start:sub_start,submission_due:sub_due,assessments:assessments});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This problem could not be saved.")])})}).promise()},checkReleased:function(){var url=this.url("check_released");var payload='""';return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolveWith(this,[data.is_released])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("The server could not be contacted.")])})}).promise()}};if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}OpenAssessment.StaffInfoView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.StaffInfoView.prototype={load:function(){var view=this;if($("#openassessment__staff-info",view.element).length>0){this.server.render("staff_info").done(function(html){$("#openassessment__staff-info",view.element).replaceWith(html);view.installHandlers()}).fail(function(errMsg){view.baseView.showLoadError("staff_info")})}},loadStudentInfo:function(){var view=this;var sel=$("#openassessment__staff-info",this.element);var student_id=sel.find("#openassessment__student_id").val();this.server.studentInfo(student_id).done(function(html){$("#openassessment__student-info",view.element).replaceWith(html)}).fail(function(errMsg){view.showLoadError("student_info")})},installHandlers:function(){var sel=$("#openassessment__staff-info",this.element);var view=this;if(sel.length<=0){return}this.baseView.setUpCollapseExpand(sel,function(){});sel.find("#openassessment_student_info_form").submit(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});sel.find("#submit_student_id").click(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});sel.find("#schedule_training").click(function(eventObject){eventObject.preventDefault();view.scheduleTraining()});sel.find("#reschedule_unfinished_tasks").click(function(eventObject){eventObject.preventDefault(); if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}OpenAssessment.BaseView=function(runtime,element,server){this.runtime=runtime;this.element=element;this.server=server;this.responseView=new OpenAssessment.ResponseView(this.element,this.server,this);this.trainingView=new OpenAssessment.StudentTrainingView(this.element,this.server,this);this.selfView=new OpenAssessment.SelfView(this.element,this.server,this);this.peerView=new OpenAssessment.PeerView(this.element,this.server,this);this.gradeView=new OpenAssessment.GradeView(this.element,this.server,this);this.messageView=new OpenAssessment.MessageView(this.element,this.server,this);this.staffInfoView=new OpenAssessment.StaffInfoView(this.element,this.server,this)};OpenAssessment.BaseView.prototype={scrollToTop:function(){if($.scrollTo instanceof Function){$(window).scrollTo($("#openassessment__steps"),800,{offset:-50})}},setUpCollapseExpand:function(parentSel,onExpand){parentSel.find(".ui-toggle-visibility__control").click(function(eventData){var sel=$(eventData.target).closest(".ui-toggle-visibility");if(sel.hasClass("is--collapsed")&&onExpand!==undefined){onExpand()}sel.toggleClass("is--collapsed")})},load:function(){this.responseView.load();this.loadAssessmentModules();this.staffInfoView.load()},loadAssessmentModules:function(){this.trainingView.load();this.peerView.load();this.selfView.load();this.gradeView.load()},loadMessageView:function(){this.messageView.load()},toggleActionError:function(type,msg){var element=this.element;var container=null;if(type=="save"){container=".response__submission__actions"}else if(type=="submit"||type=="peer"||type=="self"||type=="student-training"){container=".step__actions"}else if(type=="feedback_assess"){container=".submission__feedback__actions"}if(container===null){if(msg!==null){console.log(msg)}}else{var msgHtml=msg===null?"":msg;$(container+" .message__content",element).html("<p>"+msgHtml+"</p>");$(container,element).toggleClass("has--error",msg!==null)}},showLoadError:function(step){var container="#openassessment__"+step;$(container).toggleClass("has--error",true);$(container+" .step__status__value i").removeClass().addClass("ico icon-warning-sign");$(container+" .step__status__value .copy").html(gettext("Unable to Load"))}};function OpenAssessmentBlock(runtime,element){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.BaseView(runtime,element,server);view.load()}OpenAssessment.StudioView=function(runtime,element,server){this.runtime=runtime;this.server=server;this.liveElement=$(element);var liveElement=this.liveElement;this.settingsFieldSelectors={promptBox:$("#openassessment_prompt_editor",liveElement),titleField:$("#openassessment_title_editor",liveElement),submissionStartField:$("#openassessment_submission_start_editor",liveElement),submissionDueField:$("#openassessment_submission_due_editor",liveElement),hasPeer:$("#include_peer_assessment",liveElement),hasSelf:$("#include_self_assessment",liveElement),hasAI:$("#include_ai_assessment",liveElement),hasTraining:$("#include_student_training",liveElement),peerMustGrade:$("#peer_assessment_must_grade",liveElement),peerGradedBy:$("#peer_assessment_graded_by",liveElement),peerStart:$("#peer_assessment_start_date",liveElement),peerDue:$("#peer_assessment_due_date",liveElement),selfStart:$("#self_assessment_start_date",liveElement),selfDue:$("#self_assessment_due_date",liveElement)};this.aiTrainingExamplesCodeBox=CodeMirror.fromTextArea($("#ai_training_examples",liveElement).first().get(0),{mode:"xml",lineNumbers:true,lineWrapping:true});this.studentTrainingExamplesCodeBox=CodeMirror.fromTextArea($("#student_training_examples",liveElement).first().get(0),{mode:"xml",lineNumbers:true,lineWrapping:true});var criterionHtml=$("#openassessment_criterion_1",liveElement).parent().html();this.criterionHtmlTemplate=criterionHtml.replace(new RegExp("1","g"),"C-C-C");var optionHtml=$("#openassessment_criterion_1_option_1",liveElement).parent().html();var criteriaReplaced=optionHtml.replace(new RegExp("criterion_1","g"),"criterion_C-C-C");this.optionHtmlTemplate=criteriaReplaced.replace(new RegExp("option_1","g"),"option_O-O-O");this.numberOfCriteria=0;this.numberOfOptions=[];this.rubricCriteriaSelectors=[];this.rubricFeedbackPrompt=$("#openassessment_rubric_feedback",liveElement);this.hasRubricFeedbackPrompt=true;$("#openassessment_criterion_list",liveElement).empty();this.addNewCriterionToRubric();var view=this;$(".openassessment_save_button",liveElement).click(function(eventData){view.save()});$(".openassessment_cancel_button",liveElement).click(function(eventData){view.cancel()});$(".openassessment_editor_content_and_tabs",liveElement).tabs();view.addSettingsAssessmentCheckboxListener("ai_assessment",liveElement);view.addSettingsAssessmentCheckboxListener("self_assessment",liveElement);view.addSettingsAssessmentCheckboxListener("peer_assessment",liveElement);view.addSettingsAssessmentCheckboxListener("student_training",liveElement);$("#openassessment_rubric_add_criterion",liveElement).click(function(eventData){view.addNewCriterionToRubric(liveElement)});$("#openassessment_rubric_feedback_remove",liveElement).click(function(eventData){$("#openassessment_rubric_feedback_header_open",liveElement).fadeOut();$("#openassessment_rubric_feedback_input_wrapper",liveElement).fadeOut();$("#openassessment_rubric_feedback_header_closed",liveElement).fadeIn();view.hasRubricFeedbackPrompt=false});$("#openassessment_rubric_feedback_header_closed",liveElement).click(function(eventData){$("#openassessment_rubric_feedback_header_closed",liveElement).fadeOut();$("#openassessment_rubric_feedback_header_open",liveElement).fadeIn();$("#openassessment_rubric_feedback_input_wrapper",liveElement).fadeIn();view.hasRubricFeedbackPrompt=true});$("#openassessment_rubric_feedback_header_closed",liveElement).hide()};OpenAssessment.StudioView.prototype={load:function(){var view=this;this.server.loadEditorContext().done(function(prompt,rubric,title,subStart,subDue,assessments){view.settingsFieldSelectors.submissionStartField.prop("value",subStart);view.settingsFieldSelectors.submissionDueField.prop("value",subDue);view.settingsFieldSelectors.promptBox.prop("value",prompt);view.settingsFieldSelectors.titleField.prop("value",title);view.settingsFieldSelectors.hasTraining.prop("checked",false).change();view.settingsFieldSelectors.hasPeer.prop("checked",false).change();view.settingsFieldSelectors.hasSelf.prop("checked",false).change();view.settingsFieldSelectors.hasAI.prop("checked",false).change();for(var i=0;i<assessments.length;i++){var assessment=assessments[i];if(assessment.name=="peer-assessment"){view.settingsFieldSelectors.peerMustGrade.prop("value",assessment.must_grade);view.settingsFieldSelectors.peerGradedBy.prop("value",assessment.must_be_graded_by);view.settingsFieldSelectors.peerStart.prop("value",assessment.start);view.settingsFieldSelectors.peerDue.prop("value",assessment.due);view.settingsFieldSelectors.hasPeer.prop("checked",true).change()}else if(assessment.name=="self-assessment"){view.settingsFieldSelectors.selfStart.prop("value",assessment.start);view.settingsFieldSelectors.selfDue.prop("value",assessment.due);view.settingsFieldSelectors.hasSelf.prop("checked",true).change()}else if(assessment.name=="example-based-assessment"){view.settingsFieldSelectors.aiTrainingExamplesCodeBox.setValue(assessment.examples);view.settingsFieldSelectors.hasAI.prop("checked",true).change()}else if(assessment.name=="student-training"){view.studentTrainingExamplesCodeBox.setValue(assessment.examples);view.settingsFieldSelectors.hasTraining.prop("checked",true).change()}}while(view.numberOfCriteria<rubric.criteria.length){view.addNewCriterionToRubric()}while(view.numberOfCriteria>rubric.criteria.length){view.removeCriterionFromRubric(1)}for(i=0;i<rubric.criteria.length;i++){while(view.numberOfOptions[i+1]<rubric.criteria[i].options.length){view.addNewOptionToCriterion(view.liveElement,i+1)}while(view.numberOfOptions[i+1]>rubric.criteria[i].options.length){view.removeOptionFromCriterion(view.liveElement,i+1,1)}}for(i=0;i<rubric.criteria.length;i++){var criterion=rubric.criteria[i];var selectors=view.rubricCriteriaSelectors[i+1];selectors.name.prop("value",criterion.name);selectors.prompt.prop("value",criterion.prompt);selectors.feedback=criterion.feedback;for(var j=0;j<criterion.options.length;j++){var option=criterion.options[j];var optionSelectors=selectors.options[j+1];optionSelectors.name.prop("value",option.name);optionSelectors.points.prop("value",option.points);optionSelectors.explanation.prop("value",option.explanation)}}if(rubric.feedbackprompt){view.rubricFeedbackPrompt.prop("value",rubric.feedbackprompt);view.hasRubricFeedbackPrompt=true}else{view.rubricFeedbackPrompt.prop("value","");view.hasRubricFeedbackPrompt=false}}).fail(function(msg){view.showError(msg)})},save:function(){var view=this;this.server.checkReleased().done(function(isReleased){if(isReleased){view.confirmPostReleaseUpdate($.proxy(view.updateEditorContext,view))}else{view.updateEditorContext()}}).fail(function(errMsg){view.showError(errMsg)})},addSettingsAssessmentCheckboxListener:function(name,liveElement){$("#include_"+name,liveElement).change(function(){if(this.checked){$("#"+name+"_description_closed",liveElement).fadeOut("fast");$("#"+name+"_settings_editor",liveElement).fadeIn()}else{$("#"+name+"_settings_editor",liveElement).fadeOut("fast");$("#"+name+"_description_closed",liveElement).fadeIn()}})},confirmPostReleaseUpdate:function(onConfirm){var msg=gettext("This problem has already been released. Any changes will apply only to future assessments.");if(confirm(msg)){onConfirm()}},addNewCriterionToRubric:function(){var view=this;var liveElement=this.liveElement;var newCriterionID=this.numberOfCriteria+1;this.numberOfCriteria+=1;this.numberOfOptions[newCriterionID]=0;var criterionHtml=this.criterionHtmlTemplate.replace(new RegExp("C-C-C","g"),""+newCriterionID);$("#openassessment_criterion_list",liveElement).append(criterionHtml);liveElement=$("#openassessment_criterion_"+newCriterionID);$(".openassessment_criterion_option_list",liveElement).empty();view.rubricCriteriaSelectors[newCriterionID]={criterion:liveElement,name:$(".openassessment_criterion_name",liveElement).first(),prompt:$(".openassessment_criterion_prompt",liveElement).first(),options:[],feedback:"disabled"};$("input:radio[value=disabled]",liveElement).prop("checked",true);view.addNewOptionToCriterion(liveElement,newCriterionID);$("#openassessment_display_criterion_"+newCriterionID,liveElement).change(function(){if(this.checked){$("#openassessment_criterion_body_"+newCriterionID,liveElement).fadeIn()}else{$("#openassessment_criterion_body_"+newCriterionID,liveElement).fadeOut()}});$("#openassessment_criterion_"+newCriterionID+"_remove",liveElement).click(function(eventData){view.removeCriterionFromRubric(newCriterionID)});$("#openassessment_criterion_"+newCriterionID+"_add_option",liveElement).click(function(eventData){view.addNewOptionToCriterion(liveElement,newCriterionID)});$(".openassessment_feedback_remove_button",liveElement).click(function(eventData){$(".openassessment_criterion_feedback_direction",liveElement).fadeOut();$(".openassessment_criterion_feedback_header_open",liveElement).fadeOut();$(".openassessment_criterion_feedback_header_closed",liveElement).fadeIn();$(".openassessment_feedback_remove_button",liveElement).fadeOut();view.rubricCriteriaSelectors[newCriterionID].hasFeedback=false});$(".openassessment_criterion_feedback_header_closed",liveElement).click(function(eventData){$(".openassessment_criterion_feedback_direction",liveElement).fadeIn();$(".openassessment_criterion_feedback_header_open",liveElement).fadeIn();$(".openassessment_criterion_feedback_header_closed",liveElement).fadeOut();$(".openassessment_feedback_remove_button",liveElement).fadeIn();view.rubricCriteriaSelectors[newCriterionID].hasFeedback=true});$(".openassessment_criterion_feedback_header_closed",liveElement).hide()},removeCriterionFromRubric:function(criterionToRemove){var view=this;var numCriteria=view.numberOfCriteria;var selectors=view.rubricCriteriaSelectors;for(var i=criterionToRemove;i<numCriteria;i++){selectors[i].name.prop("value",selectors[i+1].name.prop("value"));selectors[i].prompt.prop("value",selectors[i+1].prompt.prop("value"));selectors[i].feedback=selectors[i+1].feedback;$('input:radio[value="disabled"]',selectors[i].criterion).prop("checked",true);while(view.numberOfOptions[i]<view.numberOfOptions[i+1]){view.addNewOptionToCriterion(selectors[i].criteria,i)}while(view.numberOfOptions[i]>view.numberOfOptions[i+1]){view.removeOptionFromCriterion(selectors[i].criteria,i,1)}var options1=selectors[i].options;var options2=selectors[i+1].options;var numOptions2=view.numberOfOptions[i+1];for(var j=1;j<numOptions2;j++){options1[j].points.prop("value",options2[j].points.prop("value"));options1[j].name.prop("value",options2[j].name.prop("value"));options1[j].explanation.prop("value",options2[j].explanation.prop("value"))}}view.rubricCriteriaSelectors[view.rubricCriteriaSelectors.length-1].criterion.remove();view.rubricCriteriaSelectors=view.rubricCriteriaSelectors.slice(0,numCriteria);view.numberOfOptions=view.numberOfOptions.slice(0,numCriteria);view.numberOfCriteria-=1},addNewOptionToCriterion:function(liveElement,criterionID){var view=this;var newOptionID=this.numberOfOptions[criterionID]+1;this.numberOfOptions[criterionID]+=1;var optionHtml=this.optionHtmlTemplate;optionHtml=optionHtml.replace(new RegExp("C-C-C","g"),""+criterionID);optionHtml=optionHtml.replace(new RegExp("O-O-O","g"),""+newOptionID);$("#openassessment_criterion_"+criterionID+"_options",liveElement).append(optionHtml);liveElement=$("#openassessment_criterion_"+criterionID+"_option_"+newOptionID);view.rubricCriteriaSelectors[criterionID].options[newOptionID]={option:liveElement,points:$("#openassessment_criterion_"+criterionID+"_option_"+newOptionID+"_points",liveElement),name:$("#openassessment_criterion_"+criterionID+"_option_"+newOptionID+"_name",liveElement),explanation:$("#openassessment_criterion_"+criterionID+"_option_"+newOptionID+"_explanation",liveElement)};$("#openassessment_criterion_"+criterionID+"_option_"+newOptionID+"_remove",liveElement).click(function(eventData){view.removeOptionFromCriterion(liveElement,criterionID,newOptionID)})},removeOptionFromCriterion:function(liveElement,criterionID,optionToRemove){var view=this;var numberOfOptions=view.numberOfOptions[criterionID];var optionSelectors=view.rubricCriteriaSelectors[criterionID].options;for(var i=optionToRemove;i<numberOfOptions;i++){optionSelectors[i].points.prop("value",optionSelectors[i+1].points.prop("value"));optionSelectors[i].name.prop("value",optionSelectors[i+1].name.prop("value"));optionSelectors[i].explanation.prop("value",optionSelectors[i+1].explanation.prop("value"))}optionSelectors[optionSelectors.length-1].option.remove();view.rubricCriteriaSelectors[criterionID].options=view.rubricCriteriaSelectors[criterionID].options.slice(0,optionSelectors.length-1);view.numberOfOptions[criterionID]-=1},updateEditorContext:function(){this.runtime.notify("save",{state:"start"});var prompt=this.settingsFieldSelectors.promptBox.prop("value");var title=this.settingsFieldSelectors.titleField.prop("value");var subStart=this.settingsFieldSelectors.submissionStartField.prop("value");var subDue=this.settingsFieldSelectors.submissionDueField.prop("value");var rubricCriteria=[];for(var i=1;i<=this.numberOfCriteria;i++){var selectorDict=this.rubricCriteriaSelectors[i];var criterionValueDict={order_num:i-1,name:selectorDict.name.prop("value"),prompt:selectorDict.prompt.prop("value"),feedback:$("#openassessment_criterion_"+i+"_feedback").val()};var optionSelectorList=selectorDict.options;var optionValueList=[];for(var j=1;j<=this.numberOfOptions[i];j++){var optionSelectors=optionSelectorList[j];optionValueList=optionValueList.concat([{order_num:j-1,points:optionSelectors.points.prop("value"),name:optionSelectors.name.prop("value"),explanation:optionSelectors.explanation.prop("value")}])}criterionValueDict.options=optionValueList;rubricCriteria=rubricCriteria.concat([criterionValueDict])}var rubric={criteria:rubricCriteria};if(this.hasRubricFeedbackPrompt){rubric.feedbackprompt=this.rubricFeedbackPrompt.prop("value")}var assessments=[];if(this.settingsFieldSelectors.hasTraining.prop("checked")){assessments[assessments.length]={name:"student-training",examples:this.studentTrainingExamplesCodeBox.getValue()}}if(this.settingsFieldSelectors.hasPeer.prop("checked")){var assessment={name:"peer-assessment",must_grade:parseInt(this.settingsFieldSelectors.peerMustGrade.prop("value")),must_be_graded_by:parseInt(this.settingsFieldSelectors.peerGradedBy.prop("value"))};var startStr=this.settingsFieldSelectors.peerStart.prop("value");var dueStr=this.settingsFieldSelectors.peerDue.prop("value");if(startStr){assessment=$.extend(assessment,{start:startStr})}if(dueStr){assessment=$.extend(assessment,{due:dueStr})}assessments[assessments.length]=assessment}if(this.settingsFieldSelectors.hasSelf.prop("checked")){var assessment={name:"self-assessment"};var startStr=this.settingsFieldSelectors.selfStart.prop("value");var dueStr=this.settingsFieldSelectors.selfDue.prop("value");if(startStr){assessment=$.extend(assessment,{start:startStr})}if(dueStr){assessment=$.extend(assessment,{due:dueStr})}assessments[assessments.length]=assessment}if(this.settingsFieldSelectors.hasAI.prop("checked")){assessments[assessments.length]={name:"example-based-assessment",examples:this.aiTrainingExamplesCodeBox.getValue()}}var view=this;this.server.updateEditorContext(prompt,rubric,title,subStart,subDue,assessments).done(function(){view.runtime.notify("save",{state:"end"});view.load()}).fail(function(msg){view.showError(msg)})},cancel:function(){this.runtime.notify("cancel",{})},showError:function(errorMsg){this.runtime.notify("error",{msg:errorMsg})}};function OpenAssessmentEditor(runtime,element){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.StudioView(runtime,element,server);view.load()}OpenAssessment.GradeView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.GradeView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("grade").done(function(html){$("#openassessment__grade",view.element).replaceWith(html);view.installHandlers()}).fail(function(errMsg){baseView.showLoadError("grade",errMsg)})},installHandlers:function(){var sel=$("#openassessment__grade",this.element);this.baseView.setUpCollapseExpand(sel);var view=this;sel.find("#feedback__submit").click(function(eventObject){eventObject.preventDefault();view.submitFeedbackOnAssessment()})},feedbackText:function(text){if(typeof text==="undefined"){return $("#feedback__remarks__value",this.element).val()}else{$("#feedback__remarks__value",this.element).val(text)}},feedbackOptions:function(options){var view=this;if(typeof options==="undefined"){return $.map($(".feedback__overall__value:checked",view.element),function(element,index){return $(element).val()})}else{$(".feedback__overall__value",this.element).prop("checked",false);$.each(options,function(index,opt){$("#feedback__overall__value--"+opt,view.element).prop("checked",true)})}},setHidden:function(sel,hidden){sel.toggleClass("is--hidden",hidden);sel.attr("aria-hidden",hidden?"true":"false")},isHidden:function(sel){return sel.hasClass("is--hidden")&&sel.attr("aria-hidden")=="true"},feedbackState:function(newState){var containerSel=$(".submission__feedback__content",this.element);var instructionsSel=containerSel.find(".submission__feedback__instructions");var fieldsSel=containerSel.find(".submission__feedback__fields");var actionsSel=containerSel.find(".submission__feedback__actions");var transitionSel=containerSel.find(".transition__status");var messageSel=containerSel.find(".message--complete");if(typeof newState==="undefined"){var isSubmitting=containerSel.hasClass("is--transitioning")&&containerSel.hasClass("is--submitting")&&!this.isHidden(transitionSel)&&this.isHidden(messageSel)&&this.isHidden(instructionsSel)&&this.isHidden(fieldsSel)&&this.isHidden(actionsSel);var hasSubmitted=containerSel.hasClass("is--submitted")&&this.isHidden(transitionSel)&&!this.isHidden(messageSel)&&this.isHidden(instructionsSel)&&this.isHidden(fieldsSel)&&this.isHidden(actionsSel);var isOpen=!containerSel.hasClass("is--submitted")&&!containerSel.hasClass("is--transitioning")&&!containerSel.hasClass("is--submitting")&&this.isHidden(transitionSel)&&this.isHidden(messageSel)&&!this.isHidden(instructionsSel)&&!this.isHidden(fieldsSel)&&!this.isHidden(actionsSel);if(isOpen){return"open"}else if(isSubmitting){return"submitting"}else if(hasSubmitted){return"submitted"}else{throw"Invalid feedback state"}}else{if(newState=="open"){containerSel.toggleClass("is--transitioning",false);containerSel.toggleClass("is--submitting",false);containerSel.toggleClass("is--submitted",false);this.setHidden(instructionsSel,false);this.setHidden(fieldsSel,false);this.setHidden(actionsSel,false);this.setHidden(transitionSel,true);this.setHidden(messageSel,true)}else if(newState=="submitting"){containerSel.toggleClass("is--transitioning",true);containerSel.toggleClass("is--submitting",true);containerSel.toggleClass("is--submitted",false);this.setHidden(instructionsSel,true);this.setHidden(fieldsSel,true);this.setHidden(actionsSel,true);this.setHidden(transitionSel,false);this.setHidden(messageSel,true)}else if(newState=="submitted"){containerSel.toggleClass("is--transitioning",false);containerSel.toggleClass("is--submitting",false);containerSel.toggleClass("is--submitted",true);this.setHidden(instructionsSel,true);this.setHidden(fieldsSel,true);this.setHidden(actionsSel,true);this.setHidden(transitionSel,true);this.setHidden(messageSel,false)}}},submitFeedbackOnAssessment:function(){var view=this;var baseView=this.baseView;$("#feedback__submit",this.element).toggleClass("is--disabled",true);view.feedbackState("submitting");this.server.submitFeedbackOnAssessment(this.feedbackText(),this.feedbackOptions()).done(function(){view.feedbackState("submitted")}).fail(function(errMsg){baseView.toggleActionError("feedback_assess",errMsg)})}};OpenAssessment.MessageView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.MessageView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("message").done(function(html){$("#openassessment__message",view.element).replaceWith(html)}).fail(function(errMsg){baseView.showLoadError("message",errMsg)})}};OpenAssessment.PeerView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.PeerView.prototype={load:function(){var view=this;this.server.render("peer_assessment").done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.installHandlers(false)}).fail(function(errMsg){view.baseView.showLoadError("peer-assessment")});view.baseView.loadMessageView()},loadContinuedAssessment:function(){var view=this;this.server.renderContinuedPeer().done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.installHandlers(true)}).fail(function(errMsg){view.baseView.showLoadError("peer-assessment")})},installHandlers:function(isContinuedAssessment){var sel=$("#openassessment__peer-assessment",this.element);var view=this;this.baseView.setUpCollapseExpand(sel,$.proxy(view.loadContinuedAssessment,view));var rubricSelector=$("#peer-assessment--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(view.peerSubmitEnabled,view))}sel.find("#peer-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();if(!isContinuedAssessment){view.peerAssess()}else{view.continuedPeerAssess()}})},peerSubmitEnabled:function(enabled){var button=$("#peer-assessment--001__assessment__submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled)}},peerAssess:function(){var view=this;var baseView=view.baseView;this.peerAssessRequest(function(){view.load();baseView.loadAssessmentModules();baseView.scrollToTop()})},continuedPeerAssess:function(){var view=this;var gradeView=this.baseView.gradeView;var baseView=view.baseView;view.peerAssessRequest(function(){view.loadContinuedAssessment();gradeView.load();baseView.scrollToTop()})},peerAssessRequest:function(successFunction){var view=this;view.baseView.toggleActionError("peer",null);view.peerSubmitEnabled(false);this.server.peerAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.overallFeedback()).done(successFunction).fail(function(errMsg){view.baseView.toggleActionError("peer",errMsg);view.peerSubmitEnabled(true)})},overallFeedback:function(overallFeedback){var selector="#assessment__rubric__question--feedback__value";if(typeof overallFeedback==="undefined"){return $(selector,this.element).val()}else{$(selector,this.element).val(overallFeedback)}}};OpenAssessment.ResponseView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.savedResponse="";this.lastChangeTime=Date.now();this.errorOnLastSave=false;this.autoSaveTimerId=null};OpenAssessment.ResponseView.prototype={AUTO_SAVE_POLL_INTERVAL:2e3,AUTO_SAVE_WAIT:3e4,load:function(){var view=this;this.server.render("submission").done(function(html){$("#openassessment__response",view.element).replaceWith(html);view.installHandlers();view.setAutoSaveEnabled(true)}).fail(function(errMsg){view.baseView.showLoadError("response")})},installHandlers:function(){var sel=$("#openassessment__response",this.element);var view=this;this.baseView.setUpCollapseExpand(sel);this.savedResponse=this.response();var handleChange=function(eventData){view.handleResponseChanged()};sel.find("#submission__answer__value").on("change keyup drop paste",handleChange);sel.find("#step--response__submit").click(function(eventObject){eventObject.preventDefault();view.submit()});sel.find("#submission__save").click(function(eventObject){eventObject.preventDefault();view.save()})},setAutoSaveEnabled:function(enabled){if(enabled){if(this.autoSaveTimerId===null){this.autoSaveTimerId=setInterval($.proxy(this.autoSave,this),this.AUTO_SAVE_POLL_INTERVAL)}}else{if(this.autoSaveTimerId!==null){clearInterval(this.autoSaveTimerId)}}},submitEnabled:function(enabled){var sel=$("#step--response__submit",this.element);if(typeof enabled==="undefined"){return!sel.hasClass("is--disabled")}else{sel.toggleClass("is--disabled",!enabled)}},saveEnabled:function(enabled){var sel=$("#submission__save",this.element);if(typeof enabled==="undefined"){return!sel.hasClass("is--disabled")}else{sel.toggleClass("is--disabled",!enabled)}},saveStatus:function(msg){var sel=$("#response__save_status h3",this.element);if(typeof msg==="undefined"){return sel.text()}else{var label=gettext("Status of Your Response");sel.html('<span class="sr">'+label+":"+"</span>\n"+msg)}},unsavedWarningEnabled:function(enabled){if(typeof enabled==="undefined"){return window.onbeforeunload!==null}else{if(enabled){window.onbeforeunload=function(){return gettext("If you leave this page without saving or submitting your response, you'll lose any work you've done on the response.")}}else{window.onbeforeunload=null}}},response:function(text){var sel=$("#submission__answer__value",this.element);if(typeof text==="undefined"){return sel.val()}else{sel.val(text)}},responseChanged:function(){var currentResponse=$.trim(this.response());var savedResponse=$.trim(this.savedResponse);return savedResponse!==currentResponse},autoSave:function(){var timeSinceLastChange=Date.now()-this.lastChangeTime;if(this.responseChanged()&&timeSinceLastChange>this.AUTO_SAVE_WAIT&&!this.errorOnLastSave){this.save()}},handleResponseChanged:function(){var isBlank=$.trim(this.response())!=="";this.submitEnabled(isBlank);if(this.responseChanged()){this.saveEnabled(isBlank);this.saveStatus(gettext("This response has not been saved."));this.unsavedWarningEnabled(true)}this.lastChangeTime=Date.now()},save:function(){this.errorOnLastSave=false;this.saveStatus(gettext("Saving..."));this.baseView.toggleActionError("save",null);this.unsavedWarningEnabled(false);var view=this;var savedResponse=this.response();this.server.save(savedResponse).done(function(){view.savedResponse=savedResponse;var currentResponse=view.response();view.submitEnabled(currentResponse!=="");if(currentResponse==savedResponse){view.saveEnabled(false);view.saveStatus(gettext("This response has been saved but not submitted."))}}).fail(function(errMsg){view.saveStatus(gettext("Error"));view.baseView.toggleActionError("save",errMsg);view.errorOnLastSave=true})},submit:function(){this.submitEnabled(false);var view=this;var baseView=this.baseView;this.confirmSubmission().pipe(function(){var submission=$("#submission__answer__value",view.element).val();baseView.toggleActionError("response",null);return view.server.submit(submission)}).done($.proxy(view.moveToNextStep,view)).fail(function(errCode,errMsg){if(errCode=="ENOMULTI"){view.moveToNextStep()}else{if(errMsg){baseView.toggleActionError("submit",errMsg)}view.submitEnabled(true)}})},moveToNextStep:function(){this.load();this.baseView.loadAssessmentModules();this.unsavedWarningEnabled(false)},confirmSubmission:function(){var msg="You're about to submit your response for this assignment. "+"After you submit this response, you can't change it or submit a new response.";return $.Deferred(function(defer){if(confirm(msg)){defer.resolve()}else{defer.reject()}})}};OpenAssessment.Rubric=function(element){this.element=element};OpenAssessment.Rubric.prototype={criterionFeedback:function(criterionFeedback){var selector="textarea.answer__value";var feedback={};$(selector,this.element).each(function(index,sel){if(typeof criterionFeedback!=="undefined"){$(sel).val(criterionFeedback[sel.name]);feedback[sel.name]=criterionFeedback[sel.name]}else{feedback[sel.name]=$(sel).val()}});return feedback},optionsSelected:function(optionsSelected){var selector="input[type=radio]";if(typeof optionsSelected==="undefined"){var options={};$(selector+":checked",this.element).each(function(index,sel){options[sel.name]=sel.value});return options}else{$(selector,this.element).prop("checked",false);$(selector,this.element).each(function(index,sel){if(optionsSelected.hasOwnProperty(sel.name)){if(sel.value==optionsSelected[sel.name]){$(sel).prop("checked",true)}}})}},canSubmitCallback:function(callback){$(this.element).change(function(){var numChecked=$("input[type=radio]:checked",this).length;var numAvailable=$(".field--radio.assessment__rubric__question",this).length;var canSubmit=numChecked==numAvailable;callback(canSubmit)})},showCorrections:function(corrections){var selector="input[type=radio]";var hasErrors=false;$(selector,this.element).each(function(index,sel){var listItem=$(sel).parents(".assessment__rubric__question");if(corrections.hasOwnProperty(sel.name)){hasErrors=true;listItem.find(".message--incorrect").removeClass("is--hidden");listItem.find(".message--correct").addClass("is--hidden")}else{listItem.find(".message--correct").removeClass("is--hidden");listItem.find(".message--incorrect").addClass("is--hidden")}});return hasErrors}};OpenAssessment.SelfView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.SelfView.prototype={load:function(){var view=this;this.server.render("self_assessment").done(function(html){$("#openassessment__self-assessment",view.element).replaceWith(html);
view.rescheduleUnfinishedTasks()})},scheduleTraining:function(){var view=this;this.server.scheduleTraining().done(function(msg){$("#schedule_training_message",this.element).text(msg)}).fail(function(errMsg){$("#schedule_training_message",this.element).text(errMsg)})},rescheduleUnfinishedTasks:function(){var view=this;this.server.rescheduleUnfinishedTasks().done(function(msg){$("#reschedule_unfinished_tasks_message",this.element).text(msg)}).fail(function(errMsg){$("#reschedule_unfinished_tasks_message",this.element).text(errMsg)})}};OpenAssessment.StudentTrainingView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.StudentTrainingView.prototype={load:function(){var view=this;this.server.render("student_training").done(function(html){$("#openassessment__student-training",view.element).replaceWith(html);view.installHandlers()}).fail(function(errMsg){view.baseView.showLoadError("student-training")})},installHandlers:function(){var sel=$("#openassessment__student-training",this.element);var view=this;this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#student-training--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(this.assessButtonEnabled,this))}sel.find("#student-training--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.assess()})},assess:function(){this.assessButtonEnabled(false);var options={};if(this.rubric!==null){options=this.rubric.optionsSelected()}var view=this;var baseView=this.baseView;this.server.trainingAssess(options).done(function(corrections){var incorrect=$("#openassessment__student-training--incorrect",this.element);var instructions=$("#openassessment__student-training--instructions",this.element);if(!view.rubric.showCorrections(corrections)){view.load();baseView.loadAssessmentModules();incorrect.addClass("is--hidden");instructions.removeClass("is--hidden")}else{instructions.addClass("is--hidden");incorrect.removeClass("is--hidden")}baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("student-training",errMsg);view.assessButtonEnabled(true)})},assessButtonEnabled:function(isEnabled){var button=$("#student-training--001__assessment__submit",this.element);if(typeof isEnabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!isEnabled)}}}; view.installHandlers()}).fail(function(errMsg){view.showLoadError("self-assessment")})},installHandlers:function(){var view=this;var sel=$("#openassessment__self-assessment",view.element);this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#self-assessment--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(this.selfSubmitEnabled,this))}sel.find("#self-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.selfAssess()})},selfSubmitEnabled:function(enabled){var button=$("#self-assessment--001__assessment__submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled)}},selfAssess:function(){var view=this;var baseView=this.baseView;baseView.toggleActionError("self",null);view.selfSubmitEnabled(false);var options=this.rubric.optionsSelected();this.server.selfAssess(options).done(function(){baseView.loadAssessmentModules();baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("self",errMsg);view.selfSubmitEnabled(true)})}};OpenAssessment.Server=function(runtime,element){this.runtime=runtime;this.element=element};OpenAssessment.Server.prototype={url:function(handler){return this.runtime.handlerUrl(this.element,handler)},render:function(component){var url=this.url("render_"+component);return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(this,[data])}).fail(function(data){defer.rejectWith(this,[gettext("This section could not be loaded.")])})}).promise()},renderContinuedPeer:function(){var url=this.url("render_peer_assessment");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:{continue_grading:true}}).done(function(data){defer.resolveWith(this,[data])}).fail(function(data){defer.rejectWith(this,[gettext("This section could not be loaded.")])})}).promise()},studentInfo:function(student_id){var url=this.url("render_student_info");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:{student_id:student_id}}).done(function(data){defer.resolveWith(this,[data])}).fail(function(data){defer.rejectWith(this,[gettext("This section could not be loaded.")])})}).promise()},submit:function(submission){var url=this.url("submit");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission})}).done(function(data){var success=data[0];if(success){var studentId=data[1];var attemptNum=data[2];defer.resolveWith(this,[studentId,attemptNum])}else{var errorNum=data[1];var errorMsg=data[2];defer.rejectWith(this,[errorNum,errorMsg])}}).fail(function(data){defer.rejectWith(this,["AJAX",gettext("This response could not be submitted.")])})}).promise()},save:function(submission){var url=this.url("save_submission");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission})}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This response could not be saved.")])})}).promise()},submitFeedbackOnAssessment:function(text,options){var url=this.url("submit_feedback");var payload=JSON.stringify({feedback_text:text,feedback_options:options});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This feedback could not be submitted.")])})}).promise()},peerAssess:function(optionsSelected,criterionFeedback,overallFeedback){var url=this.url("peer_assess");var payload=JSON.stringify({options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})}).promise()},selfAssess:function(optionsSelected){var url=this.url("self_assess");var payload=JSON.stringify({options_selected:optionsSelected});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},trainingAssess:function(optionsSelected){var url=this.url("training_assess");var payload=JSON.stringify({options_selected:optionsSelected});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolveWith(this,[data.corrections])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},scheduleTraining:function(){var url=this.url("schedule_training");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""'}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},rescheduleUnfinishedTasks:function(){var url=this.url("reschedule_unfinished_tasks");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""'}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("One or more rescheduling tasks failed.")])})})},loadEditorContext:function(){var url=this.url("editor_context");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""'}).done(function(data){if(data.success){defer.resolveWith(this,[data.prompt,data.rubric,data.title,data.submission_start,data.submission_due,data.assessments])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This problem could not be loaded.")])})}).promise()},updateEditorContext:function(prompt,rubric,title,sub_start,sub_due,assessments){var url=this.url("update_editor_context");var payload=JSON.stringify({prompt:prompt,rubric:rubric,title:title,submission_start:sub_start,submission_due:sub_due,assessments:assessments});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This problem could not be saved.")])})}).promise()},checkReleased:function(){var url=this.url("check_released");var payload='""';return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolveWith(this,[data.is_released])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("The server could not be contacted.")])})}).promise()}};if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}OpenAssessment.StaffInfoView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.StaffInfoView.prototype={load:function(){var view=this;if($("#openassessment__staff-info",view.element).length>0){this.server.render("staff_info").done(function(html){$("#openassessment__staff-info",view.element).replaceWith(html);view.installHandlers()}).fail(function(errMsg){view.baseView.showLoadError("staff_info")})}},loadStudentInfo:function(){var view=this;var sel=$("#openassessment__staff-info",this.element);var student_id=sel.find("#openassessment__student_id").val();this.server.studentInfo(student_id).done(function(html){$("#openassessment__student-info",view.element).replaceWith(html)}).fail(function(errMsg){view.showLoadError("student_info")})},installHandlers:function(){var sel=$("#openassessment__staff-info",this.element);var view=this;if(sel.length<=0){return}this.baseView.setUpCollapseExpand(sel,function(){});sel.find("#openassessment_student_info_form").submit(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});sel.find("#submit_student_id").click(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});sel.find("#schedule_training").click(function(eventObject){eventObject.preventDefault();view.scheduleTraining()});sel.find("#reschedule_unfinished_tasks").click(function(eventObject){eventObject.preventDefault();view.rescheduleUnfinishedTasks()})},scheduleTraining:function(){var view=this;this.server.scheduleTraining().done(function(msg){$("#schedule_training_message",this.element).text(msg)}).fail(function(errMsg){$("#schedule_training_message",this.element).text(errMsg)})},rescheduleUnfinishedTasks:function(){var view=this;this.server.rescheduleUnfinishedTasks().done(function(msg){$("#reschedule_unfinished_tasks_message",this.element).text(msg)}).fail(function(errMsg){$("#reschedule_unfinished_tasks_message",this.element).text(errMsg)})}};OpenAssessment.StudentTrainingView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.StudentTrainingView.prototype={load:function(){var view=this;this.server.render("student_training").done(function(html){$("#openassessment__student-training",view.element).replaceWith(html);view.installHandlers()}).fail(function(errMsg){view.baseView.showLoadError("student-training")})},installHandlers:function(){var sel=$("#openassessment__student-training",this.element);var view=this;this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#student-training--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(this.assessButtonEnabled,this))}sel.find("#student-training--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.assess()})},assess:function(){this.assessButtonEnabled(false);var options={};if(this.rubric!==null){options=this.rubric.optionsSelected()}var view=this;var baseView=this.baseView;this.server.trainingAssess(options).done(function(corrections){var incorrect=$("#openassessment__student-training--incorrect",this.element);var instructions=$("#openassessment__student-training--instructions",this.element);if(!view.rubric.showCorrections(corrections)){view.load();baseView.loadAssessmentModules();incorrect.addClass("is--hidden");instructions.removeClass("is--hidden")}else{instructions.addClass("is--hidden");incorrect.removeClass("is--hidden")}baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("student-training",errMsg);view.assessButtonEnabled(true)})},assessButtonEnabled:function(isEnabled){var button=$("#student-training--001__assessment__submit",this.element);if(typeof isEnabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!isEnabled)}}};
\ No newline at end of file \ No newline at end of file
...@@ -13,7 +13,6 @@ describe("OpenAssessment.StudioView", function() { ...@@ -13,7 +13,6 @@ describe("OpenAssessment.StudioView", function() {
this.loadError = false; this.loadError = false;
this.updateError = false; this.updateError = false;
this.promptBox = ""; this.promptBox = "";
this.rubricXmlBox = "";
this.titleField = ""; this.titleField = "";
this.submissionStartField = ""; this.submissionStartField = "";
this.submissionDueField = ""; this.submissionDueField = "";
...@@ -36,13 +35,33 @@ describe("OpenAssessment.StudioView", function() { ...@@ -36,13 +35,33 @@ describe("OpenAssessment.StudioView", function() {
this.isReleased = false; this.isReleased = false;
this.rubric = {
prompt: 'This is the feedback prompt',
criteria: [
{
order_num: 0,
name: 'This is the criterion name',
prompt: 'this is the criterion prompt',
feedback: 'disabled',
options: [
{
order_num: 0,
name: 'Did real bad',
points: 0,
explanation: 'Showed as little effort as I did making this test case interesting.'
}
]
}
]
};
this.errorPromise = $.Deferred(function(defer) { this.errorPromise = $.Deferred(function(defer) {
defer.rejectWith(this, ['Test error']); defer.rejectWith(this, ['Test error']);
}).promise(); }).promise();
this.loadEditorContext = function() { this.loadEditorContext = function() {
var prompt = this.promptBox; var prompt = this.promptBox;
var rubric = this.rubricXmlBox; var rubric = this.rubric;
var title = this.titleField; var title = this.titleField;
var submission_start = this.submissionStartField; var submission_start = this.submissionStartField;
var submission_due = this.submissionDueField; var submission_due = this.submissionDueField;
...@@ -86,10 +105,10 @@ describe("OpenAssessment.StudioView", function() { ...@@ -86,10 +105,10 @@ describe("OpenAssessment.StudioView", function() {
} }
}; };
this.updateEditorContext = function(prompt, rubricXml, title, sub_start, sub_due, assessments) { this.updateEditorContext = function(prompt, rubric, title, sub_start, sub_due, assessments) {
if (!this.updateError) { if (!this.updateError) {
this.promptBox = prompt; this.promptBox = prompt;
this.rubricXmlBox = rubricXml; this.rubric = rubric;
this.titleField = title; this.titleField = title;
this.submissionStartField = sub_start; this.submissionStartField = sub_start;
this.submissionDueField = sub_due; this.submissionDueField = sub_due;
...@@ -140,15 +159,32 @@ describe("OpenAssessment.StudioView", function() { ...@@ -140,15 +159,32 @@ describe("OpenAssessment.StudioView", function() {
var view = null; var view = null;
var prompt = "How much do you like waffles?"; var prompt = "How much do you like waffles?";
var rubric = var rubric = {
"<rubric>" + criteria: [
"<criterion>"+ {
"<name>Proper Appreciation of Gravity</name>"+ order_num: 0,
"<prompt>How much respect did the person give waffles?</prompt>"+ name: "Proper appreciation of Gravity",
"<option points=\"0\"><name>No</name><explanation>Not enough</explanation></option>"+ prompt: "How much respect did the person give waffles?",
"<option points=\"2\"><name>Yes</name><explanation>An appropriate Amount</explanation></option>"+ feedback: "disabled",
"</criterion>"+ options: [
"</rubric>"; {
order_num: 0,
points: 0,
name: "No",
explanation: "Not enough"
},
{
order_num: 1,
points: 2,
name: "Yes",
explanation: "An appropriate Amount"
}
]
}
]
};
var title = "The most important of all questions."; var title = "The most important of all questions.";
var subStart = ""; var subStart = "";
var subDue = "2014-10-1T10:00:00"; var subDue = "2014-10-1T10:00:00";
...@@ -207,16 +243,14 @@ describe("OpenAssessment.StudioView", function() { ...@@ -207,16 +243,14 @@ describe("OpenAssessment.StudioView", function() {
view.load(); view.load();
// Expect that the XML definition(s) were loaded // Expect that the XML definition(s) were loaded
var rubric = view.rubricXmlBox.getValue(); var prompt = view.settingsFieldSelectors.promptBox.prop('value');
var prompt = view.promptBox.value;
expect(prompt).toEqual(''); expect(prompt).toEqual('');
expect(rubric).toEqual('');
}); });
it("saves the Editor Context definition", function() { it("saves the Editor Context definition", function() {
// Update the Context // Update the Context
view.titleField.value = 'THIS IS THE NEW TITLE'; view.settingsFieldSelectors.titleField.prop('value', 'THIS IS THE NEW TITLE');
// Save the updated editor definition // Save the updated editor definition
view.save(); view.save();
...@@ -249,25 +283,24 @@ describe("OpenAssessment.StudioView", function() { ...@@ -249,25 +283,24 @@ describe("OpenAssessment.StudioView", function() {
server.updateEditorContext(prompt, rubric, title, subStart, subDue, assessments); server.updateEditorContext(prompt, rubric, title, subStart, subDue, assessments);
view.load(); view.load();
expect(view.promptBox.value).toEqual(prompt); expect(view.settingsFieldSelectors.promptBox.prop('value')).toEqual(prompt);
expect(view.rubricXmlBox.getValue()).toEqual(rubric); expect(view.settingsFieldSelectors.titleField.prop('value')).toEqual(title);
expect(view.titleField.value).toEqual(title); expect(view.settingsFieldSelectors.submissionStartField.prop('value')).toEqual(subStart);
expect(view.submissionStartField.value).toEqual(subStart); expect(view.settingsFieldSelectors.submissionDueField.prop('value')).toEqual(subDue);
expect(view.submissionDueField.value).toEqual(subDue); expect(view.settingsFieldSelectors.hasPeer.prop('checked')).toEqual(true);
expect(view.hasPeer.prop('checked')).toEqual(true); expect(view.settingsFieldSelectors.hasSelf.prop('checked')).toEqual(true);
expect(view.hasSelf.prop('checked')).toEqual(true); expect(view.settingsFieldSelectors.hasAI.prop('checked')).toEqual(false);
expect(view.hasAI.prop('checked')).toEqual(false); expect(view.settingsFieldSelectors.hasTraining.prop('checked')).toEqual(true);
expect(view.hasTraining.prop('checked')).toEqual(true); expect(view.settingsFieldSelectors.peerMustGrade.prop('value')).toEqual('5');
expect(view.peerMustGrade.prop('value')).toEqual('5'); expect(view.settingsFieldSelectors.peerGradedBy.prop('value')).toEqual('3');
expect(view.peerGradedBy.prop('value')).toEqual('3'); expect(view.settingsFieldSelectors.peerDue.prop('value')).toEqual("");
expect(view.peerDue.prop('value')).toEqual(""); expect(view.settingsFieldSelectors.selfStart.prop('value')).toEqual("");
expect(view.selfStart.prop('value')).toEqual(""); expect(view.settingsFieldSelectors.selfDue.prop('value')).toEqual("");
expect(view.selfDue.prop('value')).toEqual("");
expect(view.aiTrainingExamplesCodeBox.getValue()).toEqual(""); expect(view.aiTrainingExamplesCodeBox.getValue()).toEqual("");
expect(view.studentTrainingExamplesCodeBox.getValue()).toEqual(assessments[0].examples); expect(view.studentTrainingExamplesCodeBox.getValue()).toEqual(assessments[0].examples);
expect(view.peerStart.prop('value')).toEqual("2014-10-04T00:00:00"); expect(view.settingsFieldSelectors.peerStart.prop('value')).toEqual("2014-10-04T00:00:00");
view.titleField.value = "This is the new title."; view.settingsFieldSelectors.titleField.prop('value', "This is the new title.");
view.updateEditorContext(); view.updateEditorContext();
expect(server.titleField).toEqual("This is the new title."); expect(server.titleField).toEqual("This is the new title.");
......
...@@ -15,37 +15,27 @@ OpenAssessment.StudioView = function(runtime, element, server) { ...@@ -15,37 +15,27 @@ OpenAssessment.StudioView = function(runtime, element, server) {
this.runtime = runtime; this.runtime = runtime;
this.server = server; this.server = server;
//Instantiates JQuery variables which will allow manipulation and display controls. this.liveElement = $(element);
var liveElement = $(element); var liveElement = this.liveElement;
this.promptBox = $('#openassessment_prompt_editor', liveElement).get(0); // Instantiates JQuery selector variables which will allow manipulation and display controls.
this.settingsFieldSelectors = {
this.titleField = $('#openassessment_title_editor', liveElement).first().get(0); promptBox: $('#openassessment_prompt_editor', liveElement),
titleField: $('#openassessment_title_editor', liveElement),
this.submissionStartField = $('#openassessment_submission_start_editor', liveElement).first().get(0); submissionStartField: $('#openassessment_submission_start_editor', liveElement),
submissionDueField: $('#openassessment_submission_due_editor', liveElement),
this.submissionDueField = $('#openassessment_submission_due_editor', liveElement).first().get(0); hasPeer: $('#include_peer_assessment', liveElement),
hasSelf: $('#include_self_assessment', liveElement),
// Finds our boolean checkboxes that indicate the assessment definition hasAI: $('#include_ai_assessment', liveElement),
this.hasPeer = $('#include_peer_assessment', liveElement); hasTraining: $('#include_student_training', liveElement),
this.hasSelf = $('#include_self_assessment', liveElement); peerMustGrade: $('#peer_assessment_must_grade', liveElement),
this.hasAI = $('#include_ai_assessment', liveElement); peerGradedBy: $('#peer_assessment_graded_by', liveElement),
this.hasTraining = $('#include_student_training', liveElement); peerStart: $('#peer_assessment_start_date', liveElement),
peerDue: $('#peer_assessment_due_date', liveElement),
this.peerMustGrade = $('#peer_assessment_must_grade', liveElement); selfStart: $('#self_assessment_start_date', liveElement),
this.peerGradedBy = $('#peer_assessment_graded_by', liveElement); selfDue: $('#self_assessment_due_date', liveElement)
this.peerStart = $('#peer_assessment_start_date', liveElement); };
this.peerDue = $('#peer_assessment_due_date', liveElement);
this.selfStart = $('#self_assessment_start_date', liveElement);
this.selfDue = $('#self_assessment_due_date', liveElement);
//Instantiates our codemirror codeboxes
this.rubricXmlBox = CodeMirror.fromTextArea(
$('#openassessment_rubric_editor', liveElement).first().get(0),
{mode: "xml", lineNumbers: true, lineWrapping: true}
);
this.aiTrainingExamplesCodeBox = CodeMirror.fromTextArea( this.aiTrainingExamplesCodeBox = CodeMirror.fromTextArea(
$('#ai_training_examples', liveElement).first().get(0), $('#ai_training_examples', liveElement).first().get(0),
...@@ -57,64 +47,74 @@ OpenAssessment.StudioView = function(runtime, element, server) { ...@@ -57,64 +47,74 @@ OpenAssessment.StudioView = function(runtime, element, server) {
{mode: "xml", lineNumbers: true, lineWrapping: true} {mode: "xml", lineNumbers: true, lineWrapping: true}
); );
// Install click handlers // Captures the HTML definition of the original criterion element. This will be the template
// used for all other criterion creations
var criterionHtml = $("#openassessment_criterion_1", liveElement).parent().html();
// Replaces all instances of the original ID (1) with the new fake ID in the string
// representation of the Criterion LI. This is our new template, with a C-C-C marker to replace.
this.criterionHtmlTemplate = criterionHtml.replace(new RegExp("1", "g"), "C-C-C");
// Captures the HTML definition of the original option element. Note that there are TWO template
// tags that need to be replaced "C-C-C" for the Criterion ID, and "O-O-O" for the option ID.
var optionHtml = $("#openassessment_criterion_1_option_1", liveElement).parent().html();
var criteriaReplaced = optionHtml.replace(new RegExp("criterion_1", "g"), "criterion_C-C-C");
this.optionHtmlTemplate = criteriaReplaced.replace(new RegExp("option_1", "g"), "option_O-O-O");
// Start us off with an empty setup, and uses the adding method to add a criteria (which in turn will
// add an option). This design choice was made to ensure consistent practices in adding and removing,
// the logic of which is all maintained in the function calls.
this.numberOfCriteria = 0;
this.numberOfOptions = [];
this.rubricCriteriaSelectors = [];
this.rubricFeedbackPrompt = $('#openassessment_rubric_feedback', liveElement);
this.hasRubricFeedbackPrompt = true;
$('#openassessment_criterion_list', liveElement).empty();
this.addNewCriterionToRubric();
var view = this; var view = this;
$('.openassessment_save_button', liveElement) .click(
function (eventData) { // Installs the save and cancel buttons
$('.openassessment_save_button', liveElement) .click( function (eventData) {
view.save(); view.save();
}); });
$('.openassessment_cancel_button', liveElement) .click( $('.openassessment_cancel_button', liveElement) .click( function (eventData) {
function (eventData) {
view.cancel(); view.cancel();
});
$('.openassessment_editor_content_and_tabs', liveElement) .tabs({
activate: function (event, ui){
view.rubricXmlBox.refresh();
}
}); });
$('#include_peer_assessment', liveElement) .change(function () { // Adds the tabbing functionality
if (this.checked){ $('.openassessment_editor_content_and_tabs', liveElement) .tabs();
$("#peer_assessment_description_closed", liveElement).fadeOut('fast');
$("#peer_assessment_settings_editor", liveElement).fadeIn();
} else {
$("#peer_assessment_settings_editor", liveElement).fadeOut('fast');
$("#peer_assessment_description_closed", liveElement).fadeIn();
}
});
$('#include_self_assessment', liveElement) .change(function () { // Installs all of the checkbox listeners in the settings tab
if (this.checked){ view.addSettingsAssessmentCheckboxListener("ai_assessment", liveElement);
$("#self_assessment_description_closed", liveElement).fadeOut('fast'); view.addSettingsAssessmentCheckboxListener("self_assessment", liveElement);
$("#self_assessment_settings_editor", liveElement).fadeIn(); view.addSettingsAssessmentCheckboxListener("peer_assessment", liveElement);
} else { view.addSettingsAssessmentCheckboxListener("student_training", liveElement);
$("#self_assessment_settings_editor", liveElement).fadeOut('fast');
$("#self_assessment_description_closed", liveElement).fadeIn(); $('#openassessment_rubric_add_criterion', liveElement) .click( function (eventData) {
} view.addNewCriterionToRubric(liveElement);
}); });
$('#include_ai_assessment', liveElement) .change(function () { // Adds a listener which removes rubric feedback
if (this.checked){ $("#openassessment_rubric_feedback_remove", liveElement). click( function(eventData){
$("#ai_assessment_description_closed", liveElement).fadeOut('fast'); $("#openassessment_rubric_feedback_header_open", liveElement).fadeOut();
$("#ai_assessment_settings_editor", liveElement).fadeIn(); $("#openassessment_rubric_feedback_input_wrapper", liveElement).fadeOut();
} else { $("#openassessment_rubric_feedback_header_closed", liveElement).fadeIn();
$("#ai_assessment_settings_editor", liveElement).fadeOut('fast'); view.hasRubricFeedbackPrompt = false;
$("#ai_assessment_description_closed", liveElement).fadeIn();
}
}); });
$('#include_student_training', liveElement) .change(function () { // Adds a listener which adds rubric feedback if not already displayed.
if (this.checked){ $("#openassessment_rubric_feedback_header_closed", liveElement). click( function(eventData){
$("#student_training_description_closed", liveElement).fadeOut('fast'); $("#openassessment_rubric_feedback_header_closed", liveElement).fadeOut();
$("#student_training_settings_editor", liveElement).fadeIn(); $("#openassessment_rubric_feedback_header_open", liveElement).fadeIn();
} else { $("#openassessment_rubric_feedback_input_wrapper", liveElement).fadeIn();
$("#student_training_settings_editor", liveElement).fadeOut('fast'); view.hasRubricFeedbackPrompt = true;
$("#student_training_description_closed", liveElement).fadeIn();
}
}); });
// Initially Hides the rubric "add rubric feedback" div
$("#openassessment_rubric_feedback_header_closed", liveElement).hide();
}; };
OpenAssessment.StudioView.prototype = { OpenAssessment.StudioView.prototype = {
...@@ -125,36 +125,80 @@ OpenAssessment.StudioView.prototype = { ...@@ -125,36 +125,80 @@ OpenAssessment.StudioView.prototype = {
load: function () { load: function () {
var view = this; var view = this;
this.server.loadEditorContext().done( this.server.loadEditorContext().done(
function (prompt, rubricXml, title, subStart, subDue, assessments) { function (prompt, rubric, title, subStart, subDue, assessments) {
view.rubricXmlBox.setValue(rubricXml); view.settingsFieldSelectors.submissionStartField.prop('value', subStart);
view.submissionStartField.value = subStart; view.settingsFieldSelectors.submissionDueField.prop('value', subDue);
view.submissionDueField.value = subDue; view.settingsFieldSelectors.promptBox.prop('value', prompt);
view.promptBox.value = prompt; view.settingsFieldSelectors.titleField.prop('value', title);
view.titleField.value = title; view.settingsFieldSelectors.hasTraining.prop('checked', false).change();
view.hasTraining.prop('checked', false).change(); view.settingsFieldSelectors.hasPeer.prop('checked', false).change();
view.hasPeer.prop('checked', false).change(); view.settingsFieldSelectors.hasSelf.prop('checked', false).change();
view.hasSelf.prop('checked', false).change(); view.settingsFieldSelectors.hasAI.prop('checked', false).change();
view.hasAI.prop('checked', false).change();
for (var i = 0; i < assessments.length; i++) { for (var i = 0; i < assessments.length; i++) {
var assessment = assessments[i]; var assessment = assessments[i];
if (assessment.name == 'peer-assessment') { if (assessment.name == 'peer-assessment') {
view.peerMustGrade.prop('value', assessment.must_grade); view.settingsFieldSelectors.peerMustGrade.prop('value', assessment.must_grade);
view.peerGradedBy.prop('value', assessment.must_be_graded_by); view.settingsFieldSelectors.peerGradedBy.prop('value', assessment.must_be_graded_by);
view.peerStart.prop('value', assessment.start); view.settingsFieldSelectors.peerStart.prop('value', assessment.start);
view.peerDue.prop('value', assessment.due); view.settingsFieldSelectors.peerDue.prop('value', assessment.due);
view.hasPeer.prop('checked', true).change(); view.settingsFieldSelectors.hasPeer.prop('checked', true).change();
} else if (assessment.name == 'self-assessment') { } else if (assessment.name == 'self-assessment') {
view.selfStart.prop('value', assessment.start); view.settingsFieldSelectors.selfStart.prop('value', assessment.start);
view.selfDue.prop('value', assessment.due); view.settingsFieldSelectors.selfDue.prop('value', assessment.due);
view.hasSelf.prop('checked', true).change(); view.settingsFieldSelectors.hasSelf.prop('checked', true).change();
} else if (assessment.name == 'example-based-assessment') { } else if (assessment.name == 'example-based-assessment') {
view.aiTrainingExamplesCodeBox.setValue(assessment.examples); view.settingsFieldSelectors.aiTrainingExamplesCodeBox.setValue(assessment.examples);
view.hasAI.prop('checked', true).change(); view.settingsFieldSelectors.hasAI.prop('checked', true).change();
} else if (assessment.name == 'student-training') { } else if (assessment.name == 'student-training') {
view.studentTrainingExamplesCodeBox.setValue(assessment.examples); view.studentTrainingExamplesCodeBox.setValue(assessment.examples);
view.hasTraining.prop('checked', true).change(); view.settingsFieldSelectors.hasTraining.prop('checked', true).change();
}
}
// Corrects the length of the number of criteria
while(view.numberOfCriteria < rubric.criteria.length){
view.addNewCriterionToRubric();
}
while(view.numberOfCriteria > rubric.criteria.length){
view.removeCriterionFromRubric(1);
}
// Corrects the number of options in each criterion
for (i = 0; i < rubric.criteria.length; i++){
while(view.numberOfOptions[i+1] < rubric.criteria[i].options.length){
view.addNewOptionToCriterion(view.liveElement, i+1);
}
while(view.numberOfOptions[i+1] > rubric.criteria[i].options.length){
view.removeOptionFromCriterion(view.liveElement, i+1, 1);
}
}
// Inserts the data from the rubric into the GUI's fields
for (i = 0; i < rubric.criteria.length; i++){
var criterion = rubric.criteria[i];
var selectors = view.rubricCriteriaSelectors[i+1];
// Transfers the Criteria Fields
selectors.name.prop('value', criterion.name);
selectors.prompt.prop('value', criterion.prompt);
selectors.feedback = criterion.feedback;
for (var j = 0; j < criterion.options.length; j++){
var option = criterion.options[j];
var optionSelectors = selectors.options[j+1];
// Transfers all of the option data.
optionSelectors.name.prop('value', option.name);
optionSelectors.points.prop('value', option.points);
optionSelectors.explanation.prop('value', option.explanation);
} }
} }
if (rubric.feedbackprompt){
view.rubricFeedbackPrompt.prop('value', rubric.feedbackprompt);
view.hasRubricFeedbackPrompt = true;
} else {
view.rubricFeedbackPrompt.prop('value', "");
view.hasRubricFeedbackPrompt = false;
}
}).fail(function (msg) { }).fail(function (msg) {
view.showError(msg); view.showError(msg);
} }
...@@ -180,7 +224,26 @@ OpenAssessment.StudioView.prototype = { ...@@ -180,7 +224,26 @@ OpenAssessment.StudioView.prototype = {
} }
} }
).fail(function (errMsg) { ).fail(function (errMsg) {
view.showError(msg); view.showError(errMsg);
});
},
/**
Construct checkbox listeners for all of our assessment modules
Args:
name (string): name of assessment module to install listener on
liveElement (DOM element): the live DOM selector
*/
addSettingsAssessmentCheckboxListener: function (name, liveElement) {
$("#include_" + name , liveElement) .change(function () {
if (this.checked){
$("#" + name + "_description_closed", liveElement).fadeOut('fast');
$("#" + name + "_settings_editor", liveElement).fadeIn();
} else {
$("#" + name + "_settings_editor", liveElement).fadeOut('fast');
$("#" + name + "_description_closed", liveElement).fadeIn();
}
}); });
}, },
...@@ -201,6 +264,208 @@ OpenAssessment.StudioView.prototype = { ...@@ -201,6 +264,208 @@ OpenAssessment.StudioView.prototype = {
}, },
/** /**
Adds a new criterion to the rubric.
*/
addNewCriterionToRubric: function (){
var view = this;
var liveElement = this.liveElement;
// Always appends the new criterion to the end of the list, and we force linear ordering.
var newCriterionID = this.numberOfCriteria + 1;
this.numberOfCriteria += 1;
this.numberOfOptions[newCriterionID] = 0;
// Fills in the template with the new criterion ID
var criterionHtml = this.criterionHtmlTemplate.replace(new RegExp('C-C-C', 'g'), '' + newCriterionID);
// Adds the new criterion to the DOM
$("#openassessment_criterion_list", liveElement).append(criterionHtml);
// Now that we have altered our LiveElement, we need to "reset" it so that it recognizes the new criterion.
liveElement = $("#openassessment_criterion_" + newCriterionID);
$(".openassessment_criterion_option_list", liveElement).empty();
// Adds our selector elements for easy future access.
view.rubricCriteriaSelectors[newCriterionID] = {
criterion: liveElement,
name: $('.openassessment_criterion_name', liveElement).first(),
prompt: $('.openassessment_criterion_prompt', liveElement).first(),
options: [],
feedback: 'disabled'
};
// Defaults to no feedback
$('input:radio[value=disabled]', liveElement).prop('checked', true);
view.addNewOptionToCriterion(liveElement, newCriterionID);
// Adds a listener that will collapse/expand the criterion on click.
$('#openassessment_display_criterion_' + newCriterionID, liveElement) .change( function () {
if (this.checked){
$('#openassessment_criterion_body_' + newCriterionID, liveElement).fadeIn();
} else {
$('#openassessment_criterion_body_' + newCriterionID, liveElement).fadeOut();
}
});
// Adds a listener which will delete the criterion on a click of the remove button
// The methodology for deletion is to shift all information from previous elements down into
$("#openassessment_criterion_" + newCriterionID + "_remove", liveElement) .click( function (eventData) {
view.removeCriterionFromRubric(newCriterionID);
});
// Adds a listener which will add another option to the Criterion's definition.
$("#openassessment_criterion_" + newCriterionID + "_add_option", liveElement).click( function (eventData) {
view.addNewOptionToCriterion(liveElement, newCriterionID);
});
// Adds a listener which removes criterion feedback
$(".openassessment_feedback_remove_button", liveElement). click( function(eventData){
$(".openassessment_criterion_feedback_direction", liveElement).fadeOut();
$(".openassessment_criterion_feedback_header_open", liveElement).fadeOut();
$(".openassessment_criterion_feedback_header_closed", liveElement).fadeIn();
$(".openassessment_feedback_remove_button", liveElement).fadeOut();
view.rubricCriteriaSelectors[newCriterionID].hasFeedback = false;
});
// Adds a listener which adds criterion feedback if not already displayed.
$(".openassessment_criterion_feedback_header_closed", liveElement).click( function (eventData){
$(".openassessment_criterion_feedback_direction", liveElement).fadeIn();
$(".openassessment_criterion_feedback_header_open", liveElement).fadeIn();
$(".openassessment_criterion_feedback_header_closed", liveElement).fadeOut();
$(".openassessment_feedback_remove_button", liveElement).fadeIn();
view.rubricCriteriaSelectors[newCriterionID].hasFeedback = true;
});
// Hides the criterion header used for adding
$(".openassessment_criterion_feedback_header_closed", liveElement).hide();
},
/**
Removes a specified criterion from the problem's rubric definition.
Changes are made in the DOM, in support/control structures.
*/
removeCriterionFromRubric: function(criterionToRemove){
var view = this;
var numCriteria = view.numberOfCriteria;
var selectors = view.rubricCriteriaSelectors;
// Shifts all data from "higher up" criteria down one in order to allow us to delete the last
// element without deleting information input by the user
for (var i = criterionToRemove; i < numCriteria; i++){
// Shifts all criterion field values
selectors[i].name.prop('value', selectors[i+1].name.prop('value'));
selectors[i].prompt.prop('value', selectors[i+1].prompt.prop('value'));
selectors[i].feedback = selectors[i+1].feedback;
$('input:radio[value="disabled"]', selectors[i].criterion).prop('checked', true);
// Ensures that we won't delete information during the shift by ensuring that the option lists are of the
//same length. Note it doesn't matter what we add or delete, simply that the lengths add up.
while (view.numberOfOptions[i] < view.numberOfOptions[i+1]){
view.addNewOptionToCriterion(selectors[i].criteria, i);
}
while (view.numberOfOptions[i] > view.numberOfOptions[i+1]){
view.removeOptionFromCriterion(selectors[i].criteria, i, 1);
}
// Transfers all data from each option to the next within a criterion.
var options1 = selectors[i].options;
var options2 = selectors[i+1].options;
var numOptions2 = view.numberOfOptions[i+1];
for (var j = 1; j < numOptions2; j++){
options1[j].points.prop('value', options2[j].points.prop('value'));
options1[j].name.prop('value', options2[j].name.prop('value'));
options1[j].explanation.prop('value', options2[j].explanation.prop('value'));
}
}
// Physically removes the rubric criteria from the DOM
view.rubricCriteriaSelectors[view.rubricCriteriaSelectors.length - 1].criterion.remove();
// Deletes the criteria from our three tracking statistics/structures
view.rubricCriteriaSelectors = view.rubricCriteriaSelectors.slice(0,numCriteria);
view.numberOfOptions = view.numberOfOptions.slice(0, numCriteria);
view.numberOfCriteria -= 1;
},
/**
Initializes a new option for a given criterion. This code block dictates the methodology for
adding and removing options to a rubric.
Args:
liveElement (DOM element): An element containing the criterion interface in the DOM.
criterionID (string): The specific number of the criterion the option is being added to
*/
addNewOptionToCriterion: function (liveElement, criterionID){
var view = this;
// Finds the ID that will be associated with the option (it will be added to the end)
var newOptionID = this.numberOfOptions[criterionID] + 1;
this.numberOfOptions[criterionID] += 1;
// Replaces the template values with the true criterion and option ID's, and appends it on to the DOM
var optionHtml = this.optionHtmlTemplate;
optionHtml = optionHtml.replace(new RegExp("C-C-C", 'g'), "" + criterionID);
optionHtml = optionHtml.replace(new RegExp("O-O-O", 'g'), "" + newOptionID);
$("#openassessment_criterion_" + criterionID + "_options", liveElement).append(optionHtml);
// Resets the Live Element to be the updated (and newly created) option.
liveElement = $("#openassessment_criterion_" + criterionID + "_option_" + newOptionID);
// Constructs and assigns a dictionary of all of the selectors we store for each option.
view.rubricCriteriaSelectors[criterionID].options[newOptionID] = {
option: liveElement,
points: $("#openassessment_criterion_" + criterionID + "_option_" + newOptionID + "_points", liveElement),
name: $("#openassessment_criterion_" + criterionID + "_option_" + newOptionID + "_name", liveElement),
explanation: $("#openassessment_criterion_" + criterionID + "_option_" + newOptionID + "_explanation", liveElement)
};
// Sets the remove behavior. When deleted, an option will shift all of the data "behind" it on the list
// of options toward itself, and then deletes the last element in the list. This ensures that our options
// are always increasing by one, and that the data doesn't remain tethered to where it was entered.
$("#openassessment_criterion_" + criterionID + "_option_" + newOptionID + "_remove", liveElement).click(
function(eventData){
view.removeOptionFromCriterion(liveElement, criterionID, newOptionID);
}
);
},
/**
Removes a specified element from the DOM and from all tracking data. Note that no action is
taken against the specified element, rather, data is shifted down the chain (to construct the
illusion that the specified element was deleted), and then the last element is actually deleted.
Args:
liveElement (DOM element): An element containing the criterion interface in the DOM.
criterionID (string): The criterion ID that we are deleting from
optionToRemove (string): The option ID that we are "deleting"
*/
removeOptionFromCriterion: function(liveElement, criterionID, optionToRemove){
var view = this;
var numberOfOptions = view.numberOfOptions[criterionID];
var optionSelectors = view.rubricCriteriaSelectors[criterionID].options;
// Shifts all data down, then deletes the last element, to create the appearance we deleted the given
// elements.
for (var i = optionToRemove; i < numberOfOptions; i++){
// Utilizes stored selectors to perform the swaps.
optionSelectors[i].points.prop('value', optionSelectors[i+1].points.prop('value'));
optionSelectors[i].name.prop('value', optionSelectors[i+1].name.prop('value'));
optionSelectors[i].explanation.prop('value', optionSelectors[i+1].explanation.prop('value'));
}
optionSelectors[optionSelectors.length - 1].option.remove();
view.rubricCriteriaSelectors[criterionID].options =
view.rubricCriteriaSelectors[criterionID].options.slice(0, (optionSelectors.length - 1));
view.numberOfOptions[criterionID] -= 1;
},
/**
Save the updated XML definition to the server. Save the updated XML definition to the server.
**/ **/
updateEditorContext: function () { updateEditorContext: function () {
...@@ -209,54 +474,86 @@ OpenAssessment.StudioView.prototype = { ...@@ -209,54 +474,86 @@ OpenAssessment.StudioView.prototype = {
this.runtime.notify('save', {state: 'start'}); this.runtime.notify('save', {state: 'start'});
// Send the updated XML to the server // Send the updated XML to the server
var prompt = this.promptBox.value; var prompt = this.settingsFieldSelectors.promptBox.prop('value');
var rubricXml = this.rubricXmlBox.getValue(); var title = this.settingsFieldSelectors.titleField.prop('value');
var title = this.titleField.value; var subStart = this.settingsFieldSelectors.submissionStartField.prop('value');
var subStart = this.submissionStartField.value; var subDue = this.settingsFieldSelectors.submissionDueField.prop('value');
var subDue = this.submissionDueField.value;
// Grabs values from all of our fields, and stores them in a format which can be easily validated.
var rubricCriteria = [];
for (var i = 1; i <= this.numberOfCriteria; i++){
var selectorDict = this.rubricCriteriaSelectors[i];
var criterionValueDict = {
order_num: i - 1,
name: selectorDict.name.prop('value'),
prompt: selectorDict.prompt.prop('value'),
feedback: $("#openassessment_criterion_" + i + "_feedback").val()
};
var optionSelectorList = selectorDict.options;
var optionValueList = [];
for (var j = 1; j <= this.numberOfOptions[i]; j++){
var optionSelectors = optionSelectorList[j];
optionValueList = optionValueList.concat([{
order_num: j-1,
points: optionSelectors.points.prop('value'),
name: optionSelectors.name.prop('value'),
explanation: optionSelectors.explanation.prop('value')
}]);
}
criterionValueDict.options = optionValueList;
rubricCriteria = rubricCriteria.concat([criterionValueDict]);
}
var rubric = { 'criteria': rubricCriteria };
if (this.hasRubricFeedbackPrompt){
rubric.feedbackprompt = this.rubricFeedbackPrompt.prop('value');
}
var assessments = []; var assessments = [];
if (this.hasTraining.prop('checked')){ if (this.settingsFieldSelectors.hasTraining.prop('checked')){
assessments[assessments.length] = { assessments[assessments.length] = {
"name": "student-training", "name": "student-training",
"examples": this.studentTrainingExamplesCodeBox.getValue() "examples": this.studentTrainingExamplesCodeBox.getValue()
}; };
} }
if (this.hasPeer.prop('checked')) { if (this.settingsFieldSelectors.hasPeer.prop('checked')) {
var assessment = { var assessment = {
"name": "peer-assessment", "name": "peer-assessment",
"must_grade": parseInt(this.peerMustGrade.prop('value')), "must_grade": parseInt(this.settingsFieldSelectors.peerMustGrade.prop('value')),
"must_be_graded_by": parseInt(this.peerGradedBy.prop('value')) "must_be_graded_by": parseInt(this.settingsFieldSelectors.peerGradedBy.prop('value'))
}; };
var startStr = this.peerStart.prop('value'); var startStr = this.settingsFieldSelectors.peerStart.prop('value');
var dueStr = this.peerDue.prop('value'); var dueStr = this.settingsFieldSelectors.peerDue.prop('value');
if (startStr){ if (startStr){
assessment = $.extend(assessment, {"start": startStr}) assessment = $.extend(assessment, {"start": startStr});
} }
if (dueStr){ if (dueStr){
assessment = $.extend(assessment, {"due": dueStr}) assessment = $.extend(assessment, {"due": dueStr});
} }
assessments[assessments.length] = assessment; assessments[assessments.length] = assessment;
} }
if (this.hasSelf.prop('checked')) { if (this.settingsFieldSelectors.hasSelf.prop('checked')) {
assessment = { var assessment = {
"name": "self-assessment" "name": "self-assessment"
}; };
startStr = this.selfStart.prop('value'); var startStr = this.settingsFieldSelectors.selfStart.prop('value');
dueStr = this.selfDue.prop('value'); var dueStr = this.settingsFieldSelectors.selfDue.prop('value');
if (startStr){ if (startStr){
assessment = $.extend(assessment, {"start": startStr}) assessment = $.extend(assessment, {"start": startStr});
} }
if (dueStr){ if (dueStr){
assessment = $.extend(assessment, {"due": dueStr}) assessment = $.extend(assessment, {"due": dueStr});
} }
assessments[assessments.length] = assessment; assessments[assessments.length] = assessment;
} }
if (this.hasAI.prop('checked')) { if (this.settingsFieldSelectors.hasAI.prop('checked')) {
assessments[assessments.length] = { assessments[assessments.length] = {
"name": "example-based-assessment", "name": "example-based-assessment",
"examples": this.aiTrainingExamplesCodeBox.getValue() "examples": this.aiTrainingExamplesCodeBox.getValue()
...@@ -264,7 +561,7 @@ OpenAssessment.StudioView.prototype = { ...@@ -264,7 +561,7 @@ OpenAssessment.StudioView.prototype = {
} }
var view = this; var view = this;
this.server.updateEditorContext(prompt, rubricXml, title, subStart, subDue, assessments).done(function () { this.server.updateEditorContext(prompt, rubric, title, subStart, subDue, assessments).done(function () {
// Notify the client-side runtime that we finished saving // Notify the client-side runtime that we finished saving
// so it can hide the "Saving..." notification. // so it can hide the "Saving..." notification.
view.runtime.notify('save', {state: 'end'}); view.runtime.notify('save', {state: 'end'});
......
...@@ -438,11 +438,11 @@ OpenAssessment.Server.prototype = { ...@@ -438,11 +438,11 @@ OpenAssessment.Server.prototype = {
function(err) { console.log(err); } function(err) { console.log(err); }
); );
**/ **/
updateEditorContext: function(prompt, rubricXml, title, sub_start, sub_due, assessments) { updateEditorContext: function(prompt, rubric, title, sub_start, sub_due, assessments) {
var url = this.url('update_editor_context'); var url = this.url('update_editor_context');
var payload = JSON.stringify({ var payload = JSON.stringify({
'prompt': prompt, 'prompt': prompt,
'rubric': rubricXml, 'rubric': rubric,
'title': title, 'title': title,
'submission_start': sub_start, 'submission_start': sub_start,
'submission_due': sub_due, 'submission_due': sub_due,
......
...@@ -182,7 +182,7 @@ ...@@ -182,7 +182,7 @@
.openassessment_editor_content_and_tabs { .openassessment_editor_content_and_tabs {
width: 100%; width: 100%;
height: 370px; height: 373px;
} }
#openassessment_editor_header{ #openassessment_editor_header{
...@@ -207,7 +207,10 @@ ...@@ -207,7 +207,10 @@
.oa_editor_content_wrapper { .oa_editor_content_wrapper {
height: 100%; height: 100%;
width: 100%; width: 100%;
padding: ($baseline-v/4) ($baseline-h/4); border-radius: 3px;
border: 1px solid $edx-gray-d1;
background-color: #f5f5f5;
overflow-y: scroll;
} }
#openassessment_prompt_editor { #openassessment_prompt_editor {
...@@ -215,6 +218,18 @@ ...@@ -215,6 +218,18 @@
height: 100%; height: 100%;
resize: none; resize: none;
border: none; border: none;
border-radius: 4px;
padding: 10px;
textarea{
font-size: 14px;
border: none;
overflow: auto;
outline: none;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
} }
#openassessment_rubric_editor { #openassessment_rubric_editor {
...@@ -223,8 +238,7 @@ ...@@ -223,8 +238,7 @@
} }
#oa_basic_settings_editor { #oa_basic_settings_editor {
padding: 20px 20px; border-bottom: 1px solid $edx-gray-d1;
border-bottom: 1px solid $edx-gray-d3;
#openassessment_title_editor_wrapper{ #openassessment_title_editor_wrapper{
label{ label{
width: 25%; width: 25%;
...@@ -238,26 +252,17 @@ ...@@ -238,26 +252,17 @@
} }
#openassessment_step_select_description{ #openassessment_step_select_description{
margin: 10px 0; padding: 10px 10px 0 10px;
text-align: center;
font-size: 80%;
} }
.openassessment_assessment_module_settings_editor{ .openassessment_assessment_module_settings_editor{
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid $edx-gray-l3;
}
.openassessment_indent_line_input{
padding: 5px 20px;
}
#oa_settings_editor_wrapper {
overflow-y: scroll; overflow-y: scroll;
} padding: 5px;
margin: 10px;
#openassessment_title_editor { border: 1px solid lightgray;
width: 300px; border-radius: 3px;
margin-left: 50px;
} }
.openassessment_description{ .openassessment_description{
...@@ -265,41 +270,12 @@ ...@@ -265,41 +270,12 @@
margin: 0; margin: 0;
} }
.openassessment_date_field{
width: 130px;
}
.openassessment_number_field{
width: 25px;
}
.openassessment_description_closed{ .openassessment_description_closed{
@extend .openassessment_description; @extend .openassessment_description;
} }
.openassessment_text_field_wrapper{
width: 50%;
text-align: center;
}
.openassessment_right_text_field_wrapper {
@extend .openassessment_text_field_wrapper;
float: right;
}
.openassessment_left_text_field_wrapper {
@extend .openassessment_text_field_wrapper;
float: left;
}
.openassessment_due_date_editor{
height: 30px;
}
.openassessment_inclusion_wrapper{ .openassessment_inclusion_wrapper{
background-color: $edx-gray-l3; margin: 2.5px 5px;
padding: ($baseline-v/8) ($baseline-h/8);
margin: ($baseline-v/8) ($baseline-h/8);
border-radius: ($baseline-v)/8;
input[type="checkbox"]{ input[type="checkbox"]{
display: none; display: none;
...@@ -316,6 +292,7 @@ ...@@ -316,6 +292,7 @@
input[type="checkbox"]:checked + label:before{ input[type="checkbox"]:checked + label:before{
content: "\f046"; content: "\f046";
color: #009fe6;
} }
} }
...@@ -349,7 +326,320 @@ ...@@ -349,7 +326,320 @@
} }
} }
hr { .openassessment_assessment_module_editor{
padding: 2.5px 0px;
.openassessment_description{
padding-left: 15px;
}
}
#oa_rubric_editor_wrapper{
.wrapper-comp-settings{
display: initial;
}
#openassessment_rubric_instructions{
background-color: $edx-gray-l2;
border-bottom: 1px solid $edx-gray-d3;
padding: 10px;
}
.openassessment_criterion {
padding-bottom: 10px;
.openassessment_criterion_header {
margin: 10px;
padding: 5px;
border-bottom: 1px solid $edx-gray-d3;
overflow: auto;
input[type="checkbox"] {
display: none;
}
input[type="checkbox"] + h6:before {
font-family: "FontAwesome";
display: inline-block;
margin-right: ($baseline-h/4);
width: auto;
height: auto;
content: "\f054";
}
input[type="checkbox"]:checked + label:before {
content: "\f078";
}
.openassessment_criterion_header_title {
text-transform: uppercase;
width: 50%;
display: inline-block;
float: left;
}
.openassessment_criterion_header_remove {
@extend .openassessment_rubric_remove_button
}
}
.openassessment_criterion_add_option {
h2:before {
font-family: FontAwesome;
content: "\f067";
display: inline-block;
margin: 0 5px;
}
background-color: $edx-gray-d1;
padding: 5px;
margin: 0px 10px 10px 10px;
border-radius: 3px;
}
.openassessment_criterion_basic_editor{
.comp-setting-entry{
padding-right: 0;
margin-right: 10px;
overflow: auto;
.wrapper-comp-settings{
input{
font-size: 11px;
float: right;
width: 70%
}
.openassessment_criterion_prompt{
padding: 10px;
@extend .openassessment_large_text_input;
width: 70%;
float: right;
}
label{
padding: 0;
margin: 0;
}
}
}
}
.openassessment_criterion_feedback_wrapper{
.openassessment_criterion_feedback_header {
background-color: $edx-gray-l2;
padding: 5px;
margin: 10px;
border-radius: 3px;
.openassessment_criterion_feedback_header_closed:before{
font-family: "FontAwesome";
display: inline-block;
margin-right: 10px;
margin-left: 5px;
width: auto;
height: auto;
content: "\f067";
}
}
.openassessment_criterion_feedback_direction{
label{
margin-left: 15px;
}
}
}
}
.openassessment_criterion_option{
padding: 5px;
.openassessment_option_header{
background-color: $edx-gray-l2;
padding: 5px 5px 5px 10px;
margin: 5px 5px 8px 5px;
border-radius: 3px;
.openassessment_option_header_remove{
@extend .openassessment_rubric_remove_button
}
}
.openassessment_criterion_option_point_wrapper{
width: 40%;
border-top: none;
padding: 5px 5px 5px 0px;
float: left;
margin: 0;
label{
width: 40%;
vertical-align: middle;
padding: 0;
margin: 0;
}
input{
padding: 10px;
float: right;
width: 55%;
font-size: 11px;
}
}
.openassessment_criterion_option_name_wrapper{
float: left;
width: 60%;
padding: 5px 10px 5px 20px;
border-top: 0;
margin: 0;
label{
width: 25%;
vertical-align: middle;
padding: 0;
margin: 0;
}
input{
padding: 10px;
font-size: 11px;
width: 60%;
float: right;
}
}
.openassessment_criterion_option_explanation_wrapper{
padding: 10px 5px 0px 20px;
width: 100%;
display: inline-block;
margin: 0;
label{
width: 25%;
text-align: left;
}
textarea{
padding: 10px;
@extend .openassessment_large_text_input;
width: 70%;
float: right;
}
}
}
.openassessment_line_input{
padding: 10px;
overflow: auto;
label{
width: 30%;
text-align: left;
}
input{
width:70%;
min-width: auto;
float: right;
}
textarea{
width:70%;
float:right;
@extend .openassessment_large_text_input
}
}
.openassessment_large_text_input{
height: 70px;
width: 70%;
@extend .openassessment_input_styling;
}
.openassessment_input_styling{
resize: none;
box-sizing: border-box;
border: 1px solid #b2b2b2;
border-radius: 2px;
padding: 6px 8px 8px;
background-color: #f2f2f2;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
font-family: 'Open Sans', sans-serif;
font-size: 11px;
color: #4c4c4c;
outline: 0;
}
.openassessment_rubric_remove_button{
h2:after{
font-family: FontAwesome;
content: "\f00d";
display: inline-block;
color: inherit;
margin: 0 5px;
}
h2{
text-transform: uppercase;
font-size: 80%;
float: right;
display: inline-block;
}
margin: 0 5px;
float: right;
}
.openassessment_rubric_feedback_wrapper{
padding: 0 10px;
.openassessment_rubric_feedback_header{
margin-top: 10px;
border-bottom: 1px solid $edx-gray-d3;
font-size: 125%;
padding: 10px;
padding-right: 20px;
}
.openassessment_feedback_radio_toggle{
input[type="radio"]{
display: none;
}
input[type="radio"] + label:before{
font-family: "FontAwesome";
display: inline-block;
margin-right: ($baseline-h/4);
width: auto;
height: auto;
content: "\f10c";
}
input[type="radio"]:checked + label:before{
content: "\f05d";
}
}
}
#openassessment_rubric_add_criterion{
h6:before{
font-family: "FontAwesome";
display: inline-block;
margin-left: 5px;
margin-right: 10px;
width: auto;
height: auto;
content: "\f067";
}
background-color: $edx-gray-d1;
padding: 10px;
margin: 10px, 0;
}
}
hr{
background-color: transparent; background-color: transparent;
color: $edx-gray-d3; color: $edx-gray-d3;
height: 1px; height: 1px;
...@@ -359,7 +649,12 @@ ...@@ -359,7 +649,12 @@
} }
#openassessment_make_invisible{
}
.modal-content { .modal-content {
height: 470px !important; height: 470px !important;
background-color: #e5e5e5;
} }
...@@ -69,7 +69,7 @@ class StudioMixin(object): ...@@ -69,7 +69,7 @@ class StudioMixin(object):
return {'success': False, 'msg': _('Error updating XBlock configuration')} return {'success': False, 'msg': _('Error updating XBlock configuration')}
try: try:
rubric = xml.parse_rubric_xml_str(data["rubric"]) rubric = verify_rubric_format(data['rubric'])
submission_due = xml.parse_date(data["submission_due"], name="submission due date") submission_due = xml.parse_date(data["submission_due"], name="submission due date")
submission_start = xml.parse_date(data["submission_start"], name="submission start date") submission_start = xml.parse_date(data["submission_start"], name="submission start date")
assessments = parse_assessment_dictionaries(data["assessments"]) assessments = parse_assessment_dictionaries(data["assessments"])
...@@ -82,8 +82,8 @@ class StudioMixin(object): ...@@ -82,8 +82,8 @@ class StudioMixin(object):
return {'success': False, 'msg': _('Validation error: {error}').format(error=msg)} return {'success': False, 'msg': _('Validation error: {error}').format(error=msg)}
self.update( self.update(
rubric['criteria'], rubric.get('criteria', []),
rubric['feedbackprompt'], rubric.get('feedbackprompt', None),
assessments, assessments,
submission_due, submission_due,
submission_start, submission_start,
...@@ -112,12 +112,9 @@ class StudioMixin(object): ...@@ -112,12 +112,9 @@ class StudioMixin(object):
""" """
try: try:
rubric = xml.serialize_rubric_to_xml_str(self)
# Copies the rubric assessments so that we can change student training examples from dict -> str without # Copies the rubric assessments so that we can change student training examples from dict -> str without
# negatively modifying the openassessmentblock definition. # negatively modifying the openassessmentblock definition.
assessment_list = copy.deepcopy(self.rubric_assessments) assessment_list = copy.deepcopy(self.rubric_assessments)
# Finds the student training dictionary, if it exists, and replaces the examples with their XML definition # Finds the student training dictionary, if it exists, and replaces the examples with their XML definition
student_training_dictionary = [d for d in assessment_list if d["name"] == "student-training"] student_training_dictionary = [d for d in assessment_list if d["name"] == "student-training"]
if student_training_dictionary: if student_training_dictionary:
...@@ -127,9 +124,9 @@ class StudioMixin(object): ...@@ -127,9 +124,9 @@ class StudioMixin(object):
student_training_dictionary["examples"] = examples student_training_dictionary["examples"] = examples
# We do not expect serialization to raise an exception, but if it does, handle it gracefully. # We do not expect serialization to raise an exception, but if it does, handle it gracefully.
except Exception as ex: except:
msg = _('An unexpected error occurred while loading the problem: {error}').format(error=ex) logger.exception("An error occurred while serializing the XBlock")
logger.error(msg) msg = _('An unexpected error occurred while loading the problem')
return {'success': False, 'msg': msg, 'xml': u''} return {'success': False, 'msg': msg, 'xml': u''}
# Populates the context for the assessments section of the editing # Populates the context for the assessments section of the editing
...@@ -137,13 +134,17 @@ class StudioMixin(object): ...@@ -137,13 +134,17 @@ class StudioMixin(object):
# section. # section.
submission_due = self.submission_due if self.submission_due else '' submission_due = self.submission_due if self.submission_due else ''
submission_start = self.submission_start if self.submission_start else '' submission_start = self.submission_start if self.submission_start else ''
rubric_dict = {
'criteria' : self.rubric_criteria,
'feedbackprompt': unicode(self.rubric_feedback_prompt)
}
return { return {
'success': True, 'success': True,
'msg': '', 'msg': '',
'rubric': rubric, 'rubric': rubric_dict,
'prompt': self.prompt, 'prompt': self.prompt,
'submission_due': submission_due, 'submission_due': submission_due,
'submission_start': submission_start, 'submission_start': submission_start,
...@@ -240,4 +241,134 @@ def parse_assessment_dictionaries(input_assessments): ...@@ -240,4 +241,134 @@ def parse_assessment_dictionaries(input_assessments):
# Update the list of assessments # Update the list of assessments
assessments_list.append(assessment_dict) assessments_list.append(assessment_dict)
return assessments_list return assessments_list
\ No newline at end of file
def verify_rubric_format(rubric):
"""
Verifies that the rubric that was passed in follows the conventions that we expect, including
types and structure.
Args:
rubric (dict): Unsanitized version of our rubric. Usually taken from the GUI.
Returns:
rubric (dict): Sanitized version of the same form.
Raises:
UpdateFromXMLError
"""
if not isinstance(rubric, dict):
raise UpdateFromXmlError(_("The given rubric was not a dictionary of the form {criteria: [criteria1, criteria2...]}"))
if "criteria" not in rubric.keys():
raise UpdateFromXmlError(_("The given rubric did not contain a key for a list of criteria, and is invalid"))
if rubric.get('prompt', False):
if not isinstance(rubric['prompt'], basestring):
raise UpdateFromXmlError(_("The given rubric's feedback prompt was invalid, it must be a string."))
criteria = rubric["criteria"]
if not isinstance(criteria, list):
raise UpdateFromXmlError(_("The criteria term in the rubric dictionary corresponds to a non-list object."))
sanitized_criteria = []
for criterion in criteria:
if not isinstance(criterion, dict):
raise UpdateFromXmlError(_("A criterion given was not a dictionary."))
criterion = dict(criterion)
expected_keys = {'order_num', 'name', 'prompt', 'options', 'feedback'}
missing_keys = expected_keys - set(criterion.keys())
if missing_keys:
raise UpdateFromXmlError(_("The following keys were missing from the definition of one or more criteria: {}".format(", ".join(missing_keys))))
try:
name = unicode(criterion['name'])
except (TypeError, ValueError):
raise UpdateFromXmlError(_("The name value must be a string."))
try:
prompt = unicode(criterion['prompt'])
except (TypeError, ValueError):
raise UpdateFromXmlError(_("The prompt value must be a string."))
try:
feedback = unicode(criterion['feedback'])
except (TypeError, ValueError):
raise UpdateFromXmlError(_("The prompt value must be a string."))
try:
order_num = int(criterion['order_num'])
except (TypeError, ValueError):
raise UpdateFromXmlError(_("The order_num value must be an integer."))
if not isinstance(criterion['options'], list):
raise UpdateFromXmlError(_("The dictionary entry for 'options' in a criteria's dictionary definition must be a list."))
options = criterion['options']
sanitized_options = []
for option in options:
if not isinstance(option, dict):
raise UpdateFromXmlError(_("An option given was not a dictionary."))
expected_keys = {'order_num', 'name', 'points', 'explanation'}
unexpected_keys = list(set(option.keys()) - expected_keys)
missing_keys = list(expected_keys - set(option.keys()))
if missing_keys:
raise UpdateFromXmlError(_("The following keys were missing from the definition of one or more options: {}".format(", ".join(missing_keys))))
try:
option_name = unicode(option['name'])
except (TypeError, ValueError):
raise UpdateFromXmlError(_("All option names values must be strings."))
try:
option_explanation = unicode(option['explanation'])
except (TypeError, ValueError):
raise UpdateFromXmlError(_("All option explanation values must be strings."))
try:
option_points = int(option['points'])
except (TypeError, ValueError):
raise UpdateFromXmlError(_("All option point values must be integers."))
option_dict = {
"order_num": option['order_num'],
"name": option_name,
"explanation": option_explanation,
"points": option_points
}
sanitized_options.append(option_dict)
criterion_dict = {
"order_num": order_num,
"name": name,
"prompt": prompt,
"options": sanitized_options,
"feedback": feedback
}
sanitized_criteria.append(criterion_dict)
sanitized_rubric = {
'criteria': sanitized_criteria
}
if rubric.get('prompt'):
try:
sanitized_rubric['prompt'] = unicode(rubric.get('prompt'))
except (TypeError, ValueError):
raise UpdateFromXmlError(_("All prompt values must be strings."))
return sanitized_rubric
{ {
"simple": { "missing_feedback": {
"rubric": [ "rubric": {
"<rubric>", "prompt": "Test Prompt",
"<prompt>Test prompt</prompt>", "criteria": [
"<criterion>", {
"<name>Test criterion</name>", "order_num": 0,
"<prompt>Test criterion prompt</prompt>", "name": "Test criterion",
"<!-- no options -->", "prompt": "Test criterion prompt",
"</criterion>", "options": [
"</rubric>" {
], "order_num": 0,
"points": 0,
"name": "No",
"explanation": "No explanation"
}
]
}
]
},
"prompt": "My new prompt.", "prompt": "My new prompt.",
"submission_due": "4014-02-27T09:46:28", "submission_due": "4014-02-27T09:46:28",
"submission_start": "4014-02-10T09:46:28", "submission_start": "4014-02-10T09:46:28",
...@@ -32,4 +40,4 @@ ...@@ -32,4 +40,4 @@
"expected-assessment": "peer-assessment", "expected-assessment": "peer-assessment",
"expected-criterion-prompt": "Test criterion prompt" "expected-criterion-prompt": "Test criterion prompt"
} }
} }
\ No newline at end of file
...@@ -21,17 +21,31 @@ ...@@ -21,17 +21,31 @@
"expected_error": "error" "expected_error": "error"
}, },
"no_prompt": { "no_prompt": {
"rubric": [ "rubric": {
"<rubric>", "prompt": "Test Prompt",
"<prompt>Test prompt</prompt>", "criteria": [
"<criterion>", {
"<name>Test criterion</name>", "order_num": 0,
"<prompt>Test criterion prompt</prompt>", "name": "Test criterion",
"<option points=\"0\"><name>No</name><explanation>No explanation</explanation></option>", "prompt": "Test criterion prompt",
"<option points=\"2\"><name>Yes</name><explanation>Yes explanation</explanation></option>", "options": [
"</criterion>", {
"</rubric>" "order_num": 0,
], "points": 0,
"name": "No",
"explanation": "No explanation"
},
{
"order_num": 1,
"points": 2,
"name": "Yes",
"explanation": "Yes explanation"
}
],
"feedback": "optional"
}
]
},
"title": "My new title.", "title": "My new title.",
"assessments": [ "assessments": [
{ {
...@@ -52,17 +66,31 @@ ...@@ -52,17 +66,31 @@
"expected_error": "error" "expected_error": "error"
}, },
"no_submission_due": { "no_submission_due": {
"rubric": [ "rubric": {
"<rubric>", "prompt": "Test Prompt",
"<prompt>Test prompt</prompt>", "criteria": [
"<criterion>", {
"<name>Test criterion</name>", "order_num": 0,
"<prompt>Test criterion prompt</prompt>", "name": "Test criterion",
"<option points=\"0\"><name>No</name><explanation>No explanation</explanation></option>", "prompt": "Test criterion prompt",
"<option points=\"2\"><name>Yes</name><explanation>Yes explanation</explanation></option>", "options": [
"</criterion>", {
"</rubric>" "order_num": 0,
], "points": 0,
"name": "No",
"explanation": "No explanation"
},
{
"order_num": 1,
"points": 2,
"name": "Yes",
"explanation": "Yes explanation"
}
],
"feedback": "optional"
}
]
},
"prompt": "My new prompt.", "prompt": "My new prompt.",
"title": "My new title.", "title": "My new title.",
"assessments": [ "assessments": [
...@@ -83,17 +111,31 @@ ...@@ -83,17 +111,31 @@
"expected_error": "error" "expected_error": "error"
}, },
"invalid_dates_one": { "invalid_dates_one": {
"rubric": [ "rubric": {
"<rubric>", "prompt": "Test Prompt",
"<prompt>Test prompt</prompt>", "criteria": [
"<criterion>", {
"<name>Test criterion</name>", "order_num": 0,
"<prompt>Test criterion prompt</prompt>", "name": "Test criterion",
"<option points=\"0\"><name>No</name><explanation>No explanation</explanation></option>", "prompt": "Test criterion prompt",
"<option points=\"2\"><name>Yes</name><explanation>Yes explanation</explanation></option>", "options": [
"</criterion>", {
"</rubric>" "order_num": 0,
], "points": 0,
"name": "No",
"explanation": "No explanation"
},
{
"order_num": 1,
"points": 2,
"name": "Yes",
"explanation": "Yes explanation"
}
],
"feedback": "optional"
}
]
},
"prompt": "My new prompt.", "prompt": "My new prompt.",
"title": "My new title.", "title": "My new title.",
...@@ -111,23 +153,37 @@ ...@@ -111,23 +153,37 @@
"due": "" "due": ""
} }
], ],
"submission_due": "2012-02-27T09:46:28", "submission_due": "2012-02-27T09:46:28",
"submission_start": "2015-02-10T09:46:28", "submission_start": "2015-02-10T09:46:28",
"expected_error": "cannot be later" "expected_error": "cannot be later"
}, },
"invalid_dates_two": { "invalid_dates_two": {
"rubric": [ "rubric": {
"<rubric>", "prompt": "Test Prompt",
"<prompt>Test prompt</prompt>", "criteria": [
"<criterion>", {
"<name>Test criterion</name>", "order_num": 0,
"<prompt>Test criterion prompt</prompt>", "name": "Test criterion",
"<option points=\"0\"><name>No</name><explanation>No explanation</explanation></option>", "prompt": "Test criterion prompt",
"<option points=\"2\"><name>Yes</name><explanation>Yes explanation</explanation></option>", "options": [
"</criterion>", {
"</rubric>" "order_num": 0,
], "points": 0,
"name": "No",
"explanation": "No explanation"
},
{
"order_num": 1,
"points": 2,
"name": "Yes",
"explanation": "Yes explanation"
}
],
"feedback": "optional"
}
]
},
"prompt": "My new prompt.", "prompt": "My new prompt.",
"title": "My new title.", "title": "My new title.",
...@@ -145,9 +201,363 @@ ...@@ -145,9 +201,363 @@
"due": "2003-01-02T00:00:00" "due": "2003-01-02T00:00:00"
} }
], ],
"submission_due": "2012-02-27T09:46:28", "submission_due": "2012-02-27T09:46:28",
"submission_start": "", "submission_start": "",
"expected_error": "cannot be later" "expected_error": "cannot be later"
},
"order num is string": {
"rubric": {
"prompt": "Test Prompt",
"criteria": [
{
"order_num": "Hello",
"name": "Test criterion",
"prompt": "Test criterion prompt",
"options": [
{
"order_num": 0,
"points": 0,
"name": "No",
"explanation": "No explanation"
},
{
"order_num": 1,
"points": 2,
"name": "Yes",
"explanation": "Yes explanation"
}
],
"feedback": "optional"
}
]
},
"prompt": "My new prompt.",
"title": "My new title.",
"assessments": [
{
"name": "self-assessment",
"start": "",
"due": ""
}
],
"submission_due": "",
"submission_start": "",
"expected_error": "must be an integer"
},
"rubric not a dictionary": {
"rubric": [],
"prompt": "My new prompt.",
"title": "My new title.",
"assessments": [
{
"name": "self-assessment",
"start": "",
"due": ""
}
],
"submission_due": "",
"submission_start": "",
"expected_error": "dictionary of the form"
},
"feedback missing": {
"rubric": {
"prompt": "Test Prompt",
"criteria": [
{
"order_num": 0,
"name": "Test criterion",
"prompt": "Test criterion prompt",
"options": [
{
"order_num": 0,
"points": 0,
"name": "No",
"explanation": "No explanation"
},
{
"order_num": 1,
"points": 2,
"name": "Yes",
"explanation": "Yes explanation"
}
]
}
]
},
"prompt": "My new prompt.",
"title": "My new title.",
"assessments": [
{
"name": "self-assessment",
"start": "",
"due": ""
}
],
"submission_due": "",
"submission_start": "",
"expected_error": "feedback"
},
"expected rubric keys missing": {
"rubric": {
"prompt": "Test Prompt"
},
"prompt": "My new prompt.",
"title": "My new title.",
"assessments": [
{
"name": "self-assessment",
"start": "",
"due": ""
}
],
"submission_due": "",
"submission_start": "",
"expected_error": "did not contain a key"
},
"prompt not a string": {
"rubric": {
"prompt": 5,
"criteria": [
{
"order_num": 0,
"name": "Test criterion",
"prompt": "Test criterion prompt",
"options": [
{
"order_num": 0,
"points": 0,
"name": "No",
"explanation": "No explanation"
},
{
"order_num": 1,
"points": 2,
"name": "Yes",
"explanation": "Yes explanation"
}
],
"feedback": "optional"
}
]
},
"prompt": "My new prompt.",
"title": "My new title.",
"assessments": [
{
"name": "self-assessment",
"start": "",
"due": ""
}
],
"submission_due": "",
"submission_start": "",
"expected_error": "string"
},
"criterion not a dict": {
"rubric": {
"prompt": "prompty",
"criteria": [
[]
]
},
"prompt": "My new prompt.",
"title": "My new title.",
"assessments": [
{
"name": "self-assessment",
"start": "",
"due": ""
}
],
"submission_due": "",
"submission_start": "",
"expected_error": "not a dictionary"
},
"criteria missing keys": {
"rubric": {
"prompt": "Test Prompt",
"criteria": [
{
"order_num": 0,
"name": "Test criterion",
"options": [
{
"order_num": 0,
"points": 0,
"name": "No",
"explanation": "No explanation"
},
{
"order_num": 1,
"points": 2,
"name": "Yes",
"explanation": "Yes explanation"
}
],
"feedback": "optional"
}
]
},
"prompt": "My new prompt.",
"title": "My new title.",
"assessments": [
{
"name": "self-assessment",
"start": "",
"due": ""
}
],
"submission_due": "",
"submission_start": "",
"expected_error": "keys were missing"
},
"options not a list": {
"rubric": {
"prompt": "Test Prompt",
"criteria": [
{
"order_num": 0,
"name": "Test criterion",
"prompt": "Test criterion prompt",
"options": "not a list",
"feedback": "optional"
}
]
},
"prompt": "My new prompt.",
"title": "My new title.",
"assessments": [
{
"name": "self-assessment",
"start": "",
"due": ""
}
],
"submission_due": "",
"submission_start": "",
"expected_error": "must be a list"
},
"option not a dictionary": {
"rubric": {
"prompt": "Test Prompt",
"criteria": [
{
"order_num": 0,
"name": "Test criterion",
"prompt": "Test criterion prompt",
"options": [
{
"order_num": 0,
"points": 0,
"name": "No",
"explanation": "No explanation"
},
[]
],
"feedback": "optional"
}
]
},
"prompt": "My new prompt.",
"title": "My new title.",
"assessments": [
{
"name": "self-assessment",
"start": "",
"due": ""
}
],
"submission_due": "",
"submission_start": "",
"expected_error": "not a dictionary"
},
"option missing keys": {
"rubric": {
"prompt": "Test Prompt",
"criteria": [
{
"order_num": 0,
"name": "Test criterion",
"prompt": "Test criterion prompt",
"options": [
{
"points": 0,
"name": "No",
"explanation": "No explanation"
},
{
"order_num": 1,
"points": 2,
"name": "Yes",
"explanation": "Yes explanation"
}
],
"feedback": "optional"
}
]
},
"prompt": "My new prompt.",
"title": "My new title.",
"assessments": [
{
"name": "self-assessment",
"start": "",
"due": ""
}
],
"submission_due": "",
"submission_start": "",
"expected_error": "keys were missing"
},
"option points must be int": {
"rubric": {
"prompt": "Test Prompt",
"criteria": [
{
"order_num": 0,
"name": "Test criterion",
"prompt": "Test criterion prompt",
"options": [
{
"order_num": 0,
"points": "a million stanley nickels",
"name": "No",
"explanation": "No explanation"
},
{
"order_num": 1,
"points": "One Shrutebuck",
"name": "Yes",
"explanation": "Yes explanation"
}
],
"feedback": "optional"
}
]
},
"prompt": "My new prompt.",
"title": "My new title.",
"assessments": [
{
"name": "self-assessment",
"start": "",
"due": ""
}
],
"submission_due": "",
"submission_start": "",
"expected_error": "must be integers"
} }
} }
\ No newline at end of file
{ {
"simple": { "simple": {
"rubric": [ "rubric": {
"<rubric>", "prompt": "Test Prompt",
"<prompt>Test prompt</prompt>", "criteria": [
"<criterion>", {
"<name>Test criterion</name>", "order_num": 0,
"<prompt>Test criterion prompt</prompt>", "name": "Test criterion",
"<option points=\"0\"><name>No</name><explanation>No explanation</explanation></option>", "prompt": "Test criterion prompt",
"<option points=\"2\"><name>Yes</name><explanation>Yes explanation</explanation></option>", "options": [
"</criterion>", {
"</rubric>" "order_num": 0,
], "points": 0,
"name": "No",
"explanation": "No explanation"
},
{
"order_num": 1,
"points": 2,
"name": "Yes",
"explanation": "Yes explanation"
}
],
"feedback": "required"
}
]
},
"prompt": "My new prompt.", "prompt": "My new prompt.",
"submission_due": "4014-02-27T09:46:28", "submission_due": "4014-02-27T09:46:28",
"submission_start": "4014-02-10T09:46:28", "submission_start": "4014-02-10T09:46:28",
...@@ -32,5 +46,54 @@ ...@@ -32,5 +46,54 @@
"expected-assessment": "peer-assessment", "expected-assessment": "peer-assessment",
"expected-criterion-prompt": "Test criterion prompt" "expected-criterion-prompt": "Test criterion prompt"
},
"unicode": {
"rubric": {
"prompt": "Ṫëṡẗ ṗṛöṁṗẗ",
"criteria": [
{
"order_num": 0,
"name": "Ṫëṡẗ ċṛïẗëïṛöṅ",
"prompt": "Téśt ćŕítéíŕőń ṕŕőḿṕt",
"options": [
{
"order_num": 0,
"points": 0,
"name": "Ṅö",
"explanation": "Ńő éxṕĺáńátíőń"
},
{
"order_num": 1,
"points": 2,
"name": "sǝʎ",
"explanation": "Чэѕ эхрlаиатіои"
}
],
"feedback": "required"
}
]
},
"prompt": "Ṁÿ ṅëẅ ṗṛöṁṗẗ.",
"submission_due": "4014-02-27T09:46:28",
"submission_start": "4014-02-10T09:46:28",
"title": "ɯʎ uǝʍ ʇıʇןǝ",
"assessments": [
{
"name": "peer-assessment",
"must_grade": 5,
"must_be_graded_by": 3,
"start": "",
"due": "4014-03-10T00:00:00"
},
{
"name": "self-assessment",
"start": "",
"due": ""
}
],
"expected-assessment": "peer-assessment",
"expected-criterion-prompt": "Ṫëṡẗ ċṛïẗëṛïöṅ ṗṛöṁṗẗ"
} }
} }
\ No newline at end of file
...@@ -5,10 +5,8 @@ View-level tests for Studio view of OpenAssessment XBlock. ...@@ -5,10 +5,8 @@ View-level tests for Studio view of OpenAssessment XBlock.
import json import json
import datetime as dt import datetime as dt
import lxml.etree as etree import lxml.etree as etree
import mock
import pytz import pytz
from ddt import ddt, data, file_data from ddt import ddt, file_data
from openassessment.xblock.xml import UpdateFromXmlError
from .base import scenario, XBlockHandlerTestCase from .base import scenario, XBlockHandlerTestCase
...@@ -29,9 +27,9 @@ class StudioViewTest(XBlockHandlerTestCase): ...@@ -29,9 +27,9 @@ class StudioViewTest(XBlockHandlerTestCase):
self.assertTrue(resp['success']) self.assertTrue(resp['success'])
self.assertEqual(resp['msg'], u'') self.assertEqual(resp['msg'], u'')
# Verify that the Rubric XML is parse-able and the root is <rubric> # Verify that the Rubric has criteria, and that they are a list of dictionaries
rubric = etree.fromstring(resp['rubric']) self.assertTrue(isinstance(resp['rubric']['criteria'], list))
self.assertEqual(rubric.tag, 'rubric') self.assertTrue(isinstance(resp['rubric']['criteria'][0], dict))
# Verify that every assessment in the list of assessments has a name. # Verify that every assessment in the list of assessments has a name.
for assessment_dict in resp['assessments']: for assessment_dict in resp['assessments']:
...@@ -40,63 +38,17 @@ class StudioViewTest(XBlockHandlerTestCase): ...@@ -40,63 +38,17 @@ class StudioViewTest(XBlockHandlerTestCase):
examples = etree.fromstring(assessment_dict['examples']) examples = etree.fromstring(assessment_dict['examples'])
self.assertEqual(examples.tag, 'examples') self.assertEqual(examples.tag, 'examples')
@mock.patch('openassessment.xblock.xml.serialize_rubric_to_xml_str')
@scenario('data/basic_scenario.xml')
def test_get_editor_context_error(self, xblock, mock_rubric_serializer):
# Simulate an unexpected error while serializing the XBlock
mock_rubric_serializer.side_effect = UpdateFromXmlError('Test error!')
# Check that we get a failure message
resp = self.request(xblock, 'editor_context', '""', response_format='json')
self.assertFalse(resp['success'])
self.assertIn(u'unexpected error', resp['msg'].lower())
@file_data('data/update_xblock.json') @file_data('data/update_xblock.json')
@scenario('data/basic_scenario.xml') @scenario('data/basic_scenario.xml')
def test_update_xblock(self, xblock, data): def test_update_context(self, xblock, data):
# First, parse XML data into a single string.
data['rubric'] = "".join(data['rubric'])
xblock.published_date = None xblock.published_date = None
# Test that we can update the xblock with the expected configuration. resp = self.request(xblock, 'update_editor_context', json.dumps(data), response_format='json')
request = json.dumps(data) self.assertTrue(resp['success'], msg=resp.get('msg'))
# Verify the response is successfully
resp = self.request(xblock, 'update_editor_context', request, response_format='json')
print "ERROR IS {}".format(resp['msg'])
self.assertTrue(resp['success'])
self.assertIn('success', resp['msg'].lower())
# Check that the XBlock fields were updated
# We don't need to be exhaustive here, because we have other unit tests
# that verify this extensively.
self.assertEqual(xblock.title, data['title'])
self.assertEqual(xblock.prompt, data['prompt'])
self.assertEqual(xblock.rubric_assessments[0]['name'], data['expected-assessment'])
self.assertEqual(xblock.rubric_criteria[0]['prompt'], data['expected-criterion-prompt'])
@file_data('data/update_xblock.json')
@scenario('data/basic_scenario.xml')
def test_update_context_post_release(self, xblock, data):
# First, parse XML data into a single string.
data['rubric'] = "".join(data['rubric'])
# XBlock start date defaults to already open,
# so we should get an error when trying to update anything that change the number of points
request = json.dumps(data)
# Verify the response is successfully
resp = self.request(xblock, 'update_editor_context', request, response_format='json')
self.assertFalse(resp['success'])
@file_data('data/invalid_update_xblock.json') @file_data('data/invalid_update_xblock.json')
@scenario('data/basic_scenario.xml') @scenario('data/basic_scenario.xml')
def test_update_context_invalid_request_data(self, xblock, data): def test_update_context_invalid_request_data(self, xblock, data):
# First, parse XML data into a single string.
if 'rubric' in data:
data['rubric'] = "".join(data['rubric'])
xblock.published_date = None xblock.published_date = None
resp = self.request(xblock, 'update_editor_context', json.dumps(data), response_format='json') resp = self.request(xblock, 'update_editor_context', json.dumps(data), response_format='json')
self.assertFalse(resp['success']) self.assertFalse(resp['success'])
self.assertIn(data['expected_error'], resp['msg'].lower()) self.assertIn(data['expected_error'], resp['msg'].lower())
...@@ -104,9 +56,6 @@ class StudioViewTest(XBlockHandlerTestCase): ...@@ -104,9 +56,6 @@ class StudioViewTest(XBlockHandlerTestCase):
@file_data('data/invalid_rubric.json') @file_data('data/invalid_rubric.json')
@scenario('data/basic_scenario.xml') @scenario('data/basic_scenario.xml')
def test_update_rubric_invalid(self, xblock, data): def test_update_rubric_invalid(self, xblock, data):
# First, parse XML data into a single string.
data['rubric'] = "".join(data['rubric'])
request = json.dumps(data) request = json.dumps(data)
# Store old XBlock fields for later verification # Store old XBlock fields for later verification
...@@ -118,7 +67,7 @@ class StudioViewTest(XBlockHandlerTestCase): ...@@ -118,7 +67,7 @@ class StudioViewTest(XBlockHandlerTestCase):
# Verify the response fails # Verify the response fails
resp = self.request(xblock, 'update_editor_context', request, response_format='json') resp = self.request(xblock, 'update_editor_context', request, response_format='json')
self.assertFalse(resp['success']) self.assertFalse(resp['success'])
self.assertIn("not valid", resp['msg'].lower()) self.assertIn("the following keys were missing", resp['msg'].lower())
# Check that the XBlock fields were NOT updated # Check that the XBlock fields were NOT updated
# We don't need to be exhaustive here, because we have other unit tests # We don't need to be exhaustive here, because we have other unit tests
...@@ -128,7 +77,6 @@ class StudioViewTest(XBlockHandlerTestCase): ...@@ -128,7 +77,6 @@ class StudioViewTest(XBlockHandlerTestCase):
self.assertItemsEqual(xblock.rubric_assessments, old_assessments) self.assertItemsEqual(xblock.rubric_assessments, old_assessments)
self.assertItemsEqual(xblock.rubric_criteria, old_criteria) self.assertItemsEqual(xblock.rubric_criteria, old_criteria)
@scenario('data/basic_scenario.xml') @scenario('data/basic_scenario.xml')
def test_check_released(self, xblock): def test_check_released(self, xblock):
# By default, the problem should be released # By default, the problem should be released
......
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