Commit 7d599598 by Andy Armstrong

Complete ORA staff regrading UI

TNL-3467
parent 352ff90e
...@@ -6,11 +6,11 @@ ...@@ -6,11 +6,11 @@
{% for criterion in rubric_criteria %} {% for criterion in rubric_criteria %}
<li <li
class="field field--radio is--required assessment__rubric__question ui-toggle-visibility {% if criterion.options %}has--options{% endif %}" class="field field--radio is--required assessment__rubric__question ui-toggle-visibility {% if criterion.options %}has--options{% endif %}"
id="assessment__rubric__question--{{ criterion.order_num }}" id="{{ rubric_type }}__assessment__rubric__question--{{ criterion.order_num }}"
> >
<h4 class="question__title ui-toggle-visibility__control"> <h4 class="question__title ui-toggle-visibility__control">
<i class="icon fa fa-caret-right" aria-hidden="true"></i> <i class="icon fa fa-caret-right" aria-hidden="true"></i>
<span id="assessment__rubric__prompt--{{ criterion.order_num }}" class="ui-toggle-visibility__control__copy question__title__copy">{{ criterion.prompt }}</span> <span id="{{ rubric_type }}__assessment__rubric__prompt--{{ criterion.order_num }}" class="ui-toggle-visibility__control__copy question__title__copy">{{ criterion.prompt }}</span>
<span class="label--required sr">* ({% trans "Required" %})</span> <span class="label--required sr">* ({% trans "Required" %})</span>
</h4> </h4>
...@@ -21,11 +21,11 @@ ...@@ -21,11 +21,11 @@
<div class="wrapper--input"> <div class="wrapper--input">
<input type="radio" <input type="radio"
name="{{ criterion.name }}" name="{{ criterion.name }}"
id="assessment__rubric__question--{{ criterion.order_num }}__{{ option.order_num }}" id="{{ rubric_type }}__assessment__rubric__question--{{ criterion.order_num }}__{{ option.order_num }}"
class="answer__value" class="answer__value"
value="{{ option.name }}" value="{{ option.name }}"
aria-labelledby="assessment__rubric__prompt--{{ criterion.order_num }}"/> aria-labelledby="{{ rubric_type }}__assessment__rubric__prompt--{{ criterion.order_num }}"/>
<label for="assessment__rubric__question--{{ criterion.order_num }}__{{ option.order_num }}" <label for="{{ rubric_type }}__assessment__rubric__question--{{ criterion.order_num }}__{{ option.order_num }}"
class="answer__label" class="answer__label"
>{{ option.label }}</label> >{{ option.label }}</label>
</div> </div>
...@@ -39,9 +39,9 @@ ...@@ -39,9 +39,9 @@
{% if criterion.feedback == 'optional' or criterion.feedback == 'required' %} {% if criterion.feedback == 'optional' or criterion.feedback == 'required' %}
<li class="answer--feedback"> <li class="answer--feedback">
<div class="wrapper--input"> <div class="wrapper--input">
<label for="assessment__rubric__question--{{ criterion.order_num }}__feedback" class="answer__label">{% trans "Comments" %}</label> <label for="{{ rubric_type }}__assessment__rubric__question--{{ criterion.order_num }}__feedback" class="answer__label">{% trans "Comments" %}</label>
<textarea <textarea
id="assessment__rubric__question--{{ criterion.order_num }}__feedback" id="{{ rubric_type }}__assessment__rubric__question--{{ criterion.order_num }}__feedback"
class="answer__value" class="answer__value"
value="{{ criterion.name }}" value="{{ criterion.name }}"
name="{{ criterion.name }}" name="{{ criterion.name }}"
...@@ -56,15 +56,16 @@ ...@@ -56,15 +56,16 @@
</div> </div>
</li> </li>
{% endfor %} {% endfor %}
<li class="wrapper--input field field--textarea assessment__rubric__question assessment__rubric__question--feedback" id="assessment__rubric__question--feedback"> <li class="wrapper--input field field--textarea assessment__rubric__question assessment__rubric__question--feedback">
<label class="question__title" for="assessment__rubric__question--feedback__value"> <label class="question__title" for="{{ rubric_type }}__assessment__rubric__question--feedback__value">
<span class="question__title__copy">{{ rubric_feedback_prompt }}</span> <span class="question__title__copy">{% trans rubric_feedback_prompt %}</span>
</label> </label>
<div class="wrapper--input"> <div class="wrapper--input">
<textarea <textarea
id="assessment__rubric__question--feedback__value" id="{{ rubric_type }}__assessment__rubric__question--feedback__value"
placeholder="{{ rubric_feedback_default_text }}" class="assessment__rubric__question--feedback__value"
placeholder="{% trans rubric_feedback_default_text %}"
maxlength="500" maxlength="500"
> >
</textarea> </textarea>
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
{% trans "View the file associated with this submission." %} {% trans "View the file associated with this submission." %}
</a> </a>
{% if show_warning %} {% if show_warning %}
<p class="submission_file_warning">{% trans "(Caution: This file was uploaded by another course learner and has not been verified, screened, approved, reviewed or endorsed by edX. If you decide to access it, you do so at your own risk.)" %}</p> <p class="submission_file_warning">{% trans "(Caution: This file was uploaded by another course learner and has not been verified, screened, approved, reviewed, or endorsed by edX. If you decide to access it, you do so at your own risk.)" %}</p>
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>
......
...@@ -72,7 +72,7 @@ ...@@ -72,7 +72,7 @@
</div> </div>
<form id="peer-assessment--001__assessment" class="peer-assessment__assessment" method="post"> <form id="peer-assessment--001__assessment" class="peer-assessment__assessment" method="post">
{% include "openassessmentblock/oa_rubric.html" %} {% include "openassessmentblock/oa_rubric.html" with rubric_type="peer" %}
</form> </form>
</article> </article>
</li> </li>
......
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
{% for criterion in rubric_criteria %} {% for criterion in rubric_criteria %}
<li <li
class="field field--radio is--required assessment__rubric__question ui-toggle-visibility {% if criterion.options %}has--options{% endif %}" class="field field--radio is--required assessment__rubric__question ui-toggle-visibility {% if criterion.options %}has--options{% endif %}"
id="assessment__rubric__question--{{ criterion.order_num }}" id="peer__assessment__rubric__question--{{ criterion.order_num }}"
> >
<h4 class="question__title ui-toggle-visibility__control"> <h4 class="question__title ui-toggle-visibility__control">
<i class="icon fa fa-caret-right" aria-hidden="true"></i> <i class="icon fa fa-caret-right" aria-hidden="true"></i>
...@@ -77,10 +77,10 @@ ...@@ -77,10 +77,10 @@
<div class="wrapper--input"> <div class="wrapper--input">
<input type="radio" <input type="radio"
name="{{ criterion.name }}" name="{{ criterion.name }}"
id="assessment__rubric__question--{{ criterion.order_num }}__{{ option.order_num }}" id="peer__assessment__rubric__question--{{ criterion.order_num }}__{{ option.order_num }}"
class="answer__value" class="answer__value"
value="{{ option.name }}" /> value="{{ option.name }}" />
<label for="assessment__rubric__question--{{ criterion.order_num }}__{{ option.order_num }}" <label for="peer__assessment__rubric__question--{{ criterion.order_num }}__{{ option.order_num }}"
class="answer__label" class="answer__label"
>{{ option.label }}</label> >{{ option.label }}</label>
</div> </div>
...@@ -94,9 +94,9 @@ ...@@ -94,9 +94,9 @@
{% if criterion.feedback == 'optional' or criterion.feedback == 'required' %} {% if criterion.feedback == 'optional' or criterion.feedback == 'required' %}
<li class="answer--feedback"> <li class="answer--feedback">
<div class="wrapper--input"> <div class="wrapper--input">
<label for="assessment__rubric__question--{{ criterion.order_num }}__feedback" class="answer__label">{% trans "Comments" %}</label> <label for="peer__assessment__rubric__question--{{ criterion.order_num }}__feedback" class="answer__label">{% trans "Comments" %}</label>
<textarea <textarea
id="assessment__rubric__question--{{ criterion.order_num }}__feedback" id="peer__assessment__rubric__question--{{ criterion.order_num }}__feedback"
class="answer__value" class="answer__value"
value="{{ criterion.name }}" value="{{ criterion.name }}"
name="{{ criterion.name }}" name="{{ criterion.name }}"
...@@ -112,13 +112,13 @@ ...@@ -112,13 +112,13 @@
</li> </li>
{% endfor %} {% endfor %}
<li class="wrapper--input field field--textarea assessment__rubric__question assessment__rubric__question--feedback" id="assessment__rubric__question--feedback"> <li class="wrapper--input field field--textarea assessment__rubric__question assessment__rubric__question--feedback">
<label class="question__title" for="assessment__rubric__question--feedback__value"> <label class="question__title" for="peer__assessment__rubric__question--feedback__value">
<span class="question__title__copy">{{ rubric_feedback_prompt }}</span> <span class="question__title__copy">{{ rubric_feedback_prompt }}</span>
</label> </label>
<div class="wrapper--input"> <div class="wrapper--input">
<textarea <textarea
id="assessment__rubric__question--feedback__value" id="peer__assessment__rubric__question--feedback__value"
placeholder="{{ rubric_feedback_default_text }}" placeholder="{{ rubric_feedback_default_text }}"
maxlength="500" maxlength="500"
> >
......
...@@ -3,16 +3,16 @@ ...@@ -3,16 +3,16 @@
{% load tz %} {% load tz %}
{% block list_item %} {% block list_item %}
<li id="openassessment__response" class="openassessment__steps__step step--response ui-toggle-visibility has--error"> <li id="openassessment__response" class="openassessment__steps__step step--response ui-toggle-visibility has--error">
{% endblock %} {% endblock %}
{% block title %} {% block title %}
<span class="step__status"> <span class="step__status">
<span class="step__status__label">{% trans "This step's status" %}:</span> <span class="step__status__label">{% trans "This step's status" %}:</span>
<span class="step__status__value"> <span class="step__status__value">
<i class="icon fa fa-exclamation-triangle" aria-hidden="true"></i> <i class="icon fa fa-exclamation-triangle" aria-hidden="true"></i>
<span class="copy">{% trans "Cancelled" %}</span> <span class="copy">{% trans "Cancelled" %}</span>
</span> </span>
</span> </span>
{% endblock %} {% endblock %}
...@@ -24,26 +24,17 @@ ...@@ -24,26 +24,17 @@
<h3 class="message__title">{% trans "Submission Cancelled" %}</h3> <h3 class="message__title">{% trans "Submission Cancelled" %}</h3>
<div class="message__content"> <div class="message__content">
<p> <p>
{% blocktrans with removed_datetime=workflow_cancellation.created_at|utc|date:"N j, Y H:i e" removed_by_username=workflow_cancellation.cancelled_by %} {% blocktrans with removed_datetime=workflow_cancellation.cancelled_at|utc|date:"N j, Y H:i e" removed_by_username=workflow_cancellation.cancelled_by %}
Your submission has been cancelled by {{ removed_by_username }} on {{ removed_datetime }} Your submission has been cancelled by {{ removed_by_username }} on {{ removed_datetime }}
{% endblocktrans %} {% endblocktrans %}
<br> </p>
<!-- Comments: Reason for Cancellation--> <p>
{% blocktrans with comments=workflow_cancellation.comments %} <!-- Comments: Reason for Cancellation-->
Comments: {{ comments }} {% blocktrans with comments=workflow_cancellation.comments %}
{% endblocktrans %} Comments: {{ comments }}
</p> {% endblocktrans %}
</div> </p>
<div class="step__content">
<article class="submission__answer__display">
<h3 class="submission__answer__display__title">{% trans "Your Response" %}</h3>
<div class="submission__answer__display__content">
{{ student_submission.answer.text|linebreaks }}
</div>
</article>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
</article> </article>
<form id="self-assessment--001__assessment" class="self-assessment__assessment" method="post"> <form id="self-assessment--001__assessment" class="self-assessment__assessment" method="post">
{% include "openassessmentblock/oa_rubric.html" %} {% include "openassessmentblock/oa_rubric.html" with rubric_type="self" %}
</form> </form>
</div> </div>
......
{% load tz %}
{% load i18n %} {% load i18n %}
{% spaceless %} {% spaceless %}
...@@ -6,7 +5,7 @@ ...@@ -6,7 +5,7 @@
<div class="ui-toggle-visibility__content"> <div class="ui-toggle-visibility__content">
<div class="wrapper--staff-assessment"> <div class="wrapper--staff-assessment">
<div class="step__instruction"> <div class="step__instruction">
<p>{% trans "Allows you to override the current learner's grade using the problem's rubric." %}</p> <p>{% trans "Override this learner's current grade using the problem's rubric." %}</p>
</div> </div>
<div class="step__content"> <div class="step__content">
...@@ -26,7 +25,7 @@ ...@@ -26,7 +25,7 @@
</div> </div>
<form class="staff-assessment__assessment" method="post"> <form class="staff-assessment__assessment" method="post">
{% include "openassessmentblock/oa_rubric.html" with rubric_feedback_prompt="(Optional) What aspects of this response stood out to you? What did it do well? How could it improve?" rubric_feedback_default_text="I noticed that this response..." %} {% include "openassessmentblock/oa_rubric.html" with rubric_type="staff" rubric_feedback_prompt="(Optional) What aspects of this response stood out to you? What did it do well? How could it improve?" rubric_feedback_default_text="I noticed that this response..." %}
</form> </form>
</article> </article>
</div> </div>
...@@ -42,6 +41,8 @@ ...@@ -42,6 +41,8 @@
<button type="submit" class="action action--submit is--disabled"> <button type="submit" class="action action--submit is--disabled">
<span class="copy">{% trans "Submit your assessment" %}</span> <span class="copy">{% trans "Submit your assessment" %}</span>
</button> </button>
<div class="staff-override-error"></div>
</li> </li>
</ul> </ul>
</div> </div>
......
...@@ -33,19 +33,19 @@ ...@@ -33,19 +33,19 @@
</p> </p>
{% else %} {% else %}
{% include "openassessmentblock/oa_submission_answer.html" with answer=submission.answer answer_text_label="The learner's response to the question above:" %} {% include "openassessmentblock/oa_submission_answer.html" with answer=submission.answer answer_text_label="The learner's response to the question above:" %}
{% endif %}
{% if submission.file_url %} {% if submission.file_url %}
<a href="{{ submission.file_url }}" class="submission--file"> <a href="{{ submission.file_url }}" class="submission--file">
{% trans "The file associated with this response." %} {% trans "The file associated with this response." %}
</a> </a>
<span>{% trans "Caution: This file was uploaded by another course learner and has not been verified, screened, approved, reviewed, or endorsed by edX. If you decide to access it, you do so at your own risk." %}</span> <span>{% trans "Caution: This file was uploaded by another course learner and has not been verified, screened, approved, reviewed, or endorsed by edX. If you decide to access it, you do so at your own risk." %}</span>
{% endif %}
{% endif %} {% endif %}
</div> </div>
</div> </div>
</div> </div>
{% if peer_assessments != None %} {% if peer_assessments %}
<div class="staff-info__status ui-staff__content__section wrapper--ui--collapse"> <div class="staff-info__status ui-staff__content__section wrapper--ui--collapse">
<div class="ui-staff ui-toggle-visibility is--collapsed"> <div class="ui-staff ui-toggle-visibility is--collapsed">
<h2 class="staff-info__title ui-staff__subtitle ui-toggle-visibility__control"> <h2 class="staff-info__title ui-staff__subtitle ui-toggle-visibility__control">
...@@ -53,50 +53,52 @@ ...@@ -53,50 +53,52 @@
<span>{% trans "Peer Assessments for This Learner" %}</span> <span>{% trans "Peer Assessments for This Learner" %}</span>
</h2> </h2>
<div class="ui-toggle-visibility__content"> <div class="ui-toggle-visibility__content">
{% if peer_assessments %} {% for assessment in peer_assessments %}
{% for assessment in peer_assessments %} {% with peer_num=forloop.counter %}
{% with peer_num=forloop.counter %} <h4 class="title--sub">
<h4 class="title--sub"> {% trans "Peer" %} {{ peer_num }}: </h4> {% blocktrans %}
<table class="staff-info__status__table" summary="{% trans "Assessment" %}"> Peer {{ peer_num }}:
<thead> {% endblocktrans %}
<tr> </h4>
<th abbr="Criterion" scope="col">{% trans "Criterion" %}</th> <table class="staff-info__status__table" summary="{% trans "Assessment" %}">
<th abbr="Selected Option" scope="col">{% trans "Selected Option" %}</th> <thead>
<th abbr="Points" scope="col">{% trans "Points" %}</th> <tr>
<th abbr="Points Possible" scope="col">{% trans "Points Possible" %}</th> <th abbr="Criterion" scope="col">{% trans "Criterion" %}</th>
</tr> <th abbr="Selected Option" scope="col">{% trans "Selected Option" %}</th>
</thead> <th abbr="Feedback" scope="col">{% trans "Feedback" %}</th>
<th abbr="Points" scope="col">{% trans "Points" %}</th>
<th abbr="Points Possible" scope="col">{% trans "Points Possible" %}</th>
</tr>
</thead>
<tbody> <tbody>
{% for criterion in rubric_criteria %} {% for criterion in rubric_criteria %}
{% for part in self_assessment.parts %} {% for part in assessment.parts %}
{% if part.option.criterion.name == criterion.name %} {% if part.option.criterion.name == criterion.name %}
<tr> <tr>
<td class="label">{{ criterion.label }}</td> <td class="label">{{ criterion.label }}</td>
<td class="value">{{ part.option.label }}</td> <td class="value">{{ part.option.label }}</td>
<td class="value">{{ part.option.points }}</td> <td class="value">{{ part.feedback }}</td>
<td class="value">{{ criterion.total_value }}</td> <td class="value">{{ part.option.points }}</td>
</tr> <td class="value">{{ criterion.total_value }}</td>
{% endif %} </tr>
{% endfor %} {% endif %}
{% endfor %} {% endfor %}
</tbody> {% endfor %}
</table> </tbody>
<h4 class="title--sub">{% trans "Overall Feedback" %}</h4> </table>
<div class="student__answer__display__content"> <h4 class="title--sub">{% trans "Overall Feedback" %}</h4>
{{ assessment.feedback|linebreaks }} <div class="student__answer__display__content">
</div> {{ assessment.feedback|linebreaks }}
{% endwith %} </div>
{% endfor %} {% endwith %}
{% else %} {% endfor %}
<p>{% trans "This learner currently has no peer assessments." %}</p>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% if submitted_assessments != None %} {% if submitted_assessments %}
<div class="staff-info__status ui-staff__content__section wrapper--ui--collapse"> <div class="staff-info__status ui-staff__content__section wrapper--ui--collapse">
<div class="ui-staff ui-toggle-visibility is--collapsed"> <div class="ui-staff ui-toggle-visibility is--collapsed">
<h2 class="staff-info__title ui-staff__subtitle ui-toggle-visibility__control"> <h2 class="staff-info__title ui-staff__subtitle ui-toggle-visibility__control">
...@@ -104,46 +106,46 @@ ...@@ -104,46 +106,46 @@
<span>{% trans "Peer Assessments Completed by This Learner" %}</span> <span>{% trans "Peer Assessments Completed by This Learner" %}</span>
</h2> </h2>
<div class="ui-toggle-visibility__content"> <div class="ui-toggle-visibility__content">
{% if submitted_assessments %} {% for assessment in submitted_assessments %}
{% for assessment in submitted_assessments %} {% with peer_num=forloop.counter %}
{% with peer_num=forloop.counter %} <h4 class="title--sub">
<h4 class="title--sub">{% trans "Assessment" %} {{ peer_num }}:</h4> {% blocktrans %}
<table class="staff-info__status__table" summary="{% trans "Assessment" %}"> Assessment {{ peer_num }}:
<thead> {% endblocktrans %}
<tr> </h4>
<th abbr="Criterion" scope="col">{% trans "Criterion" %}</th> <table class="staff-info__status__table" summary="{% trans "Assessment" %}">
<th abbr="Selected Option" scope="col">{% trans "Selected Option" %}</th> <thead>
<th abbr="Feedback" scope="col">{% trans "Feedback" %}</th> <tr>
<th abbr="Points" scope="col">{% trans "Points" %}</th> <th abbr="Criterion" scope="col">{% trans "Criterion" %}</th>
<th abbr="Points Possible" scope="col">{% trans "Points Possible" %}</th> <th abbr="Selected Option" scope="col">{% trans "Selected Option" %}</th>
</tr> <th abbr="Feedback" scope="col">{% trans "Feedback" %}</th>
</thead> <th abbr="Points" scope="col">{% trans "Points" %}</th>
<th abbr="Points Possible" scope="col">{% trans "Points Possible" %}</th>
</tr>
</thead>
<tbody> <tbody>
{% for criterion in rubric_criteria %} {% for criterion in rubric_criteria %}
{% for part in assessment.parts %} {% for part in assessment.parts %}
{% if part.option.criterion.name == criterion.name %} {% if part.option.criterion.name == criterion.name %}
<tr> <tr>
<td class="label">{{ criterion.label }}</td> <td class="label">{{ criterion.label }}</td>
<td class="value">{{ part.option.label }}</td> <td class="value">{{ part.option.label }}</td>
<td class="value">{{ part.feedback }}</td> <td class="value">{{ part.feedback }}</td>
<td class="value">{{ part.option.points }}</td> <td class="value">{{ part.option.points }}</td>
<td class="value">{{ criterion.total_value }}</td> <td class="value">{{ criterion.total_value }}</td>
</tr> </tr>
{% endif %} {% endif %}
{% endfor %}
{% endfor %} {% endfor %}
</tbody> {% endfor %}
</table> </tbody>
<h4 class="title--sub">{% trans "Overall Feedback" %}</h4> </table>
<div class="student__answer__display__content"> <h4 class="title--sub">{% trans "Overall Feedback" %}</h4>
{{ assessment.feedback|linebreaks }} <div class="student__answer__display__content">
</div> {{ assessment.feedback|linebreaks }}
{% endwith %} </div>
{% endfor %} {% endwith %}
{% else %} {% endfor %}
<p>{% trans "This learner has not completed any peer assessments." %}</p>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
......
...@@ -83,7 +83,7 @@ ...@@ -83,7 +83,7 @@
{% if criterion.options %} {% if criterion.options %}
<li <li
class="field field--radio is--required assessment__rubric__question ui-toggle-visibility has--options" class="field field--radio is--required assessment__rubric__question ui-toggle-visibility has--options"
id="assessment__rubric__question--{{ criterion.order_num }}" id="training__assessment__rubric__question--{{ criterion.order_num }}"
> >
<h4 class="question__title ui-toggle-visibility__control"> <h4 class="question__title ui-toggle-visibility__control">
<i class="icon fa fa-caret-right" aria-hidden="true"></i> <i class="icon fa fa-caret-right" aria-hidden="true"></i>
...@@ -111,7 +111,7 @@ ...@@ -111,7 +111,7 @@
<div class="wrapper--input"> <div class="wrapper--input">
<input type="radio" <input type="radio"
name="{{ criterion.name }}" name="{{ criterion.name }}"
id="assessment__rubric__question--{{ criterion.order_num }}__{{ option.order_num }}" id="training__assessment__rubric__question--{{ criterion.order_num }}__{{ option.order_num }}"
class="answer__value" class="answer__value"
value="{{ option.name }}" /> value="{{ option.name }}" />
<label for="assessment__rubric__question--{{ criterion.order_num }}__{{ option.order_num }}" <label for="assessment__rubric__question--{{ criterion.order_num }}__{{ option.order_num }}"
......
...@@ -394,10 +394,10 @@ def cancel_workflow(submission_uuid, comments, cancelled_by_id, assessment_requi ...@@ -394,10 +394,10 @@ def cancel_workflow(submission_uuid, comments, cancelled_by_id, assessment_requi
def get_assessment_workflow_cancellation(submission_uuid): def get_assessment_workflow_cancellation(submission_uuid):
""" """
Get cancellation information for a assessment workflow. Get cancellation information for an assessment workflow.
Args: Args:
submission_uuid (str): The UUID of assessment workflow. submission_uuid (str): The UUID of the submission.
""" """
try: try:
workflow_cancellation = AssessmentWorkflowCancellation.get_latest_workflow_cancellation(submission_uuid) workflow_cancellation = AssessmentWorkflowCancellation.get_latest_workflow_cancellation(submission_uuid)
......
...@@ -378,7 +378,7 @@ class AssessmentWorkflow(TimeStampedModel, StatusModel): ...@@ -378,7 +378,7 @@ class AssessmentWorkflow(TimeStampedModel, StatusModel):
# A staff step must always be available, to allow for staff overrides # A staff step must always be available, to allow for staff overrides
try: try:
self.steps.get(name=self.STATUS.staff) self.steps.get(name=self.STATUS.staff)
except AssessmentWorkflowStep.DoesNotExist: except AttributeError:
for step in list(self.steps.all()): for step in list(self.steps.all()):
step.order_num += 1 step.order_num += 1
self.steps.add( self.steps.add(
......
...@@ -24,7 +24,6 @@ from openassessment.assessment.api import self as self_api ...@@ -24,7 +24,6 @@ from openassessment.assessment.api import self as self_api
from openassessment.assessment.api import ai as ai_api from openassessment.assessment.api import ai as ai_api
from openassessment.fileupload import api as file_api from openassessment.fileupload import api as file_api
from openassessment.workflow import api as workflow_api from openassessment.workflow import api as workflow_api
from openassessment.workflow.models import AssessmentWorkflowCancellation
from openassessment.fileupload import exceptions as file_exceptions from openassessment.fileupload import exceptions as file_exceptions
...@@ -232,7 +231,8 @@ class StaffAreaMixin(object): ...@@ -232,7 +231,8 @@ class StaffAreaMixin(object):
Args: Args:
student_username (unicode): The username of the student to report. student_username (unicode): The username of the student to report.
expanded_view (str): An optional view to be shown initially expanded.
The default is None meaning that all views are shown collapsed.
""" """
submission_uuid = None submission_uuid = None
submission = None submission = None
...@@ -270,8 +270,8 @@ class StaffAreaMixin(object): ...@@ -270,8 +270,8 @@ class StaffAreaMixin(object):
example_based_assessment = None example_based_assessment = None
self_assessment = None self_assessment = None
peer_assessments = [] peer_assessments = None
submitted_assessments = [] submitted_assessments = None
if "peer-assessment" in assessment_steps: if "peer-assessment" in assessment_steps:
peer_assessments = peer_api.get_assessments(submission_uuid) peer_assessments = peer_api.get_assessments(submission_uuid)
...@@ -285,22 +285,13 @@ class StaffAreaMixin(object): ...@@ -285,22 +285,13 @@ class StaffAreaMixin(object):
workflow = self.get_workflow_info(submission_uuid=submission_uuid) workflow = self.get_workflow_info(submission_uuid=submission_uuid)
workflow_cancellation = workflow_api.get_assessment_workflow_cancellation(submission_uuid) workflow_cancellation = self.get_workflow_cancellation_info(submission_uuid)
if workflow_cancellation:
workflow_cancellation['cancelled_by'] = self.get_username(workflow_cancellation['cancelled_by_id'])
# Get the date that the workflow was cancelled to use in preference to the serialized date string
cancellation_model = AssessmentWorkflowCancellation.get_latest_workflow_cancellation(submission_uuid)
workflow_cancelled_at = cancellation_model.created_at
else:
workflow_cancelled_at = None
context = { context = {
'submission': create_submission_dict(submission, self.prompts) if submission else None, 'submission': create_submission_dict(submission, self.prompts) if submission else None,
'score': workflow.get('score'), 'score': workflow.get('score'),
'workflow_status': workflow.get('status'), 'workflow_status': workflow.get('status'),
'workflow_cancellation': workflow_cancellation, 'workflow_cancellation': workflow_cancellation,
'workflow_cancelled_at': workflow_cancelled_at,
'peer_assessments': peer_assessments, 'peer_assessments': peer_assessments,
'submitted_assessments': submitted_assessments, 'submitted_assessments': submitted_assessments,
'self_assessment': self_assessment, 'self_assessment': self_assessment,
......
...@@ -86,11 +86,13 @@ ...@@ -86,11 +86,13 @@
{ {
"template": "openassessmentblock/response/oa_response.html", "template": "openassessmentblock/response/oa_response.html",
"context": { "context": {
"saved_response": {"answer": "saved_response": {
{"parts": [ "answer": {
{ "text": "", "prompt": { "description": "Prompt 1" }}, "parts": [
{ "text": "", "prompt": { "description": "Prompt 2" }} { "text": "", "prompt": { "description": "Prompt 1" }},
]} { "text": "", "prompt": { "description": "Prompt 2" }}
]
}
}, },
"save_status": "This response has not been saved.", "save_status": "This response has not been saved.",
"submit_enabled": false, "submit_enabled": false,
...@@ -585,12 +587,12 @@ ...@@ -585,12 +587,12 @@
} }
], ],
"template": { "template": {
"answer": { "answer": {
"parts": [ "parts": [
{ "text": ""}, { "text": ""},
{ "text": ""} { "text": ""}
] ]
}, },
"criteria": [ "criteria": [
{ {
"name": "criterion_with_two_options", "name": "criterion_with_two_options",
...@@ -684,6 +686,103 @@ ...@@ -684,6 +686,103 @@
{ {
"template": "openassessmentblock/staff_area/oa_student_info.html", "template": "openassessmentblock/staff_area/oa_student_info.html",
"context": { "context": {
"rubric_criteria": [
{
"name": "vocabulary",
"prompt": "vocabulary",
"order_num": 0,
"feedback": "optional",
"options": [
{
"order_num": 0,
"points": 0,
"name": "Bad"
},
{
"order_num": 1,
"points": 1,
"name": "Good"
}
]
},
{
"name": "grammar",
"prompt": "grammar",
"order_num": 1,
"options": [
{
"order_num": 0,
"points": 0,
"name": "Bad"
},
{
"order_num": 1,
"points": 1,
"name": "Good"
}
]
},
{
"name": "feedback_only",
"prompt": "Feedback only, no options!",
"order_num": 2,
"feedback": "required",
"options": []
}
],
"peer_assessments": [
{
"submission_uuid": "52d2158a-c568-11e3-b9b9-28cfe9182465",
"points_earned": 5,
"points_possible": 6,
"rubric": {
"criteria": [
{
"name": "Criterion 1",
"prompt": "Prompt 1",
"order_num": 0,
"feedback": "optional",
"options": [
{
"order_num": 2,
"points": 2,
"name": "Good"
}
],
"points_possible": 2
},
{
"name": "Criterion 2",
"prompt": "Prompt 2",
"order_num": 1,
"options": [
{
"order_num": 1,
"points": 1,
"name": "Fair"
}
],
"points_possible": 2
},
{
"name": "Criterion 3",
"prompt": "Prompt 3",
"order_num": 2,
"feedback": "optional",
"options": [
{
"order_num": 2,
"points": 2,
"name": "Good"
}
],
"points_possible": 2
}
]
}
}
],
"submission": { "submission": {
"image_url": "/test-url", "image_url": "/test-url",
"answer":{ "answer":{
...@@ -695,6 +794,85 @@ ...@@ -695,6 +794,85 @@
"output": "oa_student_info.html" "output": "oa_student_info.html"
}, },
{ {
"template": "openassessmentblock/staff_area/oa_student_info.html",
"context": {
"submission": {
"image_url": "/test-url",
"answer": {
"text": "testing response text"
}
},
"workflow_cancellation": {
"cancelled_by": "staff",
"cancelled_at": "2015-10-01T04:53",
"comments": "Cancelled!"
}
},
"output": "oa_staff_cancelled_submission.html"
},
{
"template": "openassessmentblock/staff_area/oa_student_info.html",
"context": {
"rubric_criteria": [
{
"name": "vocabulary",
"prompt": "vocabulary",
"order_num": 0,
"feedback": "optional",
"options": [
{
"order_num": 0,
"points": 0,
"name": "Bad"
},
{
"order_num": 1,
"points": 1,
"name": "Good"
}
]
},
{
"name": "grammar",
"prompt": "grammar",
"order_num": 1,
"options": [
{
"order_num": 0,
"points": 0,
"name": "Bad"
},
{
"order_num": 1,
"points": 1,
"name": "Good"
}
]
},
{
"name": "feedback_only",
"prompt": "Feedback only, no options!",
"order_num": 2,
"feedback": "required",
"options": []
}
],
"submission": {
"image_url": "/test-url",
"answer": {
"text": "testing response text"
}
},
"score": {
"points_earned": 1,
"points_possible": 2
},
"workflow_status": "done",
"expanded_view": "final-grade"
},
"output": "oa_staff_graded_submission.html"
},
{
"template": "openassessmentblock/peer/oa_peer_assessment.html", "template": "openassessmentblock/peer/oa_peer_assessment.html",
"context": { "context": {
"rubric_criteria": [ "rubric_criteria": [
......
(function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,d=e.filter,g=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,j=i.bind,w=function(n){return n instanceof w?n:this instanceof w?(this._wrapped=n,void 0):new w(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=w),exports._=w):n._=w,w.VERSION="1.4.4";var A=w.each=w.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:d&&n.filter===d?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:g&&n.every===g?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2),e=w.isFunction(t);return w.map(n,function(n){return(e?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t,r){return w.isEmpty(t)?r?null:[]:w[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.findWhere=function(n,t){return w.where(n,t,!0)},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var k=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=k(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index<t.index?-1:1}),"value")};var F=function(n,t,r,e){var u={},i=k(t||w.identity);return A(n,function(t,a){var o=i.call(r,t,a,n);e(u,o,t)}),u};w.groupBy=function(n,t,r){return F(n,t,r,function(n,t,r){(w.has(n,t)?n[t]:n[t]=[]).push(r)})},w.countBy=function(n,t,r){return F(n,t,r,function(n,t){w.has(n,t)||(n[t]=0),n[t]++})},w.sortedIndex=function(n,t,r,e){r=null==r?w.identity:k(r);for(var u=r.call(e,t),i=0,a=n.length;a>i;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i},w.bind=function(n,t){if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));var r=o.call(arguments,2);return function(){return n.apply(t,r.concat(o.call(arguments)))}},w.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},w.bindAll=function(n){var t=o.call(arguments,1);return 0===t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var I=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=I(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&I(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return I(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),"function"!=typeof/./&&(w.isFunction=function(n){return"function"==typeof n}),w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return n===void 0},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var M={escape:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","/":"&#x2F;"}};M.unescape=w.invert(M.escape);var S={escape:RegExp("["+w.keys(M.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(M.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(S[n],function(t){return M[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),D.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=++N+"";return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var T=/(.)^/,q={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},B=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){var e;r=w.defaults({},r,w.templateSettings);var u=RegExp([(r.escape||T).source,(r.interpolate||T).source,(r.evaluate||T).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(B,function(n){return"\\"+q[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,w);var c=function(n){return e.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},w.chain=function(n){return w(n).chain()};var D=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],D.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return D.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this);
\ No newline at end of file
if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}if(typeof window.ngetgext==="undefined"){window.ngettext=function(singular_text,plural_text,n){if(n>1){return plural_text}else{return singular_text}}}if(typeof window.Logger==="undefined"){window.Logger={log:function(){}}}if(typeof window.MathJax==="undefined"){window.MathJax={Hub:{Typeset:function(){},Queue:function(){}}}}if(typeof OpenAssessment.Server==="undefined"||!OpenAssessment.Server){OpenAssessment.Server=function(runtime,element){this.runtime=runtime;this.element=element};var jsonContentType="application/json; charset=utf-8";OpenAssessment.Server.prototype={url:function(handler){return this.runtime.handlerUrl(this.element,handler)},render:function(component){var view=this;var url=this.url("render_"+component);return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(view,[data])}).fail(function(){defer.rejectWith(view,[gettext("This section could not be loaded.")])})}).promise()},renderLatex:function(element){element.filter(".allow--latex").each(function(){MathJax.Hub.Queue(["Typeset",MathJax.Hub,this])})},renderContinuedPeer:function(){var view=this;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(view,[data])}).fail(function(){defer.rejectWith(view,[gettext("This section could not be loaded.")])})}).promise()},studentInfo:function(student_username,options){var url=this.url("render_student_info");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:_.extend({student_username:student_username},options)}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){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}),contentType:jsonContentType}).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(){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}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){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,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This feedback could not be submitted.")])})}).promise()},submitAssessment:function(assessmentType,payload){var url=this.url(assessmentType);return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify(payload),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})}).promise()},peerAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID){return this.submitAssessment("peer_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID})},selfAssess:function(optionsSelected,criterionFeedback,overallFeedback){return this.submitAssessment("self_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback})},staffAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID){return this.submitAssessment("staff_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID})},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,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.corrections])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){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:'""',contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){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:'""',contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("One or more rescheduling tasks failed.")])})})},updateEditorContext:function(options){var url=this.url("update_editor_context");var payload=JSON.stringify({prompts:options.prompts,feedback_prompt:options.feedbackPrompt,feedback_default_text:options.feedback_default_text,title:options.title,submission_start:options.submissionStart,submission_due:options.submissionDue,criteria:options.criteria,assessments:options.assessments,editor_assessments_order:options.editorAssessmentsOrder,file_upload_type:options.fileUploadType,white_listed_file_types:options.fileTypeWhiteList,allow_latex:options.latexEnabled,leaderboard_show:options.leaderboardNum});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){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,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.is_released])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("The server could not be contacted.")])})}).promise()},getUploadUrl:function(contentType,filename){var url=this.url("upload_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({contentType:contentType,filename:filename}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve(data.url)}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve upload url.")])})}).promise()},getDownloadUrl:function(){var url=this.url("download_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve(data.url)}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve download url.")])})}).promise()},cancelSubmission:function(submissionID,comments){var url=this.url("cancel_submission");var payload=JSON.stringify({submission_uuid:submissionID,comments:comments});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("The submission could not be removed from the grading pool.")])})}).promise()}}}if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}if(typeof window.ngetgext==="undefined"){window.ngettext=function(singular_text,plural_text,n){if(n>1){return plural_text}else{return singular_text}}}if(typeof window.Logger==="undefined"){window.Logger={log:function(){}}}if(typeof window.MathJax==="undefined"){window.MathJax={Hub:{Typeset:function(){},Queue:function(){}}}}OpenAssessment.BaseView=function(runtime,element,server,data){this.runtime=runtime;this.element=element;this.server=server;this.fileUploader=new OpenAssessment.FileUploader;this.responseView=new OpenAssessment.ResponseView(this.element,this.server,this.fileUploader,this,data);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.leaderboardView=new OpenAssessment.LeaderboardView(this.element,this.server,this);this.messageView=new OpenAssessment.MessageView(this.element,this.server,this);this.staffAreaView=new OpenAssessment.StaffAreaView(this.element,this.server,this)};OpenAssessment.BaseView.prototype={scrollToTop:function(){if($.scrollTo instanceof Function){$(window).scrollTo($("#openassessment__steps",this.element),800,{offset:-50})}},setUpCollapseExpand:function(parentSel){parentSel.on("click",".ui-toggle-visibility__control",function(eventData){var sel=$(eventData.target).closest(".ui-toggle-visibility");sel.toggleClass("is--collapsed")})},load:function(){this.responseView.load();this.loadAssessmentModules();this.staffAreaView.load()},loadAssessmentModules:function(){this.trainingView.load();this.peerView.load();this.selfView.load();this.gradeView.load();this.leaderboardView.load()},loadMessageView:function(){this.messageView.load()},toggleActionError:function(type,message){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"}else if(type==="upload"){container="#upload__error"}if(container===null){if(message!==null){console.log(message)}}else{var msgHtml=message===null?"":message;$(container+" .message__content",element).html("<p>"+msgHtml+"</p>");$(container,element).toggleClass("has--error",message!==null)}},showLoadError:function(stepName,errorMessage){if(!errorMessage){errorMessage=gettext("Unable to load")}var $container=$("#openassessment__"+stepName);$container.toggleClass("has--error",true);$container.find(".step__status__value i").removeClass().addClass("icon fa fa-exclamation-triangle");$container.find(".step__status__value .copy").html(errorMessage)}};function OpenAssessmentBlock(runtime,element,data){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.BaseView(runtime,element,server,data);view.load()}OpenAssessment.FileUploader=function(){this.upload=function(url,file){return $.Deferred(function(defer){$.ajax({url:url,type:"PUT",data:file,async:false,processData:false,contentType:file.type}).done(function(){Logger.log("openassessment.upload_file",{fileName:file.name,fileSize:file.size,fileType:file.type});defer.resolve()}).fail(function(data,textStatus){defer.rejectWith(this,[textStatus])})}).promise()}};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.server.renderLatex($("#openassessment__grade",view.element));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){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.LeaderboardView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.LeaderboardView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("leaderboard").done(function(html){$("#openassessment__leaderboard",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__leaderboard",view.element))}).fail(function(errMsg){baseView.showLoadError("leaderboard",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);view.server.renderLatex($("#openassessment__message",view.element))}).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.server.renderLatex($("#openassessment__peer-assessment",view.element));view.installHandlers(false)}).fail(function(){view.baseView.showLoadError("peer-assessment")});view.baseView.loadMessageView()},loadContinuedAssessment:function(){var view=this;view.continueAssessmentEnabled(false);this.server.renderContinuedPeer().done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__peer-assessment",view.element));view.installHandlers(true)}).fail(function(){view.baseView.showLoadError("peer-assessment");view.continueAssessmentEnabled(true)})},continueAssessmentEnabled:function(enabled){var button=$("#peer-assessment__continue__grading",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled)}},installHandlers:function(isContinuedAssessment){var sel=$("#openassessment__peer-assessment",this.element);var view=this;this.baseView.setUpCollapseExpand(sel);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()}});sel.find("#peer-assessment__continue__grading").click(function(eventObject){eventObject.preventDefault();view.loadContinuedAssessment()})},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(){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;var uuid=$("#openassessment__peer-assessment").data("submission-uuid");view.baseView.toggleActionError("peer",null);view.peerSubmitEnabled(false);this.server.peerAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.rubric.overallFeedback(),uuid).done(successFunction).fail(function(errMsg){view.baseView.toggleActionError("peer",errMsg);view.peerSubmitEnabled(true)})}};OpenAssessment.ResponseView=function(element,server,fileUploader,baseView,data){this.element=element;this.server=server;this.fileUploader=fileUploader;this.baseView=baseView;this.savedResponse=[];this.files=null;this.fileType=null;this.lastChangeTime=Date.now();this.errorOnLastSave=false;this.autoSaveTimerId=null;this.data=data;this.fileUploaded=false};OpenAssessment.ResponseView.prototype={AUTO_SAVE_POLL_INTERVAL:2e3,AUTO_SAVE_WAIT:3e4,MAX_FILE_SIZE:5242880,load:function(){var view=this;this.server.render("submission").done(function(html){$("#openassessment__response",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__response",view.element));view.installHandlers();view.setAutoSaveEnabled(true)}).fail(function(){view.baseView.showLoadError("response")})},installHandlers:function(){var sel=$("#openassessment__response",this.element);var view=this;var uploadType="";if(sel.find(".submission__answer__display__file").length){uploadType=sel.find(".submission__answer__display__file").data("upload-type")}this.baseView.setUpCollapseExpand(sel);this.savedResponse=this.response();var handleChange=function(){view.handleResponseChanged()};sel.find(".submission__answer__part__text__value").on("change keyup drop paste",handleChange);var handlePrepareUpload=function(eventData){view.prepareUpload(eventData.target.files,uploadType)};sel.find("input[type=file]").on("change",handlePrepareUpload);sel.find("#submission__preview__item").hide();sel.find("#step--response__submit").click(function(eventObject){eventObject.preventDefault();view.submit()});sel.find("#submission__save").click(function(eventObject){eventObject.preventDefault();view.save()});sel.find("#submission__preview").click(function(eventObject){eventObject.preventDefault();var preview_text=sel.find(".submission__answer__part__text__value").val();var preview_container=sel.find("#preview_content");preview_container.html(preview_text.replace(/\r\n|\r|\n/g,"<br />"));sel.find("#submission__preview__item").show();MathJax.Hub.Queue(["Typeset",MathJax.Hub,preview_container[0]])});sel.find("#file__upload").click(function(eventObject){eventObject.preventDefault();$(".submission__answer__display__file",view.element).removeClass("is--hidden");view.fileUpload()})},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)}},previewEnabled:function(enabled){var sel=$("#submission__preview",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(texts){var sel=$(".submission__answer__part__text__value",this.element);if(typeof texts==="undefined"){return sel.map(function(){return $.trim($(this).val())}).get()}else{sel.map(function(index){$(this).val(texts[index])})}},responseChanged:function(){var savedResponse=this.savedResponse;return this.response().some(function(element,index){return element!==savedResponse[index]})},autoSave:function(){var timeSinceLastChange=Date.now()-this.lastChangeTime;if(this.responseChanged()&&timeSinceLastChange>this.AUTO_SAVE_WAIT&&!this.errorOnLastSave){this.save()}},handleResponseChanged:function(){var isNotBlank=!this.response().every(function(element){return $.trim(element)===""});this.submitEnabled(isNotBlank);if(this.responseChanged()){this.saveEnabled(isNotBlank);this.previewEnabled(isNotBlank);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();var currentResponseIsEmpty=currentResponse.every(function(element){return element===""});view.submitEnabled(!currentResponseIsEmpty);var currentResponseEqualsSaved=currentResponse.every(function(element,index){return element===savedResponse[index]});if(currentResponseEqualsSaved){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;var fileDefer=$.Deferred();if(view.files!==null&&!view.fileUploaded){var msg=gettext("Do you want to upload your file before submitting?");if(confirm(msg)){fileDefer=view.fileUpload()}else{view.submitEnabled(true);return}}else{fileDefer.resolve()}fileDefer.pipe(function(){return view.confirmSubmission().pipe(function(){var submission=view.response();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=gettext("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()}})},prepareUpload:function(files,uploadType){this.files=null;this.fileType=files[0].type;var ext=files[0].name.split(".").pop().toLowerCase();if(files[0].size>this.MAX_FILE_SIZE){this.baseView.toggleActionError("upload",gettext("File size must be 5MB or less."))}else if(uploadType==="image"&&this.data.ALLOWED_IMAGE_MIME_TYPES.indexOf(this.fileType)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+"JPG, PNG or GIF")}else if(uploadType==="pdf-and-image"&&this.data.ALLOWED_FILE_MIME_TYPES.indexOf(this.fileType)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+"JPG, PNG, GIF or PDF")}else if(uploadType==="custom"&&this.data.FILE_TYPE_WHITE_LIST.indexOf(ext)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+this.data.FILE_TYPE_WHITE_LIST.join(", "))}else if(this.data.FILE_EXT_BLACK_LIST.indexOf(ext)!==-1){this.baseView.toggleActionError("upload",gettext("File type is not allowed."))}else{this.baseView.toggleActionError("upload",null);this.files=files}$("#file__upload").toggleClass("is--disabled",this.files===null)},fileUpload:function(){var view=this;var fileUpload=$("#file__upload");fileUpload.addClass("is--disabled");var handleError=function(errMsg){view.baseView.toggleActionError("upload",errMsg);fileUpload.removeClass("is--disabled")};return this.server.getUploadUrl(view.fileType,view.files[0].name).done(function(url){var file=view.files[0];view.fileUploader.upload(url,file).done(function(){view.fileUrl();view.baseView.toggleActionError("upload",null);view.fileUploaded=true}).fail(handleError)}).fail(handleError)},fileUrl:function(){var view=this;var file=$("#submission__answer__file",view.element);view.server.getDownloadUrl().done(function(url){if(file.prop("tagName")==="IMG"){file.attr("src",url)}else{file.attr("href",url)}return url})}};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},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)}},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){var rubric=this;callback(rubric.canSubmit());$(this.element).on("change keyup drop paste",function(){callback(rubric.canSubmit())})},canSubmit:function(){var numChecked=$("input[type=radio]:checked",this.element).length;var numAvailable=$(".field--radio.assessment__rubric__question.has--options",this.element).length;var completedRequiredComments=true;$("textarea[required]",this.element).each(function(){var trimmedText=$.trim($(this).val());if(trimmedText===""){completedRequiredComments=false}});return numChecked===numAvailable&&completedRequiredComments},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.server.renderLatex($("#openassessment__self-assessment",view.element));view.installHandlers()}).fail(function(){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 OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}if(typeof window.ngetgext==="undefined"){window.ngettext=function(singular_text,plural_text,n){if(n>1){return plural_text}else{return singular_text}}}if(typeof window.Logger==="undefined"){window.Logger={log:function(){}}}if(typeof window.MathJax==="undefined"){window.MathJax={Hub:{Typeset:function(){},Queue:function(){}}}}if(typeof OpenAssessment.Server==="undefined"||!OpenAssessment.Server){OpenAssessment.Server=function(runtime,element){this.runtime=runtime;this.element=element};var jsonContentType="application/json; charset=utf-8";OpenAssessment.Server.prototype={url:function(handler){return this.runtime.handlerUrl(this.element,handler)},render:function(component){var view=this;var url=this.url("render_"+component);return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(view,[data])}).fail(function(){defer.rejectWith(view,[gettext("This section could not be loaded.")])})}).promise()},renderLatex:function(element){element.filter(".allow--latex").each(function(){MathJax.Hub.Queue(["Typeset",MathJax.Hub,this])})},renderContinuedPeer:function(){var view=this;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(view,[data])}).fail(function(){defer.rejectWith(view,[gettext("This section could not be loaded.")])})}).promise()},studentInfo:function(student_username,options){var url=this.url("render_student_info");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:_.extend({student_username:student_username},options)}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){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}),contentType:jsonContentType}).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(){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}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){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,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This feedback could not be submitted.")])})}).promise()},submitAssessment:function(assessmentType,payload){var url=this.url(assessmentType);return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify(payload),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})}).promise()},peerAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID){return this.submitAssessment("peer_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID})},selfAssess:function(optionsSelected,criterionFeedback,overallFeedback){return this.submitAssessment("self_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback})},staffAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID){return this.submitAssessment("staff_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID})},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,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.corrections])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){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:'""',contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){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:'""',contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("One or more rescheduling tasks failed.")])})})},updateEditorContext:function(options){var url=this.url("update_editor_context");var payload=JSON.stringify({prompts:options.prompts,feedback_prompt:options.feedbackPrompt,feedback_default_text:options.feedback_default_text,title:options.title,submission_start:options.submissionStart,submission_due:options.submissionDue,criteria:options.criteria,assessments:options.assessments,editor_assessments_order:options.editorAssessmentsOrder,file_upload_type:options.fileUploadType,white_listed_file_types:options.fileTypeWhiteList,allow_latex:options.latexEnabled,leaderboard_show:options.leaderboardNum});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){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,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.is_released])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("The server could not be contacted.")])})}).promise()},getUploadUrl:function(contentType,filename){var url=this.url("upload_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({contentType:contentType,filename:filename}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve(data.url)}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve upload url.")])})}).promise()},getDownloadUrl:function(){var url=this.url("download_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve(data.url)}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve download url.")])})}).promise()},cancelSubmission:function(submissionID,comments){var url=this.url("cancel_submission");var payload=JSON.stringify({submission_uuid:submissionID,comments:comments});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("The submission could not be removed from the grading pool.")])})}).promise()}}}if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}if(typeof window.ngetgext==="undefined"){window.ngettext=function(singular_text,plural_text,n){if(n>1){return plural_text}else{return singular_text}}}if(typeof window.Logger==="undefined"){window.Logger={log:function(){}}}if(typeof window.MathJax==="undefined"){window.MathJax={Hub:{Typeset:function(){},Queue:function(){}}}}OpenAssessment.BaseView=function(runtime,element,server,data){this.runtime=runtime;this.element=element;this.server=server;this.fileUploader=new OpenAssessment.FileUploader;this.responseView=new OpenAssessment.ResponseView(this.element,this.server,this.fileUploader,this,data);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.leaderboardView=new OpenAssessment.LeaderboardView(this.element,this.server,this);this.messageView=new OpenAssessment.MessageView(this.element,this.server,this);this.staffAreaView=new OpenAssessment.StaffAreaView(this.element,this.server,this)};OpenAssessment.BaseView.prototype={scrollToTop:function(){if($.scrollTo instanceof Function){$(window).scrollTo($("#openassessment__steps",this.element),800,{offset:-50})}},setUpCollapseExpand:function(parentSel){parentSel.on("click",".ui-toggle-visibility__control",function(eventData){var sel=$(eventData.target).closest(".ui-toggle-visibility");sel.toggleClass("is--collapsed")})},load:function(){this.responseView.load();this.loadAssessmentModules();this.staffAreaView.load()},loadAssessmentModules:function(){this.trainingView.load();this.peerView.load();this.selfView.load();this.gradeView.load();this.leaderboardView.load()},loadMessageView:function(){this.messageView.load()},toggleActionError:function(type,message){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"}else if(type==="upload"){container="#upload__error"}if(container===null){if(message!==null){console.log(message)}}else{$(container+" .message__content",element).html("<p>"+(message?_.escape(message):"")+"</p>");$(container,element).toggleClass("has--error",message!==null)}},showLoadError:function(stepName,errorMessage){if(!errorMessage){errorMessage=gettext("Unable to load")}var $container=$("#openassessment__"+stepName);$container.toggleClass("has--error",true);$container.find(".step__status__value i").removeClass().addClass("icon fa fa-exclamation-triangle");$container.find(".step__status__value .copy").html(_.escape(errorMessage))}};function OpenAssessmentBlock(runtime,element,data){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.BaseView(runtime,element,server,data);view.load()}OpenAssessment.FileUploader=function(){this.upload=function(url,file){return $.Deferred(function(defer){$.ajax({url:url,type:"PUT",data:file,async:false,processData:false,contentType:file.type}).done(function(){Logger.log("openassessment.upload_file",{fileName:file.name,fileSize:file.size,fileType:file.type});defer.resolve()}).fail(function(data,textStatus){defer.rejectWith(this,[textStatus])})}).promise()}};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.server.renderLatex($("#openassessment__grade",view.element));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){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.LeaderboardView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.LeaderboardView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("leaderboard").done(function(html){$("#openassessment__leaderboard",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__leaderboard",view.element))}).fail(function(errMsg){baseView.showLoadError("leaderboard",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);view.server.renderLatex($("#openassessment__message",view.element))}).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.server.renderLatex($("#openassessment__peer-assessment",view.element));view.installHandlers(false)}).fail(function(){view.baseView.showLoadError("peer-assessment")});view.baseView.loadMessageView()},loadContinuedAssessment:function(){var view=this;view.continueAssessmentEnabled(false);this.server.renderContinuedPeer().done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__peer-assessment",view.element));view.installHandlers(true)}).fail(function(){view.baseView.showLoadError("peer-assessment");view.continueAssessmentEnabled(true)})},continueAssessmentEnabled:function(enabled){var button=$("#peer-assessment__continue__grading",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled)}},installHandlers:function(isContinuedAssessment){var sel=$("#openassessment__peer-assessment",this.element);var view=this;this.baseView.setUpCollapseExpand(sel);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()}});sel.find("#peer-assessment__continue__grading").click(function(eventObject){eventObject.preventDefault();view.loadContinuedAssessment()})},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);return enabled}},peerAssess:function(){var view=this;var baseView=view.baseView;this.peerAssessRequest(function(){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;var uuid=$("#openassessment__peer-assessment").data("submission-uuid");view.baseView.toggleActionError("peer",null);view.peerSubmitEnabled(false);this.server.peerAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.rubric.overallFeedback(),uuid).done(successFunction).fail(function(errMsg){view.baseView.toggleActionError("peer",errMsg);view.peerSubmitEnabled(true)})}};OpenAssessment.ResponseView=function(element,server,fileUploader,baseView,data){this.element=element;this.server=server;this.fileUploader=fileUploader;this.baseView=baseView;this.savedResponse=[];this.files=null;this.fileType=null;this.lastChangeTime=Date.now();this.errorOnLastSave=false;this.autoSaveTimerId=null;this.data=data;this.fileUploaded=false};OpenAssessment.ResponseView.prototype={AUTO_SAVE_POLL_INTERVAL:2e3,AUTO_SAVE_WAIT:3e4,MAX_FILE_SIZE:5242880,load:function(){var view=this;this.server.render("submission").done(function(html){$("#openassessment__response",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__response",view.element));view.installHandlers();view.setAutoSaveEnabled(true)}).fail(function(){view.baseView.showLoadError("response")})},installHandlers:function(){var sel=$("#openassessment__response",this.element);var view=this;var uploadType="";if(sel.find(".submission__answer__display__file").length){uploadType=sel.find(".submission__answer__display__file").data("upload-type")}this.baseView.setUpCollapseExpand(sel);this.savedResponse=this.response();var handleChange=function(){view.handleResponseChanged()};sel.find(".submission__answer__part__text__value").on("change keyup drop paste",handleChange);var handlePrepareUpload=function(eventData){view.prepareUpload(eventData.target.files,uploadType)};sel.find("input[type=file]").on("change",handlePrepareUpload);sel.find("#submission__preview__item").hide();sel.find("#step--response__submit").click(function(eventObject){eventObject.preventDefault();view.submit()});sel.find("#submission__save").click(function(eventObject){eventObject.preventDefault();view.save()});sel.find("#submission__preview").click(function(eventObject){eventObject.preventDefault();var preview_text=sel.find(".submission__answer__part__text__value").val();var preview_container=sel.find("#preview_content");preview_container.html(preview_text.replace(/\r\n|\r|\n/g,"<br />"));sel.find("#submission__preview__item").show();MathJax.Hub.Queue(["Typeset",MathJax.Hub,preview_container[0]])});sel.find("#file__upload").click(function(eventObject){eventObject.preventDefault();$(".submission__answer__display__file",view.element).removeClass("is--hidden");view.fileUpload()})},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);return 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)}},previewEnabled:function(enabled){var sel=$("#submission__preview",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">'+_.escape(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(texts){var sel=$(".submission__answer__part__text__value",this.element);if(typeof texts==="undefined"){return sel.map(function(){return $.trim($(this).val())}).get()}else{sel.map(function(index){$(this).val(texts[index])})}},responseChanged:function(){var savedResponse=this.savedResponse;return this.response().some(function(element,index){return element!==savedResponse[index]})},autoSave:function(){var timeSinceLastChange=Date.now()-this.lastChangeTime;if(this.responseChanged()&&timeSinceLastChange>this.AUTO_SAVE_WAIT&&!this.errorOnLastSave){this.save()}},handleResponseChanged:function(){var isNotBlank=!this.response().every(function(element){return $.trim(element)===""});this.submitEnabled(isNotBlank);if(this.responseChanged()){this.saveEnabled(isNotBlank);this.previewEnabled(isNotBlank);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();var currentResponseIsEmpty=currentResponse.every(function(element){return element===""});view.submitEnabled(!currentResponseIsEmpty);var currentResponseEqualsSaved=currentResponse.every(function(element,index){return element===savedResponse[index]});if(currentResponseEqualsSaved){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;var fileDefer=$.Deferred();if(view.files!==null&&!view.fileUploaded){var msg=gettext("Do you want to upload your file before submitting?");if(confirm(msg)){fileDefer=view.fileUpload()}else{view.submitEnabled(true);return}}else{fileDefer.resolve()}fileDefer.pipe(function(){return view.confirmSubmission().pipe(function(){var submission=view.response();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=gettext("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()}})},prepareUpload:function(files,uploadType){this.files=null;this.fileType=files[0].type;var ext=files[0].name.split(".").pop().toLowerCase();if(files[0].size>this.MAX_FILE_SIZE){this.baseView.toggleActionError("upload",gettext("File size must be 5MB or less."))}else if(uploadType==="image"&&this.data.ALLOWED_IMAGE_MIME_TYPES.indexOf(this.fileType)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+"JPG, PNG or GIF")}else if(uploadType==="pdf-and-image"&&this.data.ALLOWED_FILE_MIME_TYPES.indexOf(this.fileType)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+"JPG, PNG, GIF or PDF")}else if(uploadType==="custom"&&this.data.FILE_TYPE_WHITE_LIST.indexOf(ext)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+this.data.FILE_TYPE_WHITE_LIST.join(", "))}else if(this.data.FILE_EXT_BLACK_LIST.indexOf(ext)!==-1){this.baseView.toggleActionError("upload",gettext("File type is not allowed."))}else{this.baseView.toggleActionError("upload",null);this.files=files}$("#file__upload").toggleClass("is--disabled",this.files===null)},fileUpload:function(){var view=this;var fileUpload=$("#file__upload");fileUpload.addClass("is--disabled");var handleError=function(errMsg){view.baseView.toggleActionError("upload",errMsg);fileUpload.removeClass("is--disabled")};return this.server.getUploadUrl(view.fileType,view.files[0].name).done(function(url){var file=view.files[0];view.fileUploader.upload(url,file).done(function(){view.fileUrl();view.baseView.toggleActionError("upload",null);view.fileUploaded=true}).fail(handleError)}).fail(handleError)},fileUrl:function(){var view=this;var file=$("#submission__answer__file",view.element);view.server.getDownloadUrl().done(function(url){if(file.prop("tagName")==="IMG"){file.attr("src",url)}else{file.attr("href",url)}return url})}};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},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)}},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){var rubric=this;callback(rubric.canSubmit());$(this.element).on("change keyup drop paste",function(){callback(rubric.canSubmit())})},canSubmit:function(){var numChecked=$("input[type=radio]:checked",this.element).length;var numAvailable=$(".field--radio.assessment__rubric__question.has--options",this.element).length;var completedRequiredComments=true;$("textarea[required]",this.element).each(function(){var trimmedText=$.trim($(this).val());if(trimmedText===""){completedRequiredComments=false}});return numChecked===numAvailable&&completedRequiredComments},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.server.renderLatex($("#openassessment__self-assessment",view.element));view.installHandlers()}).fail(function(){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()
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);this.server.selfAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.rubric.overallFeedback()).done(function(){baseView.loadAssessmentModules();baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("self",errMsg);view.selfSubmitEnabled(true)})}};!function(OpenAssessment){"use strict";OpenAssessment.StaffAreaView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.StaffAreaView.prototype={load:function(){var view=this;if($(".openassessment__staff-area",view.element).length>0){this.server.render("staff_area").done(function(html){$(".openassessment__staff-area",view.element).replaceWith(html);view.server.renderLatex($(".openassessment__staff-area",view.element));view.installHandlers()}).fail(function(){view.baseView.showLoadError("staff_area")})}},loadStudentInfo:function(options){var view=this;var $staffTools=$(".openassessment__staff-tools",this.element);var $form=$staffTools.find(".openassessment_student_info_form");var student_username=$staffTools.find(".openassessment__student_username").val();var showFormError=function(errorMessage){$form.find(".form--error").text(errorMessage)};var deferred=$.Deferred();$(".openassessment__student-info",view.element).text("");if(student_username.trim()){this.server.studentInfo(student_username,options).done(function(html){showFormError("");$(".openassessment__student-info",view.element).replaceWith(html);$staffTools.on("click",".action--submit-cancel-submission",function(eventObject){eventObject.preventDefault();view.cancelSubmission($(this).data("submission-uuid"))});var handleChange=function(eventData){view.handleCommentChanged(eventData)};$staffTools.find(".cancel_submission_comments").on("change keyup drop paste",handleChange);var $rubric=$(".staff-assessment__assessment",view.element);if($rubric.size()>0){var rubricElement=$rubric.get(0);var rubric=new OpenAssessment.Rubric(rubricElement);rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled,view));$(".wrapper--staff-assessment .action--submit",view.element).click(function(eventObject){var target=$(eventObject.currentTarget),rootElement=target.closest(".openassessment__student-info"),submissionID=rootElement.data("submission-uuid");eventObject.preventDefault();view.submitStaffAssessment(submissionID,rubric)})}deferred.resolve()}).fail(function(){showFormError(gettext("Unexpected server error."));deferred.reject()})}else{showFormError(gettext("A learner name must be provided."));deferred.reject()}return deferred.promise()},installHandlers:function(){var view=this;var $staffArea=$(".openassessment__staff-area",this.element);var $staffTools=$(".openassessment__staff-tools",$staffArea);var $staffInfo=$(".openassessment__student-info",$staffArea);if($staffArea.length<=0){return}this.baseView.setUpCollapseExpand($staffTools,function(){});this.baseView.setUpCollapseExpand($staffInfo,function(){});$staffArea.find(".ui-staff__button").click(function(eventObject){var $button=$(eventObject.currentTarget),panelClass=$button.data("panel"),$panel=$staffArea.find("."+panelClass).first();if($button.hasClass("is--active")){$button.removeClass("is--active");$panel.addClass("is--hidden")}else{$staffArea.find(".ui-staff__button").removeClass("is--active");$button.addClass("is--active");$staffArea.find(".wrapper--ui-staff").addClass("is--hidden");$panel.removeClass("is--hidden")}});$staffArea.find(".ui-staff_close_button").click(function(eventObject){var $button=$(eventObject.currentTarget),$panel=$button.closest(".wrapper--ui-staff");$staffArea.find(".ui-staff__button").removeClass("is--active");$panel.addClass("is--hidden")});$staffTools.find(".openassessment_student_info_form").submit(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$staffTools.find(".action--submit-username").click(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$staffTools.find(".action--submit-training").click(function(eventObject){eventObject.preventDefault();view.scheduleTraining()});$staffTools.find(".action--submit-unfinished-tasks").click(function(eventObject){eventObject.preventDefault();view.rescheduleUnfinishedTasks()})},scheduleTraining:function(){var view=this;this.server.scheduleTraining().done(function(msg){$(".schedule_training_message",view.element).text(msg)}).fail(function(errMsg){$(".schedule_training_message",view.element).text(errMsg)})},rescheduleUnfinishedTasks:function(){var view=this;this.server.rescheduleUnfinishedTasks().done(function(msg){$(".reschedule_unfinished_tasks_message",view.element).text(msg)}).fail(function(errMsg){$(".reschedule_unfinished_tasks_message",view.element).text(errMsg)})},cancelSubmission:function(submissionUUID){this.cancelSubmissionEnabled(false);var view=this;var comments=$(".cancel_submission_comments",this.element).val();this.server.cancelSubmission(submissionUUID,comments).done(function(msg){$(".cancel-submission-error").html("");view.loadStudentInfo({expanded_view:"final-grade"}).done(function(){$(".openassessment__staff-info__cancel__submission",view.element).html(msg)})}).fail(function(errMsg){$(".cancel-submission-error").html(errMsg)})},cancelSubmissionEnabled:function(enabled){var $cancelButton=$(".action--submit-cancel-submission",this.element);if(typeof enabled==="undefined"){return!$cancelButton.hasClass("is--disabled")}else{$cancelButton.toggleClass("is--disabled",!enabled)}},comment:function(text){var $submissionComments=$(".cancel_submission_comments",this.element);if(typeof text==="undefined"){return $submissionComments.val()}else{$submissionComments.val(text)}},handleCommentChanged:function(){var isBlank=$.trim(this.comment())!=="";this.cancelSubmissionEnabled(isBlank)},staffSubmitEnabled:function(enabled){var button=$(".wrapper--staff-assessment .action--submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled)}},submitStaffAssessment:function(submissionID,rubric){var view=this;var baseView=this.baseView;baseView.toggleActionError("staff",null);view.staffSubmitEnabled(false);this.server.staffAssess(rubric.optionsSelected(),rubric.criterionFeedback(),rubric.overallFeedback(),submissionID).done(function(){view.loadStudentInfo({expanded_view:"final-grade"})}).fail(function(errorMessage){baseView.toggleActionError("staff",errorMessage);view.staffSubmitEnabled(true)})}}}(OpenAssessment);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.server.renderLatex($("#openassessment__student-training",view.element));view.installHandlers()}).fail(function(){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",view.element);var instructions=$("#openassessment__student-training--instructions",view.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)}}}; })},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);return enabled}},selfAssess:function(){var view=this;var baseView=this.baseView;baseView.toggleActionError("self",null);view.selfSubmitEnabled(false);this.server.selfAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.rubric.overallFeedback()).done(function(){baseView.loadAssessmentModules();baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("self",errMsg);view.selfSubmitEnabled(true)})}};!function(OpenAssessment){"use strict";OpenAssessment.StaffAreaView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.StaffAreaView.prototype={load:function(){var view=this;if($(".openassessment__staff-area",view.element).length>0){this.server.render("staff_area").done(function(html){$(".openassessment__staff-area",view.element).replaceWith(html);view.server.renderLatex($(".openassessment__staff-area",view.element));view.installHandlers()}).fail(function(){view.baseView.showLoadError("staff_area")})}},loadStudentInfo:function(options){var view=this;var $staffTools=$(".openassessment__staff-tools",this.element);var $form=$staffTools.find(".openassessment_student_info_form");var student_username=$staffTools.find(".openassessment__student_username").val();var showFormError=function(errorMessage){$form.find(".form--error").text(errorMessage)};var deferred=$.Deferred();$(".openassessment__student-info",view.element).text("");if(student_username.trim()){this.server.studentInfo(student_username,options).done(function(html){showFormError("");$(".openassessment__student-info",view.element).replaceWith(html);$staffTools.on("click",".action--submit-cancel-submission",function(eventObject){eventObject.preventDefault();view.cancelSubmission($(this).data("submission-uuid"))});var handleChange=function(eventData){view.handleCommentChanged(eventData)};$staffTools.find(".cancel_submission_comments").on("change keyup drop paste",handleChange);var $rubric=$(".staff-assessment__assessment",view.element);if($rubric.size()>0){var rubricElement=$rubric.get(0);var rubric=new OpenAssessment.Rubric(rubricElement);rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled,view));$(".wrapper--staff-assessment .action--submit",view.element).click(function(eventObject){var target=$(eventObject.currentTarget),rootElement=target.closest(".openassessment__student-info"),submissionID=rootElement.data("submission-uuid");eventObject.preventDefault();view.submitStaffAssessment(submissionID,rubric)})}deferred.resolve()}).fail(function(){showFormError(gettext("Unexpected server error."));deferred.reject()})}else{showFormError(gettext("A learner name must be provided."));deferred.reject()}return deferred.promise()},installHandlers:function(){var view=this;var $staffArea=$(".openassessment__staff-area",this.element);var $staffTools=$(".openassessment__staff-tools",$staffArea);var $staffInfo=$(".openassessment__student-info",$staffArea);if($staffArea.length<=0){return}this.baseView.setUpCollapseExpand($staffTools,function(){});this.baseView.setUpCollapseExpand($staffInfo,function(){});$staffArea.find(".ui-staff__button").click(function(eventObject){var $button=$(eventObject.currentTarget),panelClass=$button.data("panel"),$panel=$staffArea.find("."+panelClass).first();if($button.hasClass("is--active")){$button.removeClass("is--active");$panel.addClass("is--hidden")}else{$staffArea.find(".ui-staff__button").removeClass("is--active");$button.addClass("is--active");$staffArea.find(".wrapper--ui-staff").addClass("is--hidden");$panel.removeClass("is--hidden")}});$staffArea.find(".ui-staff_close_button").click(function(eventObject){var $button=$(eventObject.currentTarget),$panel=$button.closest(".wrapper--ui-staff");$staffArea.find(".ui-staff__button").removeClass("is--active");$panel.addClass("is--hidden")});$staffTools.find(".openassessment_student_info_form").submit(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$staffTools.find(".action--submit-username").click(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$staffTools.find(".action--submit-training").click(function(eventObject){eventObject.preventDefault();view.scheduleTraining()});$staffTools.find(".action--submit-unfinished-tasks").click(function(eventObject){eventObject.preventDefault();view.rescheduleUnfinishedTasks()})},scheduleTraining:function(){var view=this;this.server.scheduleTraining().done(function(msg){$(".schedule_training_message",view.element).text(msg)}).fail(function(errMsg){$(".schedule_training_message",view.element).text(errMsg)})},rescheduleUnfinishedTasks:function(){var view=this;this.server.rescheduleUnfinishedTasks().done(function(msg){$(".reschedule_unfinished_tasks_message",view.element).text(msg)}).fail(function(errMsg){$(".reschedule_unfinished_tasks_message",view.element).text(errMsg)})},cancelSubmission:function(submissionUUID){this.cancelSubmissionEnabled(false);var view=this;var comments=$(".cancel_submission_comments",this.element).val();this.server.cancelSubmission(submissionUUID,comments).done(function(){view.loadStudentInfo({expanded_view:"final-grade"})}).fail(function(errorMessage){$(".cancel-submission-error").html(_.escape(errorMessage))})},cancelSubmissionEnabled:function(enabled){var $cancelButton=$(".action--submit-cancel-submission",this.element);if(typeof enabled==="undefined"){return!$cancelButton.hasClass("is--disabled")}else{$cancelButton.toggleClass("is--disabled",!enabled)}},comment:function(text){var $submissionComments=$(".cancel_submission_comments",this.element);if(typeof text==="undefined"){return $submissionComments.val()}else{$submissionComments.val(text)}},handleCommentChanged:function(){var isBlank=$.trim(this.comment())!=="";this.cancelSubmissionEnabled(isBlank)},staffSubmitEnabled:function(enabled){var button=$(".wrapper--staff-assessment .action--submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},submitStaffAssessment:function(submissionID,rubric){var view=this;var baseView=this.baseView;baseView.toggleActionError("staff",null);view.staffSubmitEnabled(false);this.server.staffAssess(rubric.optionsSelected(),rubric.criterionFeedback(),rubric.overallFeedback(),submissionID).done(function(){view.loadStudentInfo({expanded_view:"final-grade"})}).fail(function(errorMessage){$(".staff-override-error").html(_.escape(errorMessage));view.staffSubmitEnabled(true)})}}}(OpenAssessment);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.server.renderLatex($("#openassessment__student-training",view.element));view.installHandlers()}).fail(function(){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",view.element);var instructions=$("#openassessment__student-training--instructions",view.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
...@@ -4,8 +4,21 @@ ...@@ -4,8 +4,21 @@
describe('OpenAssessment.StaffAreaView', function() { describe('OpenAssessment.StaffAreaView', function() {
'use strict'; 'use strict';
var successPromise = $.Deferred(
function(defer) { defer.resolve(); }
).promise();
var failWith = function(owner, result) {
return function() {
return $.Deferred(function(defer) {
defer.rejectWith(owner, [result]);
}).promise();
};
};
// Stub server that returns dummy data for the staff info view // Stub server that returns dummy data for the staff info view
var StubServer = function() { var StubServer = function() {
this.studentTemplate = 'oa_student_info.html';
// Remember which fragments have been loaded // Remember which fragments have been loaded
this.fragmentsLoaded = []; this.fragmentsLoaded = [];
...@@ -23,7 +36,7 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -23,7 +36,7 @@ describe('OpenAssessment.StaffAreaView', function() {
this.studentInfo = function() { this.studentInfo = function() {
var server = this; var server = this;
return $.Deferred(function(defer) { return $.Deferred(function(defer) {
var fragment = readFixtures('oa_student_info.html'); var fragment = readFixtures(server.studentTemplate);
defer.resolveWith(server, [fragment]); defer.resolveWith(server, [fragment]);
}); });
}; };
...@@ -42,14 +55,14 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -42,14 +55,14 @@ describe('OpenAssessment.StaffAreaView', function() {
}).promise(); }).promise();
}; };
var successPromise = $.Deferred(
function(defer) { defer.resolve(); }
).promise();
this.cancelSubmission = function() { this.cancelSubmission = function() {
return successPromise; return successPromise;
}; };
this.staffAssess = function() {
return successPromise;
};
this.data = {}; this.data = {};
}; };
...@@ -162,67 +175,6 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -162,67 +175,6 @@ describe('OpenAssessment.StaffAreaView', function() {
}); });
}); });
describe('Student Info', function() {
var chooseStudent = function(view, studentName) {
var studentNameField = $('.openassessment__student_username', view.element),
submitButton = $('.action--submit-username', view.element);
studentNameField.val(studentName);
submitButton.click();
};
beforeEach(function() {
loadFixtures('oa_base_course_staff.html');
appendLoadFixtures('oa_student_info.html');
});
it('shows an error when clicking "Submit" with no student name chosen', function() {
var staffArea = createStaffArea();
chooseStudent(staffArea, '');
expect($('.openassessment_student_info_form .form--error', staffArea.element).text().trim())
.toBe('A learner name must be provided.');
});
describe('Submission Management', function() {
it('updates submission cancellation button when comments changes', function() {
// Prevent the server's response from resolving,
// so we can see what happens before view gets re-rendered.
spyOn(server, 'cancelSubmission').and.callFake(function() {
return $.Deferred(function() {}).promise();
});
var staffArea = createStaffArea();
chooseStudent(staffArea, 'testStudent');
// comments is blank --> cancel submission button disabled
staffArea.comment('');
staffArea.handleCommentChanged();
expect(staffArea.cancelSubmissionEnabled()).toBe(false);
// Response is whitespace --> cancel submission button disabled
staffArea.comment(' \n \n ');
staffArea.handleCommentChanged();
expect(staffArea.cancelSubmissionEnabled()).toBe(false);
// Response is not blank --> cancel submission button enabled
staffArea.comment('Cancellation reason.');
staffArea.handleCommentChanged();
expect(staffArea.cancelSubmissionEnabled()).toBe(true);
});
it('submits the cancel submission comments to the server', function() {
spyOn(server, 'cancelSubmission').and.callThrough();
var staffArea = createStaffArea();
chooseStudent(staffArea, 'testStudent');
staffArea.comment('Cancellation reason.');
staffArea.cancelSubmission('Bob');
expect(server.cancelSubmission).toHaveBeenCalledWith('Bob', 'Cancellation reason.');
});
});
});
describe('Staff Toolbar', function() { describe('Staff Toolbar', function() {
beforeEach(function() { beforeEach(function() {
loadFixtures('oa_base_course_staff.html'); loadFixtures('oa_base_course_staff.html');
...@@ -281,6 +233,13 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -281,6 +233,13 @@ describe('OpenAssessment.StaffAreaView', function() {
}); });
describe('Staff Tools', function() { describe('Staff Tools', function() {
var chooseStudent = function(view, studentName) {
var studentNameField = $('.openassessment__student_username', view.element),
submitButton = $('.action--submit-username', view.element);
studentNameField.val(studentName);
submitButton.click();
};
beforeEach(function() { beforeEach(function() {
loadFixtures('oa_base_course_staff.html'); loadFixtures('oa_base_course_staff.html');
}); });
...@@ -296,6 +255,144 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -296,6 +255,144 @@ describe('OpenAssessment.StaffAreaView', function() {
expect($staffToolsButton).not.toHaveClass('is--active'); expect($staffToolsButton).not.toHaveClass('is--active');
expect($staffToolsPanel).toHaveClass('is--hidden'); expect($staffToolsPanel).toHaveClass('is--hidden');
}); });
it('shows an error when clicking "Submit" with no student name chosen', function() {
var staffArea = createStaffArea();
chooseStudent(staffArea, '');
expect($('.openassessment_student_info_form .form--error', staffArea.element).text().trim())
.toBe('A learner name must be provided.');
});
describe('Submission Management', function() {
it('updates submission cancellation button when comments changes', function() {
// Prevent the server's response from resolving,
// so we can see what happens before view gets re-rendered.
spyOn(server, 'cancelSubmission').and.callFake(function() {
return $.Deferred(function() {}).promise();
});
var staffArea = createStaffArea();
chooseStudent(staffArea, 'testStudent');
// comments is blank --> cancel submission button disabled
staffArea.comment('');
staffArea.handleCommentChanged();
expect(staffArea.cancelSubmissionEnabled()).toBe(false);
// Response is whitespace --> cancel submission button disabled
staffArea.comment(' \n \n ');
staffArea.handleCommentChanged();
expect(staffArea.cancelSubmissionEnabled()).toBe(false);
// Response is not blank --> cancel submission button enabled
staffArea.comment('Cancellation reason.');
staffArea.handleCommentChanged();
expect(staffArea.cancelSubmissionEnabled()).toBe(true);
});
it('submits the cancel submission comments to the server', function() {
// Show the staff area for the test student
var staffArea = createStaffArea();
chooseStudent(staffArea, 'testStudent');
// Cancel the student's submission
staffArea.comment('Cancellation reason.');
server.studentTemplate = 'oa_staff_cancelled_submission.html';
staffArea.cancelSubmission('Bob');
// Verify that the student view reflects the cancellation
expect($($('.staff-info__student__response p', staffArea.element)[0]).text().trim()).toBe(
'Learner submission removed by staff on October 1, 2015 04:53 UTC'
);
expect($($('.staff-info__student__response p', staffArea.element)[1]).text().trim()).toBe(
'Comments: Cancelled!'
);
});
it('shows an error message when a cancel submission request fails', function() {
// Show the staff area for the test student
var staffArea = createStaffArea(),
serverErrorMessage = 'Mock server error';
chooseStudent(staffArea, 'testStudent');
// Cancel the student's submission but return a server error
staffArea.comment('Cancellation reason.');
server.cancelSubmission = failWith(server, serverErrorMessage);
staffArea.cancelSubmission('Bob');
// Verify that the error message is shown
expect($('.cancel-submission-error', staffArea.element).first().text().trim()).toBe(serverErrorMessage);
});
});
describe('Staff Grade Override', function() {
var fillAssessment = function($assessment) {
$('#staff__assessment__rubric__question--2__feedback', $assessment).val('Text response');
$('.question__answers', $assessment).each(function() {
$('input[type="radio"]', this).first().click();
});
};
var submitAssessment = function(staffArea) {
var $assessment = $('.wrapper--staff-assessment', staffArea.element),
$submitButton = $('.action--submit', $assessment);
$submitButton.click();
};
it('enables the submit button when all required fields are specified', function() {
var staffArea = createStaffArea(),
$assessment, $submitButton;
chooseStudent(staffArea, 'testStudent');
$assessment = $('.wrapper--staff-assessment', staffArea.element);
$submitButton = $('.action--submit', $assessment);
expect($submitButton).toHaveClass('is--disabled');
fillAssessment($assessment);
expect($submitButton).not.toHaveClass('is--disabled');
});
it('can submit a staff grade override', function() {
var staffArea = createStaffArea(),
$assessment, $gradeSection;
chooseStudent(staffArea, 'testStudent');
// Verify that the student info section is hidden but shows the original score
$gradeSection = $('.staff-info__student__grade', staffArea.element);
expect($('.ui-toggle-visibility', $gradeSection)).toHaveClass('is--collapsed');
expect($('p', $gradeSection).first().text().trim()).toBe(
'The problem has not been started.'
);
// Fill in and submit the assessment
$assessment = $('.wrapper--staff-assessment', staffArea.element);
fillAssessment($assessment);
server.studentTemplate = 'oa_staff_graded_submission.html';
submitAssessment(staffArea);
// Verify that the student info is visible and shows the correct score
$gradeSection = $('.staff-info__student__grade', staffArea.element);
expect($('.ui-toggle-visibility', $gradeSection)).not.toHaveClass('is--collapsed');
expect($('p', $gradeSection).first().text().trim()).toBe(
'Final grade: 1 out of 2'
);
});
it('shows an error message when a grade override request fails', function() {
var staffArea = createStaffArea(),
serverErrorMessage = 'Mock server error',
$assessment;
chooseStudent(staffArea, 'testStudent');
$assessment = $('.wrapper--staff-assessment', staffArea.element);
fillAssessment($assessment);
// Submit the assessment but return a server error message
staffArea.comment('Cancellation reason.');
server.staffAssess = failWith(server, serverErrorMessage);
submitAssessment(staffArea);
// Verify that the error message is shown
expect($('.staff-override-error', staffArea.element).first().text().trim()).toBe(serverErrorMessage);
});
});
}); });
describe('Staff Info', function() { describe('Staff Info', function() {
......
...@@ -126,8 +126,7 @@ OpenAssessment.BaseView.prototype = { ...@@ -126,8 +126,7 @@ OpenAssessment.BaseView.prototype = {
else { else {
// Insert the error message // Insert the error message
var msgHtml = (message === null) ? "" : message; $(container + " .message__content", element).html('<p>' + (message ? _.escape(message) : "") + '</p>');
$(container + " .message__content", element).html('<p>' + msgHtml + '</p>');
// Toggle the error class // Toggle the error class
$(container, element).toggleClass('has--error', message !== null); $(container, element).toggleClass('has--error', message !== null);
} }
...@@ -146,7 +145,7 @@ OpenAssessment.BaseView.prototype = { ...@@ -146,7 +145,7 @@ OpenAssessment.BaseView.prototype = {
var $container = $('#openassessment__' + stepName); var $container = $('#openassessment__' + stepName);
$container.toggleClass('has--error', true); $container.toggleClass('has--error', true);
$container.find('.step__status__value i').removeClass().addClass('icon fa fa-exclamation-triangle'); $container.find('.step__status__value i').removeClass().addClass('icon fa fa-exclamation-triangle');
$container.find('.step__status__value .copy').html(errorMessage); $container.find('.step__status__value .copy').html(_.escape(errorMessage));
} }
}; };
......
...@@ -149,6 +149,7 @@ OpenAssessment.PeerView.prototype = { ...@@ -149,6 +149,7 @@ OpenAssessment.PeerView.prototype = {
return !button.hasClass('is--disabled'); return !button.hasClass('is--disabled');
} else { } else {
button.toggleClass('is--disabled', !enabled); button.toggleClass('is--disabled', !enabled);
return enabled;
} }
}, },
......
...@@ -168,6 +168,7 @@ OpenAssessment.ResponseView.prototype = { ...@@ -168,6 +168,7 @@ OpenAssessment.ResponseView.prototype = {
return !sel.hasClass('is--disabled'); return !sel.hasClass('is--disabled');
} else { } else {
sel.toggleClass('is--disabled', !enabled); sel.toggleClass('is--disabled', !enabled);
return enabled;
} }
}, },
...@@ -229,7 +230,7 @@ OpenAssessment.ResponseView.prototype = { ...@@ -229,7 +230,7 @@ OpenAssessment.ResponseView.prototype = {
// Setting the HTML will overwrite the screen reader tag, // Setting the HTML will overwrite the screen reader tag,
// so prepend it to the message. // so prepend it to the message.
var label = gettext("Status of Your Response"); var label = gettext("Status of Your Response");
sel.html('<span class="sr">' + label + ':' + '</span>\n' + msg); sel.html('<span class="sr">' + _.escape(label) + ':' + '</span>\n' + msg);
} }
}, },
......
...@@ -61,7 +61,7 @@ OpenAssessment.Rubric.prototype = { ...@@ -61,7 +61,7 @@ OpenAssessment.Rubric.prototype = {
**/ **/
overallFeedback: function(overallFeedback) { overallFeedback: function(overallFeedback) {
var selector = '#assessment__rubric__question--feedback__value'; var selector = '.assessment__rubric__question--feedback__value';
if (typeof overallFeedback === 'undefined') { if (typeof overallFeedback === 'undefined') {
return $(selector, this.element).val(); return $(selector, this.element).val();
} }
......
...@@ -90,6 +90,7 @@ OpenAssessment.SelfView.prototype = { ...@@ -90,6 +90,7 @@ OpenAssessment.SelfView.prototype = {
return !button.hasClass('is--disabled'); return !button.hasClass('is--disabled');
} else { } else {
button.toggleClass('is--disabled', !enabled); button.toggleClass('is--disabled', !enabled);
return enabled;
} }
}, },
......
...@@ -30,16 +30,14 @@ ...@@ -30,16 +30,14 @@
// for us to render the staff area into. If that doesn't exist, // for us to render the staff area into. If that doesn't exist,
// then we're not staff, so we don't need to send the AJAX request. // then we're not staff, so we don't need to send the AJAX request.
if ($('.openassessment__staff-area', view.element).length > 0) { if ($('.openassessment__staff-area', view.element).length > 0) {
this.server.render('staff_area') this.server.render('staff_area').done(function(html) {
.done(function(html) { // Load the HTML and install event handlers
// Load the HTML and install event handlers $('.openassessment__staff-area', view.element).replaceWith(html);
$('.openassessment__staff-area', view.element).replaceWith(html); view.server.renderLatex($('.openassessment__staff-area', view.element));
view.server.renderLatex($('.openassessment__staff-area', view.element)); view.installHandlers();
view.installHandlers(); }).fail(function() {
}).fail(function() { view.baseView.showLoadError('staff_area');
view.baseView.showLoadError('staff_area'); });
}
);
} }
}, },
...@@ -66,52 +64,50 @@ ...@@ -66,52 +64,50 @@
$('.openassessment__student-info', view.element).text(''); $('.openassessment__student-info', view.element).text('');
if (studentUsername.trim()) { if (studentUsername.trim()) {
this.server.studentInfo(studentUsername, options) this.server.studentInfo(studentUsername, options).done(function(html) {
.done(function(html) { // Clear any error message
// Clear any error message showFormError('');
showFormError('');
// Load the HTML and install event handlers
// Load the HTML and install event handlers $('.openassessment__student-info', view.element).replaceWith(html);
$('.openassessment__student-info', view.element).replaceWith(html);
// Install key handler for cancel submission button.
// Install key handler for cancel submission button. $staffTools.on('click', '.action--submit-cancel-submission', function(eventObject) {
$staffTools.on('click', '.action--submit-cancel-submission', function(eventObject) { eventObject.preventDefault();
eventObject.preventDefault(); view.cancelSubmission($(this).data('submission-uuid'));
view.cancelSubmission($(this).data('submission-uuid'));
});
// Install change handler for textarea (to enable cancel submission button)
var handleChange = function(eventData) { view.handleCommentChanged(eventData); };
$staffTools.find('.cancel_submission_comments')
.on('change keyup drop paste', handleChange);
// Initialize the rubric
var $rubric = $('.staff-assessment__assessment', view.element);
if ($rubric.size() > 0) {
var rubricElement = $rubric.get(0);
var rubric = new OpenAssessment.Rubric(rubricElement);
// Install a change handler for rubric options to enable/disable the submit button
rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled, view));
// Install a click handler for the submit button
$('.wrapper--staff-assessment .action--submit', view.element).click(
function(eventObject) {
var target = $(eventObject.currentTarget),
rootElement = target.closest('.openassessment__student-info'),
submissionID = rootElement.data('submission-uuid');
eventObject.preventDefault();
view.submitStaffAssessment(submissionID, rubric);
}
);
}
deferred.resolve();
})
.fail(function() {
showFormError(gettext('Unexpected server error.'));
deferred.reject();
}); });
// Install change handler for textarea (to enable cancel submission button)
var handleChange = function(eventData) { view.handleCommentChanged(eventData); };
$staffTools.find('.cancel_submission_comments')
.on('change keyup drop paste', handleChange);
// Initialize the rubric
var $rubric = $('.staff-assessment__assessment', view.element);
if ($rubric.size() > 0) {
var rubricElement = $rubric.get(0);
var rubric = new OpenAssessment.Rubric(rubricElement);
// Install a change handler for rubric options to enable/disable the submit button
rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled, view));
// Install a click handler for the submit button
$('.wrapper--staff-assessment .action--submit', view.element).click(
function(eventObject) {
var target = $(eventObject.currentTarget),
rootElement = target.closest('.openassessment__student-info'),
submissionID = rootElement.data('submission-uuid');
eventObject.preventDefault();
view.submitStaffAssessment(submissionID, rubric);
}
);
}
deferred.resolve();
}).fail(function() {
showFormError(gettext('Unexpected server error.'));
deferred.reject();
});
} else { } else {
showFormError(gettext('A learner name must be provided.')); showFormError(gettext('A learner name must be provided.'));
deferred.reject(); deferred.reject();
...@@ -202,10 +198,9 @@ ...@@ -202,10 +198,9 @@
*/ */
scheduleTraining: function() { scheduleTraining: function() {
var view = this; var view = this;
this.server.scheduleTraining() this.server.scheduleTraining().done(function(msg) {
.done(function(msg) { $('.schedule_training_message', view.element).text(msg);
$('.schedule_training_message', view.element).text(msg); }).fail(function(errMsg) {
}).fail(function(errMsg) {
$('.schedule_training_message', view.element).text(errMsg); $('.schedule_training_message', view.element).text(errMsg);
}); });
}, },
...@@ -217,10 +212,9 @@ ...@@ -217,10 +212,9 @@
*/ */
rescheduleUnfinishedTasks: function() { rescheduleUnfinishedTasks: function() {
var view = this; var view = this;
this.server.rescheduleUnfinishedTasks() this.server.rescheduleUnfinishedTasks().done(function(msg) {
.done(function(msg) { $('.reschedule_unfinished_tasks_message', view.element).text(msg);
$('.reschedule_unfinished_tasks_message', view.element).text(msg); }).fail(function(errMsg) {
}).fail(function(errMsg) {
$('.reschedule_unfinished_tasks_message', view.element).text(errMsg); $('.reschedule_unfinished_tasks_message', view.element).text(errMsg);
}); });
}, },
...@@ -233,17 +227,15 @@ ...@@ -233,17 +227,15 @@
this.cancelSubmissionEnabled(false); this.cancelSubmissionEnabled(false);
var view = this; var view = this;
var comments = $('.cancel_submission_comments', this.element).val(); var comments = $('.cancel_submission_comments', this.element).val();
this.server.cancelSubmission(submissionUUID, comments) this.server.cancelSubmission(submissionUUID, comments).done(function() {
.done(function(msg) { // Note: we ignore any message returned from the server and instead
$('.cancel-submission-error').html(''); // re-render the student info with the "Learner's Final Grade"
view.loadStudentInfo({expanded_view: 'final-grade'}) // section expanded. This section will show that the learner's
.done(function() { // submission was cancelled.
$('.openassessment__staff-info__cancel__submission', view.element).html(msg); view.loadStudentInfo({expanded_view: 'final-grade'});
}); }).fail(function(errorMessage) {
}) $('.cancel-submission-error').html(_.escape(errorMessage));
.fail(function(errMsg) { });
$('.cancel-submission-error').html(errMsg);
});
}, },
/** /**
...@@ -313,6 +305,7 @@ ...@@ -313,6 +305,7 @@
return !button.hasClass('is--disabled'); return !button.hasClass('is--disabled');
} else { } else {
button.toggleClass('is--disabled', !enabled); button.toggleClass('is--disabled', !enabled);
return enabled;
} }
}, },
...@@ -331,14 +324,17 @@ ...@@ -331,14 +324,17 @@
this.server.staffAssess( this.server.staffAssess(
rubric.optionsSelected(), rubric.criterionFeedback(), rubric.overallFeedback(), submissionID rubric.optionsSelected(), rubric.criterionFeedback(), rubric.overallFeedback(), submissionID
) ).done(function() {
.done(function() { // Note: we ignore any message returned from the server and instead
view.loadStudentInfo({expanded_view: 'final-grade'}); // re-render the student info with the "Learner's Final Grade"
}) // section expanded. This section will show the learner's
.fail(function(errorMessage) { // final grade and in the future should include details of
baseView.toggleActionError('staff', errorMessage); // the staff override itself.
view.staffSubmitEnabled(true); view.loadStudentInfo({expanded_view: 'final-grade'});
}); }).fail(function(errorMessage) {
$('.staff-override-error').html(_.escape(errorMessage));
view.staffSubmitEnabled(true);
});
} }
}; };
})(OpenAssessment); })(OpenAssessment);
...@@ -8,8 +8,9 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -8,8 +8,9 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
/** /**
* Interface for server-side XBlock handlers. * Interface for server-side XBlock handlers.
* @param runtime (Runtime): An XBlock runtime instance. *
* @param element (DOM element): The DOM element representing this XBlock. * @param {runtime} runtime - An XBlock runtime instance.
* @param {element} element - The DOM element representing this XBlock.
* @constructor * @constructor
*/ */
OpenAssessment.Server = function(runtime, element) { OpenAssessment.Server = function(runtime, element) {
...@@ -43,23 +44,21 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -43,23 +44,21 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
var url = this.url('render_' + component); var url = this.url('render_' + component);
return $.Deferred(function(defer) { return $.Deferred(function(defer) {
$.ajax({ $.ajax({
url: url, url: url,
type: "POST", type: "POST",
dataType: "html" dataType: "html"
}) }).done(function(data) {
.done(function(data) { defer.resolveWith(view, [data]);
defer.resolveWith(view, [data]); }).fail(function() {
}) defer.rejectWith(view, [gettext('This section could not be loaded.')]);
.fail(function() { });
defer.rejectWith(view, [gettext('This section could not be loaded.')]);
});
}).promise(); }).promise();
}, },
/** /**
* Render Latex for all new DOM elements with class 'allow--latex'. * Render Latex for all new DOM elements with class 'allow--latex'.
* *
* @param element The element to modify. * @param {element} element - The element to modify.
*/ */
renderLatex: function(element) { renderLatex: function(element) {
element.filter(".allow--latex").each(function() { element.filter(".allow--latex").each(function() {
...@@ -77,6 +76,7 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -77,6 +76,7 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
renderContinuedPeer: function() { renderContinuedPeer: function() {
var view = this; var view = this;
var url = this.url('render_peer_assessment'); var url = this.url('render_peer_assessment');
return $.Deferred(function(defer) { return $.Deferred(function(defer) {
$.ajax({ $.ajax({
url: url, url: url,
...@@ -94,8 +94,8 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -94,8 +94,8 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
/** /**
* Load the student information section inside the Staff Info section. * Load the student information section inside the Staff Info section.
* *
* @param {string} student_username The username for the student. * @param {string} studentUsername - The username for the student.
* @param {object} options An optional set of configuration options. * @param {object} options - An optional set of configuration options.
* @returns {promise} A JQuery promise, which resolves with the HTML of the rendered section * @returns {promise} A JQuery promise, which resolves with the HTML of the rendered section
* fails with an error message. * fails with an error message.
*/ */
...@@ -127,27 +127,25 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -127,27 +127,25 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
var url = this.url('submit'); var url = this.url('submit');
return $.Deferred(function(defer) { return $.Deferred(function(defer) {
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: url, url: url,
data: JSON.stringify({submission: submission}), data: JSON.stringify({submission: submission}),
contentType: jsonContentType contentType: jsonContentType
}) }).done(function(data) {
.done(function(data) { var success = data[0];
var success = data[0]; if (success) {
if (success) { var studentId = data[1];
var studentId = data[1]; var attemptNum = data[2];
var attemptNum = data[2]; defer.resolveWith(this, [studentId, attemptNum]);
defer.resolveWith(this, [studentId, attemptNum]); }
} else {
else { var errorNum = data[1];
var errorNum = data[1]; var errorMsg = data[2];
var errorMsg = data[2]; defer.rejectWith(this, [errorNum, errorMsg]);
defer.rejectWith(this, [errorNum, errorMsg]); }
} }).fail(function() {
}) defer.rejectWith(this, ["AJAX", gettext("This response could not be submitted.")]);
.fail(function() { });
defer.rejectWith(this, ["AJAX", gettext("This response could not be submitted.")]);
});
}).promise(); }).promise();
}, },
...@@ -162,18 +160,16 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -162,18 +160,16 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
var url = this.url('save_submission'); var url = this.url('save_submission');
return $.Deferred(function(defer) { return $.Deferred(function(defer) {
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: url, url: url,
data: JSON.stringify({submission: submission}), data: JSON.stringify({submission: submission}),
contentType: jsonContentType contentType: jsonContentType
}) }).done(function(data) {
.done(function(data) { if (data.success) { defer.resolve(); }
if (data.success) { defer.resolve(); } else { defer.rejectWith(this, [data.msg]); }
else { defer.rejectWith(this, [data.msg]); } }).fail(function() {
}) defer.rejectWith(this, [gettext("This response could not be saved.")]);
.fail(function() { });
defer.rejectWith(this, [gettext("This response could not be saved.")]);
});
}).promise(); }).promise();
}, },
...@@ -195,12 +191,8 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -195,12 +191,8 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
$.ajax({ $.ajax({
type: "POST", url: url, data: payload, contentType: jsonContentType type: "POST", url: url, data: payload, contentType: jsonContentType
}).done(function(data) { }).done(function(data) {
if (data.success) { if (data.success) { defer.resolve(); }
defer.resolve(); else { defer.rejectWith(this, [data.msg]); }
}
else {
defer.rejectWith(this, [data.msg]);
}
}).fail(function() { }).fail(function() {
defer.rejectWith(this, [gettext('This feedback could not be submitted.')]); defer.rejectWith(this, [gettext('This feedback could not be submitted.')]);
}); });
...@@ -210,8 +202,8 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -210,8 +202,8 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
/** /**
* Submits an assessment. * Submits an assessment.
* *
* @param {string} assessmentType The type of assessment. * @param {string} assessmentType - The type of assessment.
* @param payload The assessment payload * @param {object} payload - The assessment payload
* @returns {promise} A promise which resolves with no arguments if successful, * @returns {promise} A promise which resolves with no arguments if successful,
* and which fails with an error message otherwise. * and which fails with an error message otherwise.
*/ */
...@@ -236,12 +228,12 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -236,12 +228,12 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
/** /**
* Send a peer assessment to the XBlock. * Send a peer assessment to the XBlock.
* *
* @param optionsSelected The options selected as a dict, * @param {object} optionsSelected - The options selected as a dict,
* e.g. { clarity: "Very clear", precision: "Somewhat precise" } * e.g. { clarity: "Very clear", precision: "Somewhat precise" }
* @param criterionFeedback Feedback on the criterion, * @param {object} criterionFeedback - Feedback on the criterion,
* e.g. { clarity: "The essay was very clear." } * e.g. { clarity: "The essay was very clear." }
* @param {string} overallFeedback A string with the staff member's overall feedback. * @param {string} overallFeedback - A string with the staff member's overall feedback.
* @param {string} submissionID The ID of the submission being assessed. * @param {string} submissionID - The ID of the submission being assessed.
* @returns {promise} A promise which resolves with no arguments if successful, * @returns {promise} A promise which resolves with no arguments if successful,
* and which fails with an error message otherwise. * and which fails with an error message otherwise.
*/ */
...@@ -257,11 +249,11 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -257,11 +249,11 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
/** /**
* Send a self assessment to the XBlock. * Send a self assessment to the XBlock.
* *
* @param optionsSelected The options selected as a dict, * @param {object} optionsSelected - The options selected as a dict,
* e.g. { clarity: "Very clear", precision: "Somewhat precise" } * e.g. { clarity: "Very clear", precision: "Somewhat precise" }
* @param criterionFeedback Feedback on the criterion, * @param {object} criterionFeedback - Feedback on the criterion,
* e.g. { clarity: "The essay was very clear." } * e.g. { clarity: "The essay was very clear." }
* @param {string} overallFeedback A string with the staff member's overall feedback. * @param {string} overallFeedback - A string with the staff member's overall feedback.
* @returns {promise} A promise which resolves with no arguments if successful, * @returns {promise} A promise which resolves with no arguments if successful,
* and which fails with an error message otherwise. * and which fails with an error message otherwise.
*/ */
...@@ -276,12 +268,12 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -276,12 +268,12 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
/** /**
* Send a staff assessment to the XBlock. * Send a staff assessment to the XBlock.
* *
* @param optionsSelected The options selected as a dict, * @param {object} optionsSelected - The options selected as a dict,
* e.g. { clarity: "Very clear", precision: "Somewhat precise" } * e.g. { clarity: "Very clear", precision: "Somewhat precise" }
* @param criterionFeedback Feedback on the criterion, * @param {object} criterionFeedback - Feedback on the criterion,
* e.g. { clarity: "The essay was very clear." } * e.g. { clarity: "The essay was very clear." }
* @param {string} overallFeedback A string with the staff member's overall feedback. * @param {string} overallFeedback - A string with the staff member's overall feedback.
* @param {string} submissionID The ID of the submission being assessed. * @param {string} submissionID - The ID of the submission being assessed.
* @returns {promise} A promise which resolves with no arguments if successful, * @returns {promise} A promise which resolves with no arguments if successful,
* and which fails with an error message otherwise. * and which fails with an error message otherwise.
*/ */
...@@ -297,7 +289,7 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -297,7 +289,7 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
/** /**
* Submit an instructor-provided training example. * Submit an instructor-provided training example.
* *
* @param optionsSelected The options selected as a dict, * @param {object} optionsSelected - The options selected as a dict,
* e.g. { clarity: "Very clear", precision: "Somewhat precise" } * e.g. { clarity: "Very clear", precision: "Somewhat precise" }
* @returns {promise} A promise which resolves with a list of corrections if successful, * @returns {promise} A promise which resolves with a list of corrections if successful,
* and which fails with an error message otherwise. * and which fails with an error message otherwise.
...@@ -308,7 +300,9 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -308,7 +300,9 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
options_selected: optionsSelected options_selected: optionsSelected
}); });
return $.Deferred(function(defer) { return $.Deferred(function(defer) {
$.ajax({type: "POST", url: url, data: payload, contentType: jsonContentType}).done(function(data) { $.ajax({
type: "POST", url: url, data: payload, contentType: jsonContentType
}).done(function(data) {
if (data.success) { if (data.success) {
defer.resolveWith(this, [data.corrections]); defer.resolveWith(this, [data.corrections]);
} }
...@@ -322,26 +316,17 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -322,26 +316,17 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
}, },
/** /**
<<<<<<< HEAD * Schedules classifier training for Example Based Assessments.
Schedules classifier training for Example Based Assessment for this *
Location. * @returns {promise} A JQuery promise, which resolves with a
* message indicating the results of the scheduling request.
Returns: */
A JQuery promise, which resolves with a message indicating the results
of the scheduling request.
Example:
server.scheduleTraining().done(
function(msg) { console.log("Success!"); }
alert(msg);
).fail(
function(errorMsg) { console.log(errorMsg); }
);
**/
scheduleTraining: function() { scheduleTraining: function() {
var url = this.url('schedule_training'); var url = this.url('schedule_training');
return $.Deferred(function(defer) { return $.Deferred(function(defer) {
$.ajax({type: "POST", url: url, data: "\"\"", contentType: jsonContentType}).done(function(data) { $.ajax({
type: "POST", url: url, data: "\"\"", contentType: jsonContentType
}).done(function(data) {
if (data.success) { if (data.success) {
defer.resolveWith(this, [data.msg]); defer.resolveWith(this, [data.msg]);
} }
...@@ -363,9 +348,9 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -363,9 +348,9 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
rescheduleUnfinishedTasks: function() { rescheduleUnfinishedTasks: function() {
var url = this.url('reschedule_unfinished_tasks'); var url = this.url('reschedule_unfinished_tasks');
return $.Deferred(function(defer) { return $.Deferred(function(defer) {
$.ajax( $.ajax({
{type: "POST", url: url, data: "\"\"", contentType: jsonContentType} type: "POST", url: url, data: "\"\"", contentType: jsonContentType
).done(function(data) { }).done(function(data) {
if (data.success) { if (data.success) {
defer.resolveWith(this, [data.msg]); defer.resolveWith(this, [data.msg]);
} }
...@@ -381,7 +366,7 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -381,7 +366,7 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
/** /**
* Update the XBlock's XML definition on the server. * Update the XBlock's XML definition on the server.
* *
* @param options An object with the following options: * @param {object} options - An object with the following options:
* title (string): The title of the problem. * title (string): The title of the problem.
* prompt (string): The question prompt. * prompt (string): The question prompt.
* feedbackPrompt (string): The directions to the student for giving overall feedback on a submission. * feedbackPrompt (string): The directions to the student for giving overall feedback on a submission.
...@@ -418,15 +403,13 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -418,15 +403,13 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
}); });
return $.Deferred(function(defer) { return $.Deferred(function(defer) {
$.ajax({ $.ajax({
type: "POST", url: url, data: payload, contentType: jsonContentType type: "POST", url: url, data: payload, contentType: jsonContentType
}) }).done(function(data) {
.done(function(data) { if (data.success) { defer.resolve(); }
if (data.success) { defer.resolve(); } else { defer.rejectWith(this, [data.msg]); }
else { defer.rejectWith(this, [data.msg]); } }).fail(function() {
}) defer.rejectWith(this, [gettext('This problem could not be saved.')]);
.fail(function() { });
defer.rejectWith(this, [gettext('This problem could not be saved.')]);
});
}).promise(); }).promise();
}, },
...@@ -442,15 +425,13 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -442,15 +425,13 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
var payload = "\"\""; var payload = "\"\"";
return $.Deferred(function(defer) { return $.Deferred(function(defer) {
$.ajax({ $.ajax({
type: "POST", url: url, data: payload, contentType: jsonContentType type: "POST", url: url, data: payload, contentType: jsonContentType
}) }).done(function(data) {
.done(function(data) { if (data.success) { defer.resolveWith(this, [data.is_released]); }
if (data.success) { defer.resolveWith(this, [data.is_released]); } else { defer.rejectWith(this, [data.msg]); }
else { defer.rejectWith(this, [data.msg]); } }).fail(function() {
}) defer.rejectWith(this, [gettext("The server could not be contacted.")]);
.fail(function() { });
defer.rejectWith(this, [gettext("The server could not be contacted.")]);
});
}).promise(); }).promise();
}, },
...@@ -503,11 +484,11 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -503,11 +484,11 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
/** /**
* Cancel a submission from the peer grading pool. * Cancel a submission from the peer grading pool.
* *
* @param submissionID The id of the submission to be canceled. * @param {object} submissionID - The id of the submission to be canceled.
* @param comments The reason for canceling the submission. * @param {object} comments - The reason for canceling the submission.
* @returns {*} * @returns {*}
*/ */
cancelSubmission: function (submissionID, comments) { cancelSubmission: function(submissionID, comments) {
var url = this.url('cancel_submission'); var url = this.url('cancel_submission');
var payload = JSON.stringify({ var payload = JSON.stringify({
submission_uuid: submissionID, submission_uuid: submissionID,
......
...@@ -6,8 +6,8 @@ from xblock.core import XBlock ...@@ -6,8 +6,8 @@ from xblock.core import XBlock
from submissions import api from submissions import api
from openassessment.fileupload import api as file_upload_api from openassessment.fileupload import api as file_upload_api
from openassessment.fileupload.exceptions import FileUploadError from openassessment.fileupload.exceptions import FileUploadError
from openassessment.workflow import api as workflow_api
from openassessment.workflow.errors import AssessmentWorkflowError from openassessment.workflow.errors import AssessmentWorkflowError
from .resolve_dates import DISTANT_FUTURE from .resolve_dates import DISTANT_FUTURE
from data_conversion import create_submission_dict, prepare_submission_for_serialization from data_conversion import create_submission_dict, prepare_submission_for_serialization
...@@ -421,18 +421,12 @@ class SubmissionMixin(object): ...@@ -421,18 +421,12 @@ class SubmissionMixin(object):
context['save_status'] = self.save_status context['save_status'] = self.save_status
context['submit_enabled'] = self.saved_response != '' context['submit_enabled'] = self.saved_response != ''
path = "openassessmentblock/response/oa_response.html" path = "openassessmentblock/response/oa_response.html"
elif workflow["status"] == "cancelled": elif workflow["status"] == "cancelled":
workflow_cancellation = workflow_api.get_assessment_workflow_cancellation(self.submission_uuid) context["workflow_cancellation"] = self.get_workflow_cancellation_info(self.submission_uuid)
if workflow_cancellation:
workflow_cancellation['cancelled_by'] = self.get_username(workflow_cancellation['cancelled_by_id'])
context['workflow_cancellation'] = workflow_cancellation
context["student_submission"] = self.get_user_submission( context["student_submission"] = self.get_user_submission(
workflow["submission_uuid"] workflow["submission_uuid"]
) )
path = 'openassessmentblock/response/oa_response_cancelled.html' path = 'openassessmentblock/response/oa_response_cancelled.html'
elif workflow["status"] == "done": elif workflow["status"] == "done":
student_submission = self.get_user_submission( student_submission = self.get_user_submission(
workflow["submission_uuid"] workflow["submission_uuid"]
......
...@@ -231,7 +231,7 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -231,7 +231,7 @@ class TestCourseStaff(XBlockHandlerTestCase):
path, context = xblock.get_student_info_path_and_context("Bob") path, context = xblock.get_student_info_path_and_context("Bob")
self.assertEquals("Bob Answer 1", context['submission']['answer']['parts'][0]['text']) self.assertEquals("Bob Answer 1", context['submission']['answer']['parts'][0]['text'])
self.assertEquals([], context['peer_assessments']) self.assertEquals(None, context['peer_assessments'])
self.assertEquals("openassessmentblock/staff_area/oa_student_info.html", path) self.assertEquals("openassessmentblock/staff_area/oa_student_info.html", path)
@scenario('data/basic_scenario.xml', user_id='Bob') @scenario('data/basic_scenario.xml', user_id='Bob')
......
...@@ -318,23 +318,17 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -318,23 +318,17 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
@scenario('data/submission_open.xml', user_id="Bob") @scenario('data/submission_open.xml', user_id="Bob")
def test_cancelled_submission(self, xblock): def test_cancelled_submission(self, xblock):
student_item = xblock.get_student_item_dict() student_item = xblock.get_student_item_dict()
mock_staff = Mock(name='Bob')
xblock.get_username = Mock(return_value=mock_staff)
submission = xblock.create_submission( submission = xblock.create_submission(
student_item, student_item,
('A man must have a code', 'A man must have an umbrella too.') ('A man must have a code', 'A man must have an umbrella too.')
) )
xblock.get_workflow_info = Mock(return_value={ workflow_api.cancel_workflow(
'status': 'cancelled', submission_uuid=submission['uuid'], comments='Inappropriate language',
'submission_uuid': submission['uuid'] cancelled_by_id='Bob',
}) assessment_requirements=xblock.workflow_requirements()
)
xblock.get_username = Mock(return_value='Bob')
workflow_api.get_assessment_workflow_cancellation = Mock(return_value={
'comments': 'Inappropriate language',
'cancelled_by_id': 'Bob',
'created_at': dt.datetime(2999, 5, 6).replace(tzinfo=pytz.utc),
'cancelled_by': 'Bob'
})
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/response/oa_response_cancelled.html', xblock, 'openassessmentblock/response/oa_response_cancelled.html',
...@@ -347,9 +341,9 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -347,9 +341,9 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'student_submission': submission, 'student_submission': submission,
'workflow_cancellation': { 'workflow_cancellation': {
'comments': 'Inappropriate language', 'comments': 'Inappropriate language',
'cancelled_at': xblock.get_workflow_cancellation_info(submission['uuid']).get('cancelled_at'),
'cancelled_by_id': 'Bob', 'cancelled_by_id': 'Bob',
'created_at': dt.datetime(2999, 5, 6).replace(tzinfo=pytz.utc), 'cancelled_by': mock_staff
'cancelled_by': 'Bob'
} }
} }
) )
...@@ -498,6 +492,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -498,6 +492,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
""" """
path, context = xblock.submission_path_and_context() path, context = xblock.submission_path_and_context()
self.maxDiff = None # Show a full diff
self.assertEqual(path, expected_path) self.assertEqual(path, expected_path)
self.assertEqual(context, expected_context) self.assertEqual(context, expected_context)
......
...@@ -3,7 +3,9 @@ Handle OpenAssessment XBlock requests to the Workflow API. ...@@ -3,7 +3,9 @@ Handle OpenAssessment XBlock requests to the Workflow API.
""" """
from xblock.core import XBlock from xblock.core import XBlock
from openassessment.workflow import api as workflow_api from openassessment.workflow import api as workflow_api
from openassessment.workflow.models import AssessmentWorkflowCancellation
from openassessment.xblock.data_conversion import create_rubric_dict from openassessment.xblock.data_conversion import create_rubric_dict
...@@ -106,7 +108,7 @@ class WorkflowMixin(object): ...@@ -106,7 +108,7 @@ class WorkflowMixin(object):
if submission_uuid is None: if submission_uuid is None:
submission_uuid = self.submission_uuid submission_uuid = self.submission_uuid
if submission_uuid: if submission_uuid is not None:
requirements = self.workflow_requirements() requirements = self.workflow_requirements()
workflow_api.update_from_assessments(submission_uuid, requirements) workflow_api.update_from_assessments(submission_uuid, requirements)
...@@ -125,9 +127,9 @@ class WorkflowMixin(object): ...@@ -125,9 +127,9 @@ class WorkflowMixin(object):
Raises: Raises:
AssessmentWorkflowError AssessmentWorkflowError
""" """
if not submission_uuid: if submission_uuid is None:
submission_uuid = self.submission_uuid submission_uuid = self.submission_uuid
if not submission_uuid: if submission_uuid is None:
return {} return {}
return workflow_api.get_workflow_for_submission( return workflow_api.get_workflow_for_submission(
submission_uuid, self.workflow_requirements() submission_uuid, self.workflow_requirements()
...@@ -179,3 +181,26 @@ class WorkflowMixin(object): ...@@ -179,3 +181,26 @@ class WorkflowMixin(object):
for ra in self.valid_assessments for ra in self.valid_assessments
if ra['name'] in self.ASSESSMENT_STEP_NAMES if ra['name'] in self.ASSESSMENT_STEP_NAMES
] ]
def get_workflow_cancellation_info(self, submission_uuid):
"""
Returns cancellation information for a particular submission.
:param submission_uuid: The submission to return information for.
:return: The cancellation information, or None if the submission has
not been cancelled.
"""
cancellation_info = workflow_api.get_assessment_workflow_cancellation(submission_uuid)
if not cancellation_info:
return None
# Add the username of the staff member who cancelled the submission
cancellation_info['cancelled_by'] = self.get_username(cancellation_info['cancelled_by_id'])
# Add the date that the workflow was cancelled (in preference to the serialized date string)
del cancellation_info['created_at']
cancellation_model = AssessmentWorkflowCancellation.get_latest_workflow_cancellation(submission_uuid)
if cancellation_model:
cancellation_info['cancelled_at'] = cancellation_model.created_at
return cancellation_info
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
"version": "0.2.0", "version": "0.2.0",
"repository": "https://github.com/edx/edx-ora2.git", "repository": "https://github.com/edx/edx-ora2.git",
"devDependencies": { "devDependencies": {
"underscore": "^1.8.2",
"karma": "^0.12.16", "karma": "^0.12.16",
"karma-coverage": "^0.2.6", "karma-coverage": "^0.2.6",
"karma-jasmine": "^0.3.6", "karma-jasmine": "^0.3.6",
......
#!/usr/bin/env bash
# Need to exit with an error code to fail the Travis build
set -e
cd `dirname $BASH_SOURCE` && cd ..
export DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE:-"settings.test_with_coverage"}
./scripts/test-python.sh $1
./scripts/render-templates.sh
./scripts/test-js.sh
...@@ -167,7 +167,7 @@ class AssessmentMixin(object): ...@@ -167,7 +167,7 @@ class AssessmentMixin(object):
""" """
Mixin for interacting with the assessment rubric. Mixin for interacting with the assessment rubric.
""" """
def assess(self, options_selected): def assess(self, assessment_type, options_selected):
""" """
Create an assessment. Create an assessment.
...@@ -183,7 +183,8 @@ class AssessmentMixin(object): ...@@ -183,7 +183,8 @@ class AssessmentMixin(object):
""" """
for criterion_num, option_num in enumerate(options_selected): for criterion_num, option_num in enumerate(options_selected):
sel = "#assessment__rubric__question--{criterion_num}__{option_num}".format( sel = "#{assessment_type}__assessment__rubric__question--{criterion_num}__{option_num}".format(
assessment_type=assessment_type,
criterion_num=criterion_num, criterion_num=criterion_num,
option_num=option_num option_num=option_num
) )
......
...@@ -103,7 +103,7 @@ class OpenAssessmentTest(WebAppTest): ...@@ -103,7 +103,7 @@ class OpenAssessmentTest(WebAppTest):
# Submit a self-assessment # Submit a self-assessment
self.self_asmnt_page.wait_for_page().wait_for_response() self.self_asmnt_page.wait_for_page().wait_for_response()
self.assertIn(self.SUBMISSION, self.self_asmnt_page.response_text) self.assertIn(self.SUBMISSION, self.self_asmnt_page.response_text)
self.self_asmnt_page.assess(self.OPTIONS_SELECTED).wait_for_complete() self.self_asmnt_page.assess("self", self.OPTIONS_SELECTED).wait_for_complete()
self.assertTrue(self.self_asmnt_page.is_complete) self.assertTrue(self.self_asmnt_page.is_complete)
# Verify the grade # Verify the grade
...@@ -170,7 +170,7 @@ class PeerAssessmentTest(OpenAssessmentTest): ...@@ -170,7 +170,7 @@ class PeerAssessmentTest(OpenAssessmentTest):
self.submission_page.visit().submit_response(self.SUBMISSION) self.submission_page.visit().submit_response(self.SUBMISSION)
# Assess the submission (there should be at least one available) # Assess the submission (there should be at least one available)
self.peer_asmnt_page.wait_for_page().wait_for_response().assess(self.OPTIONS_SELECTED) self.peer_asmnt_page.wait_for_page().wait_for_response().assess("peer", self.OPTIONS_SELECTED)
# Check that the status indicates we've assessed one submission # Check that the status indicates we've assessed one submission
try: try:
...@@ -208,7 +208,7 @@ class StudentTrainingTest(OpenAssessmentTest): ...@@ -208,7 +208,7 @@ class StudentTrainingTest(OpenAssessmentTest):
msg = "Did not complete at least {num} student training example(s).".format(num=example_num) msg = "Did not complete at least {num} student training example(s).".format(num=example_num)
self.fail(msg) self.fail(msg)
self.student_training_page.wait_for_page().wait_for_response().assess(options_selected) self.student_training_page.wait_for_page().wait_for_response().assess("training", options_selected)
# Check browser scrolled back to top only on first example # Check browser scrolled back to top only on first example
...@@ -365,11 +365,11 @@ class StaffAreaTest(OpenAssessmentTest): ...@@ -365,11 +365,11 @@ class StaffAreaTest(OpenAssessmentTest):
self.staff_area_page.verify_learner_final_score("Final grade: 6 out of 8") self.staff_area_page.verify_learner_final_score("Final grade: 6 out of 8")
# Do staff override and wait for final score to change. # Do staff override and wait for final score to change.
self.staff_area_page.assess([0, 1]) self.staff_area_page.assess("staff", [0, 1])
# Verify that the new student score is different from the original one. # Verify that the new student score is different from the original one.
# TODO: uncomment this after hooked up to the API. Also verify other state if appropriate. # Unfortunately there is no indication presently that this was a staff override.
# self.staff_area_page.verify_learner_final_score("Final grade: 1 out of 8") self.staff_area_page.verify_learner_final_score("Final grade: 1 out of 8")
@retry() @retry()
@attr('acceptance') @attr('acceptance')
...@@ -399,7 +399,7 @@ class StaffAreaTest(OpenAssessmentTest): ...@@ -399,7 +399,7 @@ class StaffAreaTest(OpenAssessmentTest):
self.staff_area_page.verify_learner_final_score( self.staff_area_page.verify_learner_final_score(
"The learner's submission has been removed from peer assessment. " "The learner's submission has been removed from peer assessment. "
"The learner receives a grade of zero unless you reset the learner's attempts for the " "The learner receives a grade of zero unless you delete the learner's state for the "
"problem to allow them to resubmit a response." "problem to allow them to resubmit a response."
) )
......
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