Commit 16bd2372 by Kevin Kim

Convert ORA dates to user's preferred time zone.

parent 9a1f72f0
...@@ -17,12 +17,12 @@ ...@@ -17,12 +17,12 @@
{% if peer_start %} {% if peer_start %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with start_date=peer_start|utc|date:"N j, Y H:i e" time_until=peer_start|timeuntil %}available <span class="date">{{ start_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with start_date=peer_start|timezone:time_zone|date:"N j, Y H:i e" time_until=peer_start|timeuntil %}available <span class="date">{{ start_date }} (in {{ time_until }})</span>{% endblocktrans %}
</span> </span>
{% elif peer_due %} {% elif peer_due %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with due_date=peer_due|utc|date:"N j, Y H:i e" time_until=peer_due|timeuntil %}due <span class="date">{{ due_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with due_date=peer_due|timezone:time_zone|date:"N j, Y H:i e" time_until=peer_due|timeuntil %}due <span class="date">{{ due_date }} (in {{ time_until }})</span>{% endblocktrans %}
</span> </span>
{% endif %} {% endif %}
</span> </span>
......
...@@ -13,12 +13,12 @@ ...@@ -13,12 +13,12 @@
{% if submission_start %} {% if submission_start %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with start_date=submission_start|utc|date:"N j, Y H:i e" time_until=submission_start|timeuntil %}available <span class="date">{{ start_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with start_date=submission_start|timezone:time_zone|date:"N j, Y H:i e" time_until=submission_start|timeuntil %}available <span class="date">{{ start_date }} (in {{ time_until }})</span>{% endblocktrans %}
</span> </span>
{% elif submission_due %} {% elif submission_due %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with due_date=submission_due|utc|date:"N j, Y H:i e" time_until=submission_due|timeuntil %}due <span class="date">{{ due_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with due_date=submission_due|timezone:time_zone|date:"N j, Y H:i e" time_until=submission_due|timeuntil %}due <span class="date">{{ due_date }} (in {{ time_until }})</span>{% endblocktrans %}
</span> </span>
{% endif %} {% endif %}
</span> </span>
......
...@@ -13,12 +13,12 @@ ...@@ -13,12 +13,12 @@
{% if self_start %} {% if self_start %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with start_date=self_start|utc|date:"N j, Y H:i e" time_until=self_start|timeuntil %}available <span class="date">{{ start_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with start_date=self_start|timezone:time_zone|date:"N j, Y H:i e" time_until=self_start|timeuntil %}available <span class="date">{{ start_date }} (in {{ time_until }})</span>{% endblocktrans %}
</span> </span>
{% elif self_due %} {% elif self_due %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with due_date=self_due|utc|date:"N j, Y H:i e" time_until=self_due|timeuntil %}due <span class="date">{{ due_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with due_date=self_due|timezone:time_zone|date:"N j, Y H:i e" time_until=self_due|timeuntil %}due <span class="date">{{ due_date }} (in {{ time_until }})</span>{% endblocktrans %}
</span> </span>
{% endif %} {% endif %}
</span> </span>
......
...@@ -13,12 +13,12 @@ ...@@ -13,12 +13,12 @@
{% if training_start %} {% if training_start %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with start_date=training_start|utc|date:"N j, Y H:i e" time_until=training_start|timeuntil %}available <span class="date">{{ start_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with start_date=training_start|timezone:time_zone|date:"N j, Y H:i e" time_until=training_start|timeuntil %}available <span class="date">{{ start_date }} (in {{ time_until }})</span>{% endblocktrans %}
</span> </span>
{% elif training_due %} {% elif training_due %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with due_date=training_due|utc|date:"N j, Y H:i e" time_until=training_due|timeuntil %}due <span class="date">{{ due_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with due_date=training_due|timezone:time_zone|date:"N j, Y H:i e" time_until=training_due|timeuntil %}due <span class="date">{{ due_date }} (in {{ time_until }})</span>{% endblocktrans %}
</span> </span>
</span> </span>
{% endif %} {% endif %}
......
...@@ -7,11 +7,10 @@ from openassessment.assessment.api import peer as peer_api ...@@ -7,11 +7,10 @@ from openassessment.assessment.api import peer as peer_api
from openassessment.assessment.errors import ( from openassessment.assessment.errors import (
PeerAssessmentRequestError, PeerAssessmentInternalError, PeerAssessmentWorkflowError PeerAssessmentRequestError, PeerAssessmentInternalError, PeerAssessmentWorkflowError
) )
from openassessment.workflow import api as workflow_api
from openassessment.workflow.errors import AssessmentWorkflowError from openassessment.workflow.errors import AssessmentWorkflowError
from openassessment.xblock.defaults import DEFAULT_RUBRIC_FEEDBACK_TEXT from openassessment.xblock.defaults import DEFAULT_RUBRIC_FEEDBACK_TEXT
from .data_conversion import create_rubric_dict from .data_conversion import create_rubric_dict
from .resolve_dates import DISTANT_FUTURE from .resolve_dates import DISTANT_FUTURE, get_current_time_zone
from .data_conversion import clean_criterion_feedback, create_submission_dict, verify_assessment_parameters from .data_conversion import clean_criterion_feedback, create_submission_dict, verify_assessment_parameters
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -162,10 +161,12 @@ class PeerAssessmentMixin(object): ...@@ -162,10 +161,12 @@ class PeerAssessmentMixin(object):
path = 'openassessmentblock/peer/oa_peer_unavailable.html' path = 'openassessmentblock/peer/oa_peer_unavailable.html'
finished = False finished = False
problem_closed, reason, start_date, due_date = self.is_closed(step="peer-assessment") problem_closed, reason, start_date, due_date = self.is_closed(step="peer-assessment")
user_service = self.runtime.service(self, 'user')
context_dict = { context_dict = {
"rubric_criteria": self.rubric_criteria_with_labels, "rubric_criteria": self.rubric_criteria_with_labels,
"allow_latex": self.allow_latex, "allow_latex": self.allow_latex,
"time_zone": get_current_time_zone(user_service),
} }
if self.rubric_feedback_prompt is not None: if self.rubric_feedback_prompt is not None:
......
...@@ -24,6 +24,18 @@ DISTANT_PAST = dt.datetime(dt.MINYEAR, 1, 1, tzinfo=pytz.utc) ...@@ -24,6 +24,18 @@ DISTANT_PAST = dt.datetime(dt.MINYEAR, 1, 1, tzinfo=pytz.utc)
DISTANT_FUTURE = dt.datetime(dt.MAXYEAR, 1, 1, tzinfo=pytz.utc) DISTANT_FUTURE = dt.datetime(dt.MAXYEAR, 1, 1, tzinfo=pytz.utc)
def get_current_time_zone(user_service):
"""
Returns the preferred time zone for the current user, if specified, or UTC if not
:param user_service: XblockUserService
"""
user_preferences = user_service.get_current_user().opt_attrs.get('edx-platform.user_preferences')
if user_preferences is None:
return pytz.utc
return pytz.timezone(user_preferences.get('time_zone', 'utc'))
def _parse_date(value, _): def _parse_date(value, _):
""" """
Parse an ISO formatted datestring into a datetime object with timezone set to UTC. Parse an ISO formatted datestring into a datetime object with timezone set to UTC.
...@@ -46,7 +58,11 @@ def _parse_date(value, _): ...@@ -46,7 +58,11 @@ def _parse_date(value, _):
try: try:
return parse_date(value).replace(tzinfo=pytz.utc) return parse_date(value).replace(tzinfo=pytz.utc)
except ValueError: except ValueError:
raise InvalidDateFormat(_("'{date}' is an invalid date format. Make sure the date is formatted as YYYY-MM-DDTHH:MM:SS.").format(date=value)) raise InvalidDateFormat(
_("'{date}' is an invalid date format. Make sure the date is formatted as YYYY-MM-DDTHH:MM:SS.").format(
date=value
)
)
else: else:
raise InvalidDateFormat(_("'{date}' must be a date string or datetime").format(date=value)) raise InvalidDateFormat(_("'{date}' must be a date string or datetime").format(date=value))
...@@ -201,8 +217,11 @@ def resolve_dates(start, end, date_ranges, _): ...@@ -201,8 +217,11 @@ def resolve_dates(start, end, date_ranges, _):
step_end = _parse_date(step_end, _) if step_end is not None else prev_end step_end = _parse_date(step_end, _) if step_end is not None else prev_end
if step_start < prev_start: if step_start < prev_start:
msg = _(u"This step's start date '{start}' cannot be earlier than the previous step's start date '{prev}'.").format( msg = _(
start=step_start, prev=prev_start u"This step's start date '{start}' cannot be earlier than the previous step's start date '{prev}'."
).format(
start=step_start,
prev=prev_start,
) )
raise DateValidationError(msg) raise DateValidationError(msg)
......
...@@ -6,7 +6,7 @@ from webob import Response ...@@ -6,7 +6,7 @@ from webob import Response
from openassessment.assessment.api import self as self_api from openassessment.assessment.api import self as self_api
from openassessment.workflow import api as workflow_api from openassessment.workflow import api as workflow_api
from submissions import api as submission_api from submissions import api as submission_api
from .resolve_dates import DISTANT_FUTURE from .resolve_dates import DISTANT_FUTURE, get_current_time_zone
from .data_conversion import (clean_criterion_feedback, create_submission_dict, from .data_conversion import (clean_criterion_feedback, create_submission_dict,
create_rubric_dict, verify_assessment_parameters) create_rubric_dict, verify_assessment_parameters)
...@@ -51,9 +51,14 @@ class SelfAssessmentMixin(object): ...@@ -51,9 +51,14 @@ class SelfAssessmentMixin(object):
SubmissionError: Error occurred while retrieving the current submission. SubmissionError: Error occurred while retrieving the current submission.
SelfAssessmentRequestError: Error occurred while checking if we had a self-assessment. SelfAssessmentRequestError: Error occurred while checking if we had a self-assessment.
""" """
context = {'allow_latex': self.allow_latex}
path = 'openassessmentblock/self/oa_self_unavailable.html' path = 'openassessmentblock/self/oa_self_unavailable.html'
problem_closed, reason, start_date, due_date = self.is_closed(step="self-assessment") problem_closed, reason, start_date, due_date = self.is_closed(step="self-assessment")
user_service = self.runtime.service(self, 'user')
context = {
'allow_latex': self.allow_latex,
'time_zone': get_current_time_zone(user_service)
}
# We display the due date whether the problem is open or closed. # We display the due date whether the problem is open or closed.
# If no date is set, it defaults to the distant future, in which # If no date is set, it defaults to the distant future, in which
...@@ -121,7 +126,10 @@ class SelfAssessmentMixin(object): ...@@ -121,7 +126,10 @@ class SelfAssessmentMixin(object):
""" """
if self.submission_uuid is None: if self.submission_uuid is None:
return {'success': False, 'msg': self._(u"You must submit a response before you can perform a self-assessment.")} return {
'success': False,
'msg': self._(u"You must submit a response before you can perform a self-assessment.")
}
try: try:
assessment = self_api.create_assessment( assessment = self_api.create_assessment(
......
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(singularText,pluralText,n){if(n>1){return pluralText}else{return singularText}}}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(studentUsername,options){var url=this.url("render_student_info");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:_.extend({student_username:studentUsername},options)}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("This section could not be loaded.")])})}).promise()},staffGradeForm:function(){var url=this.url("render_staff_grade_form");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("The staff assessment form could not be loaded.")])})}).promise()},staffGradeCounts:function(){var url=this.url("render_staff_grade_counts");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("The display of ungraded and checked out responses 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,assessType){return this.submitAssessment("staff_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID,assess_type:assessType})},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])}}).fail(function(){defer.rejectWith(this,[gettext("The submission could not be removed from the grading pool.")])})}).promise()},publishEvent:function(eventName,eventData){eventData.event_name=eventName;var url=this.url("publish_event");var payload=JSON.stringify(eventData);$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType})}}}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(singularText,pluralText,n){if(n>1){return pluralText}else{return singularText}}}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.staffView=new OpenAssessment.StaffView(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);this.usageID=""};if(typeof OpenAssessment.unsavedChanges==="undefined"||!OpenAssessment.unsavedChanges){OpenAssessment.unsavedChanges={}}OpenAssessment.clearUnsavedChanges=function(){OpenAssessment.unsavedChanges={};window.onbeforeunload=null};OpenAssessment.BaseView.prototype={scrollToTop:function(selector){if(!selector){selector="#openassessment__steps"}if($.scrollTo instanceof Function){$(window).scrollTo($(selector,this.element),800,{offset:-50})}},setUpCollapseExpand:function(parentElement){parentElement.on("click",".ui-toggle-visibility__control",function(eventData){var sel=$(eventData.target).closest(".ui-toggle-visibility");sel.toggleClass("is--collapsed")})},getUsageID:function(){if(!this.usageID){this.usageID=$(this.element).data("usage-id")}return this.usageID},load:function(){this.responseView.load();this.loadAssessmentModules();this.staffAreaView.load()},loadAssessmentModules:function(){this.trainingView.load();this.peerView.load();this.staffView.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))},unsavedWarningEnabled:function(enabled,key,message){if(typeof enabled==="undefined"){return window.onbeforeunload!==null}else{var usageID=$(this.element).data("usage-id");if(enabled){if(typeof OpenAssessment.unsavedChanges[usageID]==="undefined"||!OpenAssessment.unsavedChanges[usageID]){OpenAssessment.unsavedChanges[usageID]={}}OpenAssessment.unsavedChanges[usageID][key]=message;window.onbeforeunload=function(){for(var xblockUsageID in OpenAssessment.unsavedChanges){if(OpenAssessment.unsavedChanges.hasOwnProperty(xblockUsageID)){for(var key in OpenAssessment.unsavedChanges[xblockUsageID]){if(OpenAssessment.unsavedChanges[xblockUsageID].hasOwnProperty(key)){return OpenAssessment.unsavedChanges[xblockUsageID][key]}}}}}}else{if(typeof OpenAssessment.unsavedChanges[usageID]!=="undefined"){delete OpenAssessment.unsavedChanges[usageID][key];if($.isEmptyObject(OpenAssessment.unsavedChanges[usageID])){delete OpenAssessment.unsavedChanges[usageID]}if($.isEmptyObject(OpenAssessment.unsavedChanges)){window.onbeforeunload=null}}}}}};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(selector,hidden){selector.toggleClass("is--hidden",hidden);selector.attr("aria-hidden",hidden?"true":"false")},isHidden:function(selector){return selector.hasClass("is--hidden")&&selector.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={UNSAVED_WARNING_KEY:"peer-assessment",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=$(".action--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)}else{this.rubric=null}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(view.peerSubmitEnabled,view));this.rubric.changesExistCallback($.proxy(view.assessmentRubricChanges,view))}sel.find("#peer-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();if(!isContinuedAssessment){view.peerAssess()}else{view.continuedPeerAssess()}});sel.find(".action--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}},assessmentRubricChanges:function(changesExist){if(changesExist){this.baseView.unsavedWarningEnabled(true,this.UNSAVED_WARNING_KEY,gettext("If you leave this page without submitting your peer assessment, you will lose any work you have done."))}},peerAssess:function(){var view=this;var baseView=view.baseView;this.peerAssessRequest(function(){baseView.unsavedWarningEnabled(false,view.UNSAVED_WARNING_KEY);baseView.loadAssessmentModules();baseView.scrollToTop()})},continuedPeerAssess:function(){var view=this;var gradeView=this.baseView.gradeView;var baseView=view.baseView;view.peerAssessRequest(function(){baseView.unsavedWarningEnabled(false,view.UNSAVED_WARNING_KEY);view.loadContinuedAssessment();gradeView.load();baseView.scrollToTop()})},peerAssessRequest:function(successFunction){var view=this;var uuid=this.getUUID();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)})},getUUID:function(){var xBlockElement=$("div[data-usage-id='"+this.baseView.getUsageID()+"']");return xBlockElement.find("#openassessment__peer-assessment").data("submission-uuid")}};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,UNSAVED_WARNING_KEY:"learner-response",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 previewText=sel.find(".submission__answer__part__text__value").val();var previewContainer=sel.find("#preview_content");previewContainer.html(previewText.replace(/\r\n|\r|\n/g,"<br />"));sel.find("#submission__preview__item").show();MathJax.Hub.Queue(["Typeset",MathJax.Hub,previewContainer[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",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)}},response:function(texts){var sel=$(".response__submission .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.baseView.unsavedWarningEnabled(true,this.UNSAVED_WARNING_KEY,gettext("If you leave this page without saving or submitting your response, you will lose any work you have done on the response."))}this.lastChangeTime=Date.now()},save:function(){this.errorOnLastSave=false;this.saveStatus(gettext("Saving..."));this.baseView.toggleActionError("save",null);this.baseView.unsavedWarningEnabled(false,this.UNSAVED_WARNING_KEY);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.baseView.unsavedWarningEnabled(false,this.UNSAVED_WARNING_KEY)},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={};var rubric=this;$(selector,this.element).each(function(index,sel){var criterionName=rubric.getCriterionName(sel); 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(singularText,pluralText,n){if(n>1){return pluralText}else{return singularText}}}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(studentUsername,options){var url=this.url("render_student_info");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:_.extend({student_username:studentUsername},options)}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("This section could not be loaded.")])})}).promise()},staffGradeForm:function(){var url=this.url("render_staff_grade_form");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("The staff assessment form could not be loaded.")])})}).promise()},staffGradeCounts:function(){var url=this.url("render_staff_grade_counts");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("The display of ungraded and checked out responses 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,assessType){return this.submitAssessment("staff_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID,assess_type:assessType})},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])}}).fail(function(){defer.rejectWith(this,[gettext("The submission could not be removed from the grading pool.")])})}).promise()},publishEvent:function(eventName,eventData){eventData.event_name=eventName;var url=this.url("publish_event");var payload=JSON.stringify(eventData);$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType})}}}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(singularText,pluralText,n){if(n>1){return pluralText}else{return singularText}}}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.staffView=new OpenAssessment.StaffView(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);this.usageID=""};if(typeof OpenAssessment.unsavedChanges==="undefined"||!OpenAssessment.unsavedChanges){OpenAssessment.unsavedChanges={}}OpenAssessment.clearUnsavedChanges=function(){OpenAssessment.unsavedChanges={};window.onbeforeunload=null};OpenAssessment.BaseView.prototype={scrollToTop:function(selector){if(!selector){selector="#openassessment__steps"}if($.scrollTo instanceof Function){$(window).scrollTo($(selector,this.element),800,{offset:-50})}},setUpCollapseExpand:function(parentElement){parentElement.on("click",".ui-toggle-visibility__control",function(eventData){var sel=$(eventData.target).closest(".ui-toggle-visibility");sel.toggleClass("is--collapsed")})},getUsageID:function(){if(!this.usageID){this.usageID=$(this.element).data("usage-id")}return this.usageID},load:function(){this.responseView.load();this.loadAssessmentModules();this.staffAreaView.load()},loadAssessmentModules:function(){this.trainingView.load();this.peerView.load();this.staffView.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))},unsavedWarningEnabled:function(enabled,key,message){if(typeof enabled==="undefined"){return window.onbeforeunload!==null}else{var usageID=$(this.element).data("usage-id");if(enabled){if(typeof OpenAssessment.unsavedChanges[usageID]==="undefined"||!OpenAssessment.unsavedChanges[usageID]){OpenAssessment.unsavedChanges[usageID]={}}OpenAssessment.unsavedChanges[usageID][key]=message;window.onbeforeunload=function(){for(var xblockUsageID in OpenAssessment.unsavedChanges){if(OpenAssessment.unsavedChanges.hasOwnProperty(xblockUsageID)){for(var key in OpenAssessment.unsavedChanges[xblockUsageID]){if(OpenAssessment.unsavedChanges[xblockUsageID].hasOwnProperty(key)){return OpenAssessment.unsavedChanges[xblockUsageID][key]}}}}}}else{if(typeof OpenAssessment.unsavedChanges[usageID]!=="undefined"){delete OpenAssessment.unsavedChanges[usageID][key];if($.isEmptyObject(OpenAssessment.unsavedChanges[usageID])){delete OpenAssessment.unsavedChanges[usageID]}if($.isEmptyObject(OpenAssessment.unsavedChanges)){window.onbeforeunload=null}}}}}};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(selector,hidden){selector.toggleClass("is--hidden",hidden);selector.attr("aria-hidden",hidden?"true":"false")},isHidden:function(selector){return selector.hasClass("is--hidden")&&selector.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={UNSAVED_WARNING_KEY:"peer-assessment",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=$(".action--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)}else{this.rubric=null}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(view.peerSubmitEnabled,view));this.rubric.changesExistCallback($.proxy(view.assessmentRubricChanges,view))}sel.find("#peer-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();if(!isContinuedAssessment){view.peerAssess()}else{view.continuedPeerAssess()}});sel.find(".action--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}},assessmentRubricChanges:function(changesExist){if(changesExist){this.baseView.unsavedWarningEnabled(true,this.UNSAVED_WARNING_KEY,gettext("If you leave this page without submitting your peer assessment, you will lose any work you have done."))}},peerAssess:function(){var view=this;var baseView=view.baseView;this.peerAssessRequest(function(){baseView.unsavedWarningEnabled(false,view.UNSAVED_WARNING_KEY);baseView.loadAssessmentModules();baseView.scrollToTop()})},continuedPeerAssess:function(){var view=this;var gradeView=this.baseView.gradeView;var baseView=view.baseView;view.peerAssessRequest(function(){baseView.unsavedWarningEnabled(false,view.UNSAVED_WARNING_KEY);view.loadContinuedAssessment();gradeView.load();baseView.scrollToTop()})},peerAssessRequest:function(successFunction){var view=this;var uuid=this.getUUID();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)})},getUUID:function(){var xBlockElement=$("div[data-usage-id='"+this.baseView.getUsageID()+"']");return xBlockElement.find("#openassessment__peer-assessment").data("submission-uuid")}};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,UNSAVED_WARNING_KEY:"learner-response",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 previewText=sel.find(".submission__answer__part__text__value").val();var previewContainer=sel.find("#preview_content");previewContainer.html(previewText.replace(/\r\n|\r|\n/g,"<br />"));sel.find("#submission__preview__item").show();MathJax.Hub.Queue(["Typeset",MathJax.Hub,previewContainer[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",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)}},response:function(texts){var sel=$(".response__submission .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.baseView.unsavedWarningEnabled(true,this.UNSAVED_WARNING_KEY,gettext("If you leave this page without saving or submitting your response, you will lose any work you have done on the response."))}this.lastChangeTime=Date.now()},save:function(){this.errorOnLastSave=false;this.saveStatus(gettext("Saving..."));this.baseView.toggleActionError("save",null);this.baseView.unsavedWarningEnabled(false,this.UNSAVED_WARNING_KEY);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.baseView.unsavedWarningEnabled(false,this.UNSAVED_WARNING_KEY)},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={};var rubric=this;$(selector,this.element).each(function(index,sel){var criterionName=rubric.getCriterionName(sel);
if(typeof criterionFeedback!=="undefined"){$(sel).val(criterionFeedback[criterionName]);feedback[criterionName]=criterionFeedback[criterionName]}else{feedback[criterionName]=$(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]";var rubric=this;if(typeof optionsSelected==="undefined"){var options={};$(selector+":checked",this.element).each(function(index,sel){options[rubric.getCriterionName(sel)]=sel.value});return options}else{$(selector,this.element).prop("checked",false);$(selector,this.element).each(function(index,sel){var criterionName=rubric.getCriterionName(sel);if(optionsSelected.hasOwnProperty(criterionName)){if(sel.value===optionsSelected[criterionName]){$(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},changesExistCallback:function(callback){var rubric=this;callback(rubric.changesExist());$(this.element).on("change keyup drop paste",function(){callback(rubric.changesExist())})},changesExist:function(){var numChecked=$("input[type=radio]:checked",this.element).length;var textExists=false;$("textarea",this.element).each(function(){var trimmedText=$.trim($(this).val());if(trimmedText!==""){textExists=true}});return numChecked>0||textExists},showCorrections:function(corrections){var selector="input[type=radio]";var hasErrors=false;var rubric=this;$(selector,this.element).each(function(index,sel){var listItem=$(sel).parents(".assessment__rubric__question");if(corrections.hasOwnProperty(rubric.getCriterionName(sel))){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},getCriterionName:function(element){return $(element).data("criterion-name")}};OpenAssessment.SelfView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.SelfView.prototype={UNSAVED_WARNING_KEY:"self-assessment",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)}else{this.rubric=null}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(this.selfSubmitEnabled,this));this.rubric.changesExistCallback($.proxy(this.assessmentRubricChanges,this))}sel.find("#self-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.selfAssess()})},selfSubmitEnabled:function(enabled){var button=$("#self-assessment--001__assessment__submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},assessmentRubricChanges:function(changesExist){if(changesExist){this.baseView.unsavedWarningEnabled(true,this.UNSAVED_WARNING_KEY,gettext("If you leave this page without submitting your self assessment, you will lose any work you have done."))}},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.unsavedWarningEnabled(false,view.UNSAVED_WARNING_KEY);baseView.loadAssessmentModules();baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("self",errMsg);view.selfSubmitEnabled(true)})}};OpenAssessment.StaffView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.StaffView.prototype={load:function(){var view=this;this.server.render("staff_assessment").done(function(html){$("#openassessment__staff-assessment",view.element).replaceWith(html)}).fail(function(){view.baseView.showLoadError("staff-assessment")})}};!function(OpenAssessment){"use strict";OpenAssessment.StaffAreaView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.StaffAreaView.prototype={FULL_GRADE_UNSAVED_WARNING_KEY:"staff-grade",OVERRIDE_UNSAVED_WARNING_KEY:"staff-override",IS_SHOWING_CLASS:"is--showing",SLIDABLE_CLASS:"ui-slidable",SLIDABLE_CONTENT_CLASS:"ui-slidable__content",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(classToExpand){var view=this;var $manageLearnersTab=$(".openassessment__staff-tools",this.element);var $form=$manageLearnersTab.find(".openassessment_student_info_form");var studentUsername=$manageLearnersTab.find(".openassessment__student_username").val();var showFormError=function(errorMessage){$form.find(".form--error").text(errorMessage).focus()};var deferred=$.Deferred();$(".openassessment__student-info",view.element).text("");if(studentUsername.trim()){this.server.studentInfo(studentUsername).done(function(html){showFormError("");$(".openassessment__student-info",view.element).replaceWith(html);$manageLearnersTab.on("click",".action--submit-cancel-submission",function(eventObject){eventObject.preventDefault();view.cancelSubmission($(this).data("submission-uuid"))});var handleChange=function(eventData){view.handleCommentChanged(eventData)};$manageLearnersTab.find(".cancel_submission_comments").on("change keyup drop paste",handleChange);var $rubric=$manageLearnersTab.find(".staff-assessment__assessment");if($rubric.size()>0){var rubricElement=$rubric.get(0);var rubric=new OpenAssessment.Rubric(rubricElement);rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled,view,$manageLearnersTab));rubric.changesExistCallback($.proxy(view.assessmentRubricChanges,view,view.OVERRIDE_UNSAVED_WARNING_KEY));$manageLearnersTab.find(".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.submitStaffOverride(submissionID,rubric,$manageLearnersTab)})}$manageLearnersTab.find("."+view.SLIDABLE_CLASS).click(function(event){var $toggle=$(event.currentTarget),$content=$toggle.next("."+view.SLIDABLE_CONTENT_CLASS);if($toggle.hasClass(view.IS_SHOWING_CLASS)){$toggle.removeClass(view.IS_SHOWING_CLASS).attr("aria-expanded","false");$content.slideUp()}else{$toggle.addClass(view.IS_SHOWING_CLASS).attr("aria-expanded","true");$content.slideDown()}});$manageLearnersTab.find(".staff-info__student__report__summary").focus();if(classToExpand){$manageLearnersTab.find("."+classToExpand+" ."+view.SLIDABLE_CONTENT_CLASS).slideDown();$manageLearnersTab.find("."+classToExpand+" ."+view.SLIDABLE_CLASS).addClass(view.IS_SHOWING_CLASS).attr("aria-expanded","true").focus()}deferred.resolve()}).fail(function(){showFormError(gettext("Unexpected server error."));deferred.reject()})}else{showFormError(gettext("You must provide a learner name."));deferred.reject()}return deferred.promise()},loadStaffGradeForm:function(){var view=this;var $staffGradeTab=$(".openassessment__staff-grading",this.element);var $staffGradeControl=$staffGradeTab.find("."+view.SLIDABLE_CLASS);var $staffGradeContent=$staffGradeTab.find("."+view.SLIDABLE_CONTENT_CLASS);var deferred=$.Deferred();var showFormError=function(errorMessage){$staffGradeTab.find(".staff__grade__form--error").text(errorMessage).focus()};$staffGradeControl.toggleClass(view.IS_SHOWING_CLASS,true).attr("aria-expanded","true");if(this.staffGradeFormLoaded){$staffGradeContent.slideDown();deferred.resolve()}else{this.staffGradeFormLoaded=true;this.server.staffGradeForm().done(function(html){showFormError("");$staffGradeTab.find(".staff__grade__form").replaceWith(html);view.updateStaffGradeCounts();var $rubric=$staffGradeTab.find(".staff-assessment__assessment");if($rubric.size()>0){var rubricElement=$rubric.get(0);var rubric=new OpenAssessment.Rubric(rubricElement);rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled,view,$staffGradeTab));rubric.changesExistCallback($.proxy(view.assessmentRubricChanges,view,view.FULL_GRADE_UNSAVED_WARNING_KEY));$staffGradeTab.find(".wrapper--staff-assessment .action--submit").click(function(eventObject){var submissionID=$staffGradeTab.find(".staff__grade__form").data("submission-uuid");eventObject.preventDefault();view.submitStaffGrade(submissionID,rubric,$staffGradeTab,$(eventObject.currentTarget).hasClass("continue_grading--action"))})}$staffGradeContent.slideDown(function(){$staffGradeControl.focus()});deferred.resolve()}).fail(function(){showFormError(gettext("Unexpected server error."));view.staffGradeFormLoaded=false;deferred.reject()})}return deferred.promise()},closeStaffGradeForm:function(clear){var $staffGradeTab=$(".openassessment__staff-grading",this.element);var $staffGradeControl=$staffGradeTab.find("."+this.SLIDABLE_CLASS);var $staffGradeContent=$staffGradeTab.find("."+this.SLIDABLE_CONTENT_CLASS);$staffGradeControl.toggleClass(this.IS_SHOWING_CLASS,false).attr("aria-expanded","false");if(clear){$staffGradeTab.find(".staff__grade__form").replaceWith('<div class="staff__grade__form"></div>');this.updateStaffGradeCounts()}else{$staffGradeContent.slideUp()}$staffGradeControl.focus()},updateStaffGradeCounts:function(){var view=this;var $staffGradeTab=$(".openassessment__staff-grading",this.element);view.server.staffGradeCounts().done(function(html){$staffGradeTab.find(".staff__grade__status").replaceWith(html)}).fail(function(){$staffGradeTab.find(".staff__grade__status").replaceWith('<span class="staff__grade__status"><span class="staff__grade__value"><span class="copy">'+gettext("Error getting the number of ungraded responses")+"</span></span></span>")})},installHandlers:function(){var view=this;var $staffArea=$(".openassessment__staff-area",this.element);var $manageLearnersTab=$(".openassessment__staff-tools",$staffArea);var $staffGradeTool=$(".openassessment__staff-grading",$staffArea);if($staffArea.length<=0){return}$staffArea.find(".ui-staff__button").click(function(eventObject){$staffArea.find(".ui-staff__button").each(function(index,button){if(button!==eventObject.currentTarget){var $panel=$staffArea.find("."+$(button).data("panel")).first();$panel.slideUp(0)}});var $button=$(eventObject.currentTarget),$panel=$staffArea.find("."+$button.data("panel")).first();if($button.hasClass("is--active")){$button.removeClass("is--active").attr("aria-expanded","false");$panel.slideUp()}else{$staffArea.find(".ui-staff__button").removeClass("is--active").attr("aria-expanded","false");$button.addClass("is--active").attr("aria-expanded","true");$panel.slideDown()}$panel.find(".ui-staff_close_button").focus()});$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").attr("aria-expanded","false");$panel.slideUp();$staffArea.find(".ui-staff__button").each(function(index,button){var $staffPanel=$staffArea.find("."+$(button).data("panel")).first();if($staffPanel[0]===$panel[0]){$(button).focus()}})});$manageLearnersTab.find(".openassessment_student_info_form").submit(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$manageLearnersTab.find(".action--submit-username").click(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$manageLearnersTab.find(".action--submit-training").click(function(eventObject){eventObject.preventDefault();view.scheduleTraining()});$manageLearnersTab.find(".action--submit-unfinished-tasks").click(function(eventObject){eventObject.preventDefault();view.rescheduleUnfinishedTasks()});$staffGradeTool.find(".staff__grade__show-form").click(function(event){var wasShowing=$(event.currentTarget).hasClass(view.IS_SHOWING_CLASS);if(wasShowing){view.closeStaffGradeForm(false)}else{view.loadStaffGradeForm()}})},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("staff-info__student__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(scope,enabled){var button=scope.find(".wrapper--staff-assessment .action--submit");if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},assessmentRubricChanges:function(key,changesExist){if(changesExist){this.baseView.unsavedWarningEnabled(true,key,gettext("If you leave this page without submitting your staff assessment, you will lose any work you have done."))}},submitStaffOverride:function(submissionID,rubric,scope){var view=this;var successCallback=function(){view.baseView.unsavedWarningEnabled(false,view.OVERRIDE_UNSAVED_WARNING_KEY);view.loadStudentInfo("staff-info__student__grade")};this.callStaffAssess(submissionID,rubric,scope,successCallback,".staff-override-error","regrade")},submitStaffGrade:function(submissionID,rubric,scope,continueGrading){var view=this;var successCallback=function(){view.baseView.unsavedWarningEnabled(false,view.FULL_GRADE_UNSAVED_WARNING_KEY);view.staffGradeFormLoaded=false;if(continueGrading){view.loadStaffGradeForm();view.baseView.scrollToTop(".openassessment__staff-area")}else{view.closeStaffGradeForm(true)}};this.callStaffAssess(submissionID,rubric,scope,successCallback,".staff-grade-error","full-grade")},callStaffAssess:function(submissionID,rubric,scope,successCallback,errorSelector,assessType){var view=this;view.staffSubmitEnabled(scope,false);this.server.staffAssess(rubric.optionsSelected(),rubric.criterionFeedback(),rubric.overallFeedback(),submissionID,assessType).done(successCallback).fail(function(errorMessage){scope.find(errorSelector).html(_.escape(errorMessage));view.staffSubmitEnabled(scope,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)}}}; if(typeof criterionFeedback!=="undefined"){$(sel).val(criterionFeedback[criterionName]);feedback[criterionName]=criterionFeedback[criterionName]}else{feedback[criterionName]=$(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]";var rubric=this;if(typeof optionsSelected==="undefined"){var options={};$(selector+":checked",this.element).each(function(index,sel){options[rubric.getCriterionName(sel)]=sel.value});return options}else{$(selector,this.element).prop("checked",false);$(selector,this.element).each(function(index,sel){var criterionName=rubric.getCriterionName(sel);if(optionsSelected.hasOwnProperty(criterionName)){if(sel.value===optionsSelected[criterionName]){$(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},changesExistCallback:function(callback){var rubric=this;callback(rubric.changesExist());$(this.element).on("change keyup drop paste",function(){callback(rubric.changesExist())})},changesExist:function(){var numChecked=$("input[type=radio]:checked",this.element).length;var textExists=false;$("textarea",this.element).each(function(){var trimmedText=$.trim($(this).val());if(trimmedText!==""){textExists=true}});return numChecked>0||textExists},showCorrections:function(corrections){var selector="input[type=radio]";var hasErrors=false;var rubric=this;$(selector,this.element).each(function(index,sel){var listItem=$(sel).parents(".assessment__rubric__question");if(corrections.hasOwnProperty(rubric.getCriterionName(sel))){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},getCriterionName:function(element){return $(element).data("criterion-name")}};OpenAssessment.SelfView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.SelfView.prototype={UNSAVED_WARNING_KEY:"self-assessment",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)}else{this.rubric=null}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(this.selfSubmitEnabled,this));this.rubric.changesExistCallback($.proxy(this.assessmentRubricChanges,this))}sel.find("#self-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.selfAssess()})},selfSubmitEnabled:function(enabled){var button=$("#self-assessment--001__assessment__submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},assessmentRubricChanges:function(changesExist){if(changesExist){this.baseView.unsavedWarningEnabled(true,this.UNSAVED_WARNING_KEY,gettext("If you leave this page without submitting your self assessment, you will lose any work you have done."))}},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.unsavedWarningEnabled(false,view.UNSAVED_WARNING_KEY);baseView.loadAssessmentModules();baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("self",errMsg);view.selfSubmitEnabled(true)})}};OpenAssessment.StaffView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.StaffView.prototype={load:function(){var view=this;this.server.render("staff_assessment").done(function(html){$("#openassessment__staff-assessment",view.element).replaceWith(html)}).fail(function(){view.baseView.showLoadError("staff-assessment")})}};(function(OpenAssessment){"use strict";OpenAssessment.StaffAreaView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.StaffAreaView.prototype={FULL_GRADE_UNSAVED_WARNING_KEY:"staff-grade",OVERRIDE_UNSAVED_WARNING_KEY:"staff-override",IS_SHOWING_CLASS:"is--showing",SLIDABLE_CLASS:"ui-slidable",SLIDABLE_CONTENT_CLASS:"ui-slidable__content",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(classToExpand){var view=this;var $manageLearnersTab=$(".openassessment__staff-tools",this.element);var $form=$manageLearnersTab.find(".openassessment_student_info_form");var studentUsername=$manageLearnersTab.find(".openassessment__student_username").val();var showFormError=function(errorMessage){$form.find(".form--error").text(errorMessage).focus()};var deferred=$.Deferred();$(".openassessment__student-info",view.element).text("");if(studentUsername.trim()){this.server.studentInfo(studentUsername).done(function(html){showFormError("");$(".openassessment__student-info",view.element).replaceWith(html);$manageLearnersTab.on("click",".action--submit-cancel-submission",function(eventObject){eventObject.preventDefault();view.cancelSubmission($(this).data("submission-uuid"))});var handleChange=function(eventData){view.handleCommentChanged(eventData)};$manageLearnersTab.find(".cancel_submission_comments").on("change keyup drop paste",handleChange);var $rubric=$manageLearnersTab.find(".staff-assessment__assessment");if($rubric.size()>0){var rubricElement=$rubric.get(0);var rubric=new OpenAssessment.Rubric(rubricElement);rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled,view,$manageLearnersTab));rubric.changesExistCallback($.proxy(view.assessmentRubricChanges,view,view.OVERRIDE_UNSAVED_WARNING_KEY));$manageLearnersTab.find(".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.submitStaffOverride(submissionID,rubric,$manageLearnersTab)})}$manageLearnersTab.find("."+view.SLIDABLE_CLASS).click(function(event){var $toggle=$(event.currentTarget),$content=$toggle.next("."+view.SLIDABLE_CONTENT_CLASS);if($toggle.hasClass(view.IS_SHOWING_CLASS)){$toggle.removeClass(view.IS_SHOWING_CLASS).attr("aria-expanded","false");$content.slideUp()}else{$toggle.addClass(view.IS_SHOWING_CLASS).attr("aria-expanded","true");$content.slideDown()}});$manageLearnersTab.find(".staff-info__student__report__summary").focus();if(classToExpand){$manageLearnersTab.find("."+classToExpand+" ."+view.SLIDABLE_CONTENT_CLASS).slideDown();$manageLearnersTab.find("."+classToExpand+" ."+view.SLIDABLE_CLASS).addClass(view.IS_SHOWING_CLASS).attr("aria-expanded","true").focus()}deferred.resolve()}).fail(function(){showFormError(gettext("Unexpected server error."));deferred.reject()})}else{showFormError(gettext("You must provide a learner name."));deferred.reject()}return deferred.promise()},loadStaffGradeForm:function(){var view=this;var $staffGradeTab=$(".openassessment__staff-grading",this.element);var $staffGradeControl=$staffGradeTab.find("."+view.SLIDABLE_CLASS);var $staffGradeContent=$staffGradeTab.find("."+view.SLIDABLE_CONTENT_CLASS);var deferred=$.Deferred();var showFormError=function(errorMessage){$staffGradeTab.find(".staff__grade__form--error").text(errorMessage).focus()};$staffGradeControl.toggleClass(view.IS_SHOWING_CLASS,true).attr("aria-expanded","true");if(this.staffGradeFormLoaded){$staffGradeContent.slideDown();deferred.resolve()}else{this.staffGradeFormLoaded=true;this.server.staffGradeForm().done(function(html){showFormError("");$staffGradeTab.find(".staff__grade__form").replaceWith(html);view.updateStaffGradeCounts();var $rubric=$staffGradeTab.find(".staff-assessment__assessment");if($rubric.size()>0){var rubricElement=$rubric.get(0);var rubric=new OpenAssessment.Rubric(rubricElement);rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled,view,$staffGradeTab));rubric.changesExistCallback($.proxy(view.assessmentRubricChanges,view,view.FULL_GRADE_UNSAVED_WARNING_KEY));$staffGradeTab.find(".wrapper--staff-assessment .action--submit").click(function(eventObject){var submissionID=$staffGradeTab.find(".staff__grade__form").data("submission-uuid");eventObject.preventDefault();view.submitStaffGrade(submissionID,rubric,$staffGradeTab,$(eventObject.currentTarget).hasClass("continue_grading--action"))})}$staffGradeContent.slideDown(function(){$staffGradeControl.focus()});deferred.resolve()}).fail(function(){showFormError(gettext("Unexpected server error."));view.staffGradeFormLoaded=false;deferred.reject()})}return deferred.promise()},closeStaffGradeForm:function(clear){var $staffGradeTab=$(".openassessment__staff-grading",this.element);var $staffGradeControl=$staffGradeTab.find("."+this.SLIDABLE_CLASS);var $staffGradeContent=$staffGradeTab.find("."+this.SLIDABLE_CONTENT_CLASS);$staffGradeControl.toggleClass(this.IS_SHOWING_CLASS,false).attr("aria-expanded","false");if(clear){$staffGradeTab.find(".staff__grade__form").replaceWith('<div class="staff__grade__form"></div>');this.updateStaffGradeCounts()}else{$staffGradeContent.slideUp()}$staffGradeControl.focus()},updateStaffGradeCounts:function(){var view=this;var $staffGradeTab=$(".openassessment__staff-grading",this.element);view.server.staffGradeCounts().done(function(html){$staffGradeTab.find(".staff__grade__status").replaceWith(html)}).fail(function(){$staffGradeTab.find(".staff__grade__status").replaceWith('<span class="staff__grade__status"><span class="staff__grade__value"><span class="copy">'+gettext("Error getting the number of ungraded responses")+"</span></span></span>")})},installHandlers:function(){var view=this;var $staffArea=$(".openassessment__staff-area",this.element);var $manageLearnersTab=$(".openassessment__staff-tools",$staffArea);var $staffGradeTool=$(".openassessment__staff-grading",$staffArea);if($staffArea.length<=0){return}$staffArea.find(".ui-staff__button").click(function(eventObject){$staffArea.find(".ui-staff__button").each(function(index,button){if(button!==eventObject.currentTarget){var $panel=$staffArea.find("."+$(button).data("panel")).first();$panel.slideUp(0)}});var $button=$(eventObject.currentTarget),$panel=$staffArea.find("."+$button.data("panel")).first();if($button.hasClass("is--active")){$button.removeClass("is--active").attr("aria-expanded","false");$panel.slideUp()}else{$staffArea.find(".ui-staff__button").removeClass("is--active").attr("aria-expanded","false");$button.addClass("is--active").attr("aria-expanded","true");$panel.slideDown()}$panel.find(".ui-staff_close_button").focus()});$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").attr("aria-expanded","false");$panel.slideUp();$staffArea.find(".ui-staff__button").each(function(index,button){var $staffPanel=$staffArea.find("."+$(button).data("panel")).first();if($staffPanel[0]===$panel[0]){$(button).focus()}})});$manageLearnersTab.find(".openassessment_student_info_form").submit(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$manageLearnersTab.find(".action--submit-username").click(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$manageLearnersTab.find(".action--submit-training").click(function(eventObject){eventObject.preventDefault();view.scheduleTraining()});$manageLearnersTab.find(".action--submit-unfinished-tasks").click(function(eventObject){eventObject.preventDefault();view.rescheduleUnfinishedTasks()});$staffGradeTool.find(".staff__grade__show-form").click(function(event){var wasShowing=$(event.currentTarget).hasClass(view.IS_SHOWING_CLASS);if(wasShowing){view.closeStaffGradeForm(false)}else{view.loadStaffGradeForm()}})},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("staff-info__student__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(scope,enabled){var button=scope.find(".wrapper--staff-assessment .action--submit");if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},assessmentRubricChanges:function(key,changesExist){if(changesExist){this.baseView.unsavedWarningEnabled(true,key,gettext("If you leave this page without submitting your staff assessment, you will lose any work you have done."))}},submitStaffOverride:function(submissionID,rubric,scope){var view=this;var successCallback=function(){view.baseView.unsavedWarningEnabled(false,view.OVERRIDE_UNSAVED_WARNING_KEY);view.loadStudentInfo("staff-info__student__grade")};this.callStaffAssess(submissionID,rubric,scope,successCallback,".staff-override-error","regrade")},submitStaffGrade:function(submissionID,rubric,scope,continueGrading){var view=this;var successCallback=function(){view.baseView.unsavedWarningEnabled(false,view.FULL_GRADE_UNSAVED_WARNING_KEY);view.staffGradeFormLoaded=false;if(continueGrading){view.loadStaffGradeForm();view.baseView.scrollToTop(".openassessment__staff-area")}else{view.closeStaffGradeForm(true)}};this.callStaffAssess(submissionID,rubric,scope,successCallback,".staff-grade-error","full-grade")},callStaffAssess:function(submissionID,rubric,scope,successCallback,errorSelector,assessType){var view=this;view.staffSubmitEnabled(scope,false);this.server.staffAssess(rubric.optionsSelected(),rubric.criterionFeedback(),rubric.overallFeedback(),submissionID,assessType).done(successCallback).fail(function(errorMessage){scope.find(errorSelector).html(_.escape(errorMessage));view.staffSubmitEnabled(scope,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
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -8,7 +8,7 @@ from openassessment.assessment.api import student_training ...@@ -8,7 +8,7 @@ from openassessment.assessment.api import student_training
from openassessment.workflow import api as workflow_api from openassessment.workflow import api as workflow_api
from openassessment.workflow.errors import AssessmentWorkflowError from openassessment.workflow.errors import AssessmentWorkflowError
from openassessment.xblock.data_conversion import convert_training_examples_list_to_dict, create_submission_dict from openassessment.xblock.data_conversion import convert_training_examples_list_to_dict, create_submission_dict
from .resolve_dates import DISTANT_FUTURE from .resolve_dates import DISTANT_FUTURE, get_current_time_zone
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -69,12 +69,14 @@ class StudentTrainingMixin(object): ...@@ -69,12 +69,14 @@ class StudentTrainingMixin(object):
# If no submissions have been created yet, the status will be None. # If no submissions have been created yet, the status will be None.
workflow_status = self.get_workflow_info().get('status') workflow_status = self.get_workflow_info().get('status')
problem_closed, reason, start_date, due_date = self.is_closed(step="student-training") problem_closed, reason, start_date, due_date = self.is_closed(step="student-training")
user_service = self.runtime.service(self, 'user')
context = {} context = {}
template = 'openassessmentblock/student_training/student_training_unavailable.html' template = 'openassessmentblock/student_training/student_training_unavailable.html'
# add allow_latex field to the context # add allow_latex field to the context
context['allow_latex'] = self.allow_latex context['allow_latex'] = self.allow_latex
context['time_zone'] = get_current_time_zone(user_service)
if not workflow_status: if not workflow_status:
return template, context return template, context
......
...@@ -8,7 +8,7 @@ from openassessment.fileupload import api as file_upload_api ...@@ -8,7 +8,7 @@ from openassessment.fileupload import api as file_upload_api
from openassessment.fileupload.exceptions import FileUploadError from openassessment.fileupload.exceptions import FileUploadError
from openassessment.workflow.errors import AssessmentWorkflowError from openassessment.workflow.errors import AssessmentWorkflowError
from .resolve_dates import DISTANT_FUTURE from .resolve_dates import DISTANT_FUTURE, get_current_time_zone
from data_conversion import create_submission_dict, prepare_submission_for_serialization from data_conversion import create_submission_dict, prepare_submission_for_serialization
from validation import validate_submission from validation import validate_submission
...@@ -376,9 +376,12 @@ class SubmissionMixin(object): ...@@ -376,9 +376,12 @@ class SubmissionMixin(object):
""" """
workflow = self.get_workflow_info() workflow = self.get_workflow_info()
problem_closed, reason, start_date, due_date = self.is_closed('submission') problem_closed, reason, start_date, due_date = self.is_closed('submission')
user_service = self.runtime.service(self, 'user')
path = 'openassessmentblock/response/oa_response.html' path = 'openassessmentblock/response/oa_response.html'
context = {} context = {
'time_zone': get_current_time_zone(user_service)
}
# Due dates can default to the distant future, in which case # Due dates can default to the distant future, in which case
# there's effectively no due date. # there's effectively no due date.
......
""" """
Tests the Open Assessment XBlock functionality. Tests the Open Assessment XBlock functionality.
""" """
import ddt
from collections import namedtuple from collections import namedtuple
import datetime as dt import datetime as dt
from freezegun import freeze_time
import pytz import pytz
from mock import Mock, patch, MagicMock, PropertyMock from mock import Mock, patch, MagicMock, PropertyMock
...@@ -12,7 +15,11 @@ from openassessment.workflow.errors import AssessmentWorkflowError ...@@ -12,7 +15,11 @@ from openassessment.workflow.errors import AssessmentWorkflowError
from .base import XBlockHandlerTestCase, scenario from .base import XBlockHandlerTestCase, scenario
@ddt.ddt
class TestOpenAssessment(XBlockHandlerTestCase): class TestOpenAssessment(XBlockHandlerTestCase):
"""Test Open Asessessment Xblock functionality"""
TIME_ZONE_FN_PATH = 'openassessment.xblock.peer_assessment_mixin.get_current_time_zone'
@scenario('data/basic_scenario.xml') @scenario('data/basic_scenario.xml')
def test_load_student_view(self, xblock): def test_load_student_view(self, xblock):
...@@ -23,34 +30,34 @@ class TestOpenAssessment(XBlockHandlerTestCase): ...@@ -23,34 +30,34 @@ class TestOpenAssessment(XBlockHandlerTestCase):
contents. contents.
""" """
xblock_fragment = self.runtime.render(xblock, "student_view") xblock_fragment = self.runtime.render(xblock, "student_view")
self.assertTrue(xblock_fragment.body_html().find("Openassessmentblock")) self.assertIn("OpenAssessmentBlock", xblock_fragment.body_html())
# Validate Submission Rendering. # Validate Submission Rendering.
submission_response = xblock.render_submission({}) submission_response = xblock.render_submission({})
self.assertIsNotNone(submission_response) self.assertIsNotNone(submission_response)
self.assertTrue(submission_response.body.find("openassessment__response")) self.assertIn("openassessment__response", submission_response.body)
# Validate Peer Rendering. # Validate Peer Rendering.
request = namedtuple('Request', 'params') request = namedtuple('Request', 'params')
request.params = {} request.params = {}
peer_response = xblock.render_peer_assessment(request) peer_response = xblock.render_peer_assessment(request)
self.assertIsNotNone(peer_response) self.assertIsNotNone(peer_response)
self.assertTrue(peer_response.body.find("openassessment__peer-assessment")) self.assertIn("openassessment__peer-assessment", peer_response.body)
# Validate Self Rendering. # Validate Self Rendering.
self_response = xblock.render_self_assessment(request) self_response = xblock.render_self_assessment(request)
self.assertIsNotNone(self_response) self.assertIsNotNone(self_response)
self.assertTrue(self_response.body.find("openassessment__peer-assessment")) self.assertIn("openassessment__self-assessment", self_response.body)
# Validate Staff Grade. # Validate Staff Grade.
staff_response = xblock.render_staff_assessment(request) staff_response = xblock.render_staff_assessment(request)
self.assertIsNotNone(self_response) self.assertIsNotNone(self_response)
self.assertTrue(staff_response.body.find("openassessment__staff-assessment")) self.assertIn("openassessment__staff-assessment", staff_response.body)
# Validate Grading. # Validate Grading.
grade_response = xblock.render_grade({}) grade_response = xblock.render_grade({})
self.assertIsNotNone(grade_response) self.assertIsNotNone(grade_response)
self.assertTrue(grade_response.body.find("openassessment__grade")) self.assertIn("openassessment__grade", grade_response.body)
@scenario('data/empty_prompt.xml') @scenario('data/empty_prompt.xml')
def test_prompt_intentionally_empty(self, xblock): def test_prompt_intentionally_empty(self, xblock):
...@@ -91,95 +98,227 @@ class TestOpenAssessment(XBlockHandlerTestCase): ...@@ -91,95 +98,227 @@ class TestOpenAssessment(XBlockHandlerTestCase):
xblock_fragment = self.runtime.render(xblock, "student_view") xblock_fragment = self.runtime.render(xblock, "student_view")
# Expect that the page renders even if the update fails # Expect that the page renders even if the update fails
self.assertTrue(xblock_fragment.body_html().find("Openassessmentblock")) self.assertIn("OpenAssessmentBlock", xblock_fragment.body_html())
@scenario('data/dates_scenario.xml') @ddt.data(('utc', 'April 1, 2014 00:00 UTC'),
def test_load_student_view_with_dates(self, xblock): ('America/Los_Angeles', 'March 31, 2014 17:00 PDT'))
@ddt.unpack
def test_load_student_view_with_dates(self, time_zone, expected_date):
"""OA XBlock returns some HTML to the user. """OA XBlock returns some HTML to the user.
View basic test for verifying we're returned some HTML about the View basic test for verifying we're returned some HTML about the
Open Assessment XBlock. We don't want to match too heavily against the Open Assessment XBlock. We don't want to match too heavily against the
contents. contents.
""" """
xblock_fragment = self.runtime.render(xblock, "student_view") with patch('openassessment.xblock.submission_mixin.get_current_time_zone') as time_zone_fn:
self.assertTrue(xblock_fragment.body_html().find("Openassessmentblock")) time_zone_fn.return_value = pytz.timezone(time_zone)
# Validate Submission Rendering. xblock = self.load_scenario('data/dates_scenario.xml')
submission_response = xblock.render_submission({}) xblock_fragment = self.runtime.render(xblock, "student_view")
self.assertIsNotNone(submission_response) self.assertIn("OpenAssessmentBlock", xblock_fragment.body_html())
self.assertTrue(submission_response.body.find("openassessment__response"))
self.assertTrue(submission_response.body.find("April"))
@scenario('data/basic_scenario.xml')
def test_formatted_dates(self, xblock):
# Set start/due dates # Validate Submission Rendering.
xblock.start = dt.datetime(2014, 4, 1, 1, 1, 1) submission_response = xblock.render_submission({})
xblock.due = dt.datetime(2014, 5, 1) self.assertIsNotNone(submission_response)
self.assertIn("openassessment__response", submission_response.body)
self.assertIn(expected_date, submission_response.body)
request = namedtuple('Request', 'params') def _set_up_start_date(self, start_date):
request.params = {} """
resp = xblock.render_peer_assessment(request) Helper function to set up start date for xblocks
self.assertTrue(resp.body.find('Tuesday, April 01, 2014')) """
self.assertTrue(resp.body.find('Thursday, May 01, 2014')) xblock = self.load_scenario('data/basic_scenario.xml')
xblock.start = start_date
@scenario('data/basic_scenario.xml') return xblock
def test_formatted_dates_for_beta_tester_with_days_early(self, xblock):
"""Test dates for beta tester with days early"""
# Set start/due dates def _set_up_days_early_for_beta(self, xblock, days_early):
xblock.start = dt.datetime(2014, 4, 6, 1, 1, 1) """
xblock.due = dt.datetime(2014, 5, 1) Helper function to set up start date early for beta testers
"""
xblock.xmodule_runtime = Mock( xblock.xmodule_runtime = Mock(
course_id='test_course', course_id='test_course',
anonymous_student_id='test_student', anonymous_student_id='test_student',
days_early_for_beta=5, days_early_for_beta=days_early,
user_is_staff=False, user_is_staff=False,
user_is_beta_tester=True user_is_beta_tester=True
) )
self.assertEqual(xblock.xmodule_runtime.days_early_for_beta, 5)
request = namedtuple('Request', 'params')
request.params = {}
resp = xblock.render_peer_assessment(request)
self.assertTrue(resp.body.find('Tuesday, April 01, 2014'))
self.assertTrue(resp.body.find('Thursday, May 01, 2014'))
@patch.object(openassessmentblock.OpenAssessmentBlock, 'is_beta_tester', new_callable=PropertyMock)
@scenario('data/basic_scenario.xml')
def test_formatted_dates_for_beta_tester_without_days_early(self, xblock, mock_is_beta_tester):
"""Test dates for beta tester without days early"""
mock_is_beta_tester.return_value = True
# Set start/due dates
xblock.start = dt.datetime(2014, 4, 6, 1, 1, 1)
xblock.due = dt.datetime(2014, 5, 1)
request = namedtuple('Request', 'params')
request.params = {}
resp = xblock.render_peer_assessment(request)
self.assertTrue(resp.body.find('Tuesday, April 06, 2014'))
self.assertTrue(resp.body.find('Thursday, May 01, 2014'))
@scenario('data/basic_scenario.xml') def _set_up_end_date(self, end_date):
def test_formatted_dates_for_beta_tester_with_nonetype_days_early(self, xblock): """
"""Test dates for beta tester with NoneType days early""" Helper function to set up end date for xblocks
"""
xblock = self.load_scenario('data/basic_scenario.xml')
xblock.due = end_date
return xblock
# Set start/due dates def _render_xblock(self, xblock):
xblock.start = dt.datetime(2014, 4, 6, 1, 1, 1) """
xblock.due = dt.datetime(2014, 5, 1) Helper function to render xblock
xblock.xmodule_runtime = Mock( """
course_id='test_course',
anonymous_student_id='test_student',
days_early_for_beta=None,
user_is_staff=False,
user_is_beta_tester=True
)
self.assertEqual(xblock.xmodule_runtime.days_early_for_beta, None)
request = namedtuple('Request', 'params') request = namedtuple('Request', 'params')
request.params = {} request.params = {}
resp = xblock.render_peer_assessment(request) return xblock.render_peer_assessment(request)
self.assertTrue(resp.body.find('Tuesday, April 06, 2014'))
self.assertTrue(resp.body.find('Thursday, May 01, 2014')) @ddt.data(('utc', 'April 1, 2014 01:01 UTC'),
('America/Los_Angeles', 'March 31, 2014 18:01 PDT'))
@ddt.unpack
@freeze_time("2014-01-01")
def test_formatted_start_dates(self, time_zone, expected_start_date):
"""Test start dates correctly formatted"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone(time_zone)
xblock = self._set_up_start_date(dt.datetime(2014, 4, 1, 1, 1, 1))
resp = self._render_xblock(xblock)
self.assertIn(expected_start_date, resp.body)
@ddt.data((dt.datetime(2015, 3, 8, 9, 59, 00, tzinfo=pytz.utc), 'March 8, 2015 01:59 PST'),
(dt.datetime(2015, 3, 8, 10, 00, 00, tzinfo=pytz.utc), 'March 8, 2015 03:00 PDT'))
@ddt.unpack
@freeze_time("2014-01-01")
def test_formatted_start_dates_daylight_savings(self, date, expected_start_date):
"""Test start dates correctly formatted for daylight savings time"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone('America/Los_Angeles')
# Set start dates'
xblock = self._set_up_start_date(date)
resp = self._render_xblock(xblock)
self.assertIn(expected_start_date, resp.body)
@ddt.data(('utc', 'May 1, 2014 00:00 UTC'),
('America/Los_Angeles', 'April 30, 2014 17:00 PDT'))
@ddt.unpack
def test_formatted_end_dates(self, time_zone, expected_end_date):
"""Test end dates correctly formatted"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone(time_zone)
# Set due dates'
xblock = self._set_up_end_date(dt.datetime(2014, 5, 1))
resp = self._render_xblock(xblock)
self.assertIn(expected_end_date, resp.body)
@ddt.data((dt.datetime(2015, 3, 8, 9, 59, 00, tzinfo=pytz.utc), 'March 8, 2015 01:59 PST'),
(dt.datetime(2015, 3, 8, 10, 00, 00, tzinfo=pytz.utc), 'March 8, 2015 03:00 PDT'))
@ddt.unpack
def test_formatted_end_dates_daylight_savings(self, date, expected_end_date):
"""Test end dates correctly formatted for daylight savings time"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone('America/Los_Angeles')
# Set due dates'
xblock = self._set_up_end_date(date)
resp = self._render_xblock(xblock)
self.assertIn(expected_end_date, resp.body)
@ddt.data(('utc', 'April 1, 2014 01:01 UTC'),
('America/Los_Angeles', 'March 31, 2014 18:01 PDT'))
@ddt.unpack
@freeze_time("2014-01-01")
def test_formatted_start_dates_for_beta_tester_with_days_early(self, time_zone, expected_start_date):
"""Test start dates for beta tester with days early"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone(time_zone)
# Set start dates
xblock = self._set_up_start_date(dt.datetime(2014, 4, 6, 1, 1, 1))
self._set_up_days_early_for_beta(xblock, 5)
self.assertEqual(xblock.xmodule_runtime.days_early_for_beta, 5)
resp = self._render_xblock(xblock)
self.assertIn(expected_start_date, resp.body)
@ddt.data(('utc', 'May 1, 2014 00:00 UTC'),
('America/Los_Angeles', 'April 30, 2014 17:00 PDT'))
@ddt.unpack
def test_formatted_end_dates_for_beta_tester_with_days_early(self, time_zone, expected_end_date):
"""Test end dates for beta tester with days early"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone(time_zone)
# Set due dates
xblock = self._set_up_start_date(dt.datetime(2014, 4, 6, 1, 1, 1))
xblock.due = dt.datetime(2014, 5, 1)
self._set_up_days_early_for_beta(xblock, 5)
self.assertEqual(xblock.xmodule_runtime.days_early_for_beta, 5)
resp = self._render_xblock(xblock)
self.assertIn(expected_end_date, resp.body)
@ddt.data(('utc', 'April 6, 2014 01:01 UTC'),
('America/Los_Angeles', 'April 5, 2014 18:01 PDT'))
@ddt.unpack
@freeze_time("2014-01-01")
@patch.object(openassessmentblock.OpenAssessmentBlock, 'is_beta_tester', new_callable=PropertyMock)
def test_formatted_start_dates_for_beta_tester_without_days_early(
self,
time_zone,
expected_start_date,
mock_is_beta_tester
):
"""Test start dates for beta tester without days early"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone(time_zone)
mock_is_beta_tester.return_value = True
# Set start dates
xblock = self._set_up_start_date(dt.datetime(2014, 4, 6, 1, 1, 1))
resp = self._render_xblock(xblock)
self.assertIn(expected_start_date, resp.body)
@ddt.data(('utc', 'May 1, 2014 00:00 UTC'),
('America/Los_Angeles', 'April 30, 2014 17:00 PDT'))
@ddt.unpack
@patch.object(openassessmentblock.OpenAssessmentBlock, 'is_beta_tester', new_callable=PropertyMock)
def test_formatted_end_dates_for_beta_tester_without_days_early(
self,
time_zone,
expected_end_date,
mock_is_beta_tester
):
"""Test end dates for beta tester without days early"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone(time_zone)
mock_is_beta_tester.return_value = True
# Set due dates
xblock = self._set_up_end_date(dt.datetime(2014, 5, 1))
resp = self._render_xblock(xblock)
self.assertIn(expected_end_date, resp.body)
@ddt.data(('utc', 'April 6, 2014 01:01 UTC'),
('America/Los_Angeles', 'April 5, 2014 18:01 PDT'))
@ddt.unpack
@freeze_time("2014-01-01")
def test_formatted_start_dates_for_beta_tester_with_nonetype_days_early(self, time_zone, expected_start_date):
"""Test start dates for beta tester with NoneType days early"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone(time_zone)
# Set start dates
xblock = self._set_up_start_date(dt.datetime(2014, 4, 6, 1, 1, 1))
self._set_up_days_early_for_beta(xblock, None)
self.assertEqual(xblock.xmodule_runtime.days_early_for_beta, None)
resp = self._render_xblock(xblock)
self.assertIn(expected_start_date, resp.body)
@ddt.data(('utc', 'May 1, 2014 00:00 UTC'),
('America/Los_Angeles', 'April 30, 2014 17:00 PDT'))
@ddt.unpack
def test_formatted_end_dates_for_beta_tester_with_nonetype_days_early(self, time_zone, expected_end_date):
"""Test end dates for beta tester with NoneType days early"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone(time_zone)
# Set due dates
xblock = self._set_up_end_date(dt.datetime(2014, 5, 1))
self._set_up_days_early_for_beta(xblock, None)
self.assertEqual(xblock.xmodule_runtime.days_early_for_beta, None)
resp = self._render_xblock(xblock)
self.assertIn(expected_end_date, resp.body)
@scenario('data/basic_scenario.xml', user_id='Bob') @scenario('data/basic_scenario.xml', user_id='Bob')
def test_default_fields(self, xblock): def test_default_fields(self, xblock):
......
...@@ -309,6 +309,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -309,6 +309,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'must_grade': 5, 'must_grade': 5,
'review_num': 1, 'review_num': 1,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_unavailable.html', expected_context xblock, 'openassessmentblock/peer/oa_peer_unavailable.html', expected_context
...@@ -324,6 +325,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -324,6 +325,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'must_grade': 5, 'must_grade': 5,
'review_num': 1, 'review_num': 1,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_closed.html', expected_context xblock, 'openassessmentblock/peer/oa_peer_closed.html', expected_context
...@@ -339,6 +341,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -339,6 +341,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'must_grade': 5, 'must_grade': 5,
'review_num': 1, 'review_num': 1,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_unavailable.html', expected_context xblock, 'openassessmentblock/peer/oa_peer_unavailable.html', expected_context
...@@ -357,6 +360,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -357,6 +360,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'review_num': 1, 'review_num': 1,
'submit_button_text': 'submit your assessment & move to response #2', 'submit_button_text': 'submit your assessment & move to response #2',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_waiting.html', xblock, 'openassessmentblock/peer/oa_peer_waiting.html',
...@@ -396,6 +400,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -396,6 +400,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'peer_file_url': '', 'peer_file_url': '',
'submit_button_text': 'submit your assessment & move to response #2', 'submit_button_text': 'submit your assessment & move to response #2',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_assessment.html', xblock, 'openassessmentblock/peer/oa_peer_assessment.html',
...@@ -415,6 +420,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -415,6 +420,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'review_num': 1, 'review_num': 1,
'submit_button_text': 'submit your assessment & move to response #2', 'submit_button_text': 'submit your assessment & move to response #2',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
self._assert_path_and_context( self._assert_path_and_context(
...@@ -438,6 +444,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -438,6 +444,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'review_num': 1, 'review_num': 1,
'submit_button_text': 'submit your assessment & move to response #2', 'submit_button_text': 'submit your assessment & move to response #2',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_closed.html', xblock, 'openassessmentblock/peer/oa_peer_closed.html',
...@@ -473,6 +480,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -473,6 +480,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'review_num': 1, 'review_num': 1,
'submit_button_text': 'submit your assessment & move to response #2', 'submit_button_text': 'submit your assessment & move to response #2',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_closed.html', xblock, 'openassessmentblock/peer/oa_peer_closed.html',
...@@ -500,6 +508,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -500,6 +508,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'must_grade': 5, 'must_grade': 5,
'review_num': 1, 'review_num': 1,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
self._assert_path_and_context( self._assert_path_and_context(
...@@ -532,6 +541,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -532,6 +541,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'rubric_criteria': xblock.rubric_criteria, 'rubric_criteria': xblock.rubric_criteria,
'submit_button_text': 'Submit your assessment & review another response', 'submit_button_text': 'Submit your assessment & review another response',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_turbo_mode_waiting.html', xblock, 'openassessmentblock/peer/oa_peer_turbo_mode_waiting.html',
...@@ -562,6 +572,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -562,6 +572,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'rubric_criteria': xblock.rubric_criteria, 'rubric_criteria': xblock.rubric_criteria,
'submit_button_text': 'Submit your assessment & review another response', 'submit_button_text': 'Submit your assessment & review another response',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_turbo_mode.html', xblock, 'openassessmentblock/peer/oa_peer_turbo_mode.html',
...@@ -583,6 +594,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -583,6 +594,7 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'rubric_criteria': xblock.rubric_criteria, 'rubric_criteria': xblock.rubric_criteria,
'submit_button_text': 'Submit your assessment & review another response', 'submit_button_text': 'Submit your assessment & review another response',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_unavailable.html', xblock, 'openassessmentblock/peer/oa_peer_unavailable.html',
......
...@@ -3,10 +3,12 @@ Test resolving unspecified dates and date strings to datetimes. ...@@ -3,10 +3,12 @@ Test resolving unspecified dates and date strings to datetimes.
""" """
import datetime import datetime
import pytz
from django.test import TestCase from django.test import TestCase
import ddt import ddt
from openassessment.xblock.resolve_dates import resolve_dates, DISTANT_PAST, DISTANT_FUTURE from mock import MagicMock
from openassessment.xblock.resolve_dates import resolve_dates, DISTANT_PAST, DISTANT_FUTURE, get_current_time_zone
import pytz
from workbench.runtime import WorkBenchUserService
STUB_I18N = lambda x: x STUB_I18N = lambda x: x
...@@ -128,3 +130,14 @@ class ResolveDatesTest(TestCase): ...@@ -128,3 +130,14 @@ class ResolveDatesTest(TestCase):
], ],
STUB_I18N STUB_I18N
) )
@ddt.data(({}, pytz.utc),
({'pref-lang': 'en', 'time_zone': 'America/Los_Angeles'}, pytz.timezone('America/Los_Angeles')))
@ddt.unpack
def test_get_current_time_zone(self, user_preferences, expected_time_zone):
"""Verify get_current_time_zone returns correct time zone or UTC"""
user_service = WorkBenchUserService(3)
user_service.get_current_user().opt_attrs['edx-platform.user_preferences'] = user_preferences
time_zone = get_current_time_zone(user_service)
self.assertEqual(expected_time_zone, time_zone)
...@@ -169,7 +169,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -169,7 +169,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
self._assert_path_and_context( self._assert_path_and_context(
xblock, xblock,
'openassessmentblock/self/oa_self_unavailable.html', 'openassessmentblock/self/oa_self_unavailable.html',
{'self_start': datetime.datetime(5999, 1, 1).replace(tzinfo=pytz.utc), 'allow_latex': False} {
'self_start': datetime.datetime(5999, 1, 1).replace(tzinfo=pytz.utc),
'allow_latex': False,
'time_zone': pytz.utc,
}
) )
@scenario('data/self_assessment_closed.xml', user_id='Bob') @scenario('data/self_assessment_closed.xml', user_id='Bob')
...@@ -178,14 +182,22 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -178,14 +182,22 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
self._assert_path_and_context( self._assert_path_and_context(
xblock, xblock,
'openassessmentblock/self/oa_self_closed.html', 'openassessmentblock/self/oa_self_closed.html',
{'self_due': datetime.datetime(2000, 1, 1).replace(tzinfo=pytz.utc), 'allow_latex': False} {
'self_due': datetime.datetime(2000, 1, 1).replace(tzinfo=pytz.utc),
'allow_latex': False,
'time_zone': pytz.utc,
}
) )
@scenario('data/self_assessment_open.xml', user_id='Bob') @scenario('data/self_assessment_open.xml', user_id='Bob')
def test_open_no_submission(self, xblock): def test_open_no_submission(self, xblock):
# Without making a submission, this step should be unavailable # Without making a submission, this step should be unavailable
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/self/oa_self_unavailable.html', {'allow_latex': False} xblock, 'openassessmentblock/self/oa_self_unavailable.html',
{
'allow_latex': False,
'time_zone': pytz.utc,
}
) )
@scenario('data/self_assessment_open.xml', user_id='James Brown') @scenario('data/self_assessment_open.xml', user_id='James Brown')
...@@ -197,7 +209,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -197,7 +209,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
# Should still not be able to access self-assessment # Should still not be able to access self-assessment
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/self/oa_self_unavailable.html', {'allow_latex': False} xblock, 'openassessmentblock/self/oa_self_unavailable.html',
{
'allow_latex': False,
'time_zone': pytz.utc,
}
) )
@scenario('data/self_assessment_open.xml', user_id='James Brown') @scenario('data/self_assessment_open.xml', user_id='James Brown')
...@@ -209,7 +225,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -209,7 +225,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
xblock.get_student_item_dict(), u"𝓟𝓪𝓼𝓼 𝓽𝓱𝓮 𝓹𝓮𝓪𝓼" xblock.get_student_item_dict(), u"𝓟𝓪𝓼𝓼 𝓽𝓱𝓮 𝓹𝓮𝓪𝓼"
) )
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/self/oa_self_complete.html', {'allow_latex': False}, xblock, 'openassessmentblock/self/oa_self_complete.html',
{
'allow_latex': False,
'time_zone': pytz.utc,
},
workflow_status='waiting', workflow_status='waiting',
status_details={ status_details={
'self': {'complete': True}, 'self': {'complete': True},
...@@ -226,7 +246,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -226,7 +246,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
# In the self --> peer configuration, self can be complete # In the self --> peer configuration, self can be complete
# if our status is "peer" # if our status is "peer"
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/self/oa_self_complete.html', {'allow_latex': False}, xblock, 'openassessmentblock/self/oa_self_complete.html',
{
'allow_latex': False,
'time_zone': pytz.utc,
},
workflow_status="peer", workflow_status="peer",
status_details={ status_details={
'self': {'complete': True}, 'self': {'complete': True},
...@@ -241,7 +265,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -241,7 +265,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
xblock.get_student_item_dict(), (u"Ⱥɨn'ŧ ɨŧ fᵾnꝁɏ 1", u"Ⱥɨn'ŧ ɨŧ fᵾnꝁɏ 2") xblock.get_student_item_dict(), (u"Ⱥɨn'ŧ ɨŧ fᵾnꝁɏ 1", u"Ⱥɨn'ŧ ɨŧ fᵾnꝁɏ 2")
) )
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/self/oa_self_complete.html', {'allow_latex': False}, xblock, 'openassessmentblock/self/oa_self_complete.html',
{
'allow_latex': False,
'time_zone': pytz.utc,
},
workflow_status='done' workflow_status='done'
) )
...@@ -252,7 +280,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -252,7 +280,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
xblock.get_student_item_dict(), u"Ⱥɨn'ŧ ɨŧ fᵾnꝁɏ" xblock.get_student_item_dict(), u"Ⱥɨn'ŧ ɨŧ fᵾnꝁɏ"
) )
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/self/oa_self_cancelled.html', {'allow_latex': False}, xblock, 'openassessmentblock/self/oa_self_cancelled.html',
{
'allow_latex': False,
'time_zone': pytz.utc,
},
workflow_status='cancelled' workflow_status='cancelled'
) )
...@@ -270,6 +302,7 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -270,6 +302,7 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
'file_upload_type': None, 'file_upload_type': None,
'self_file_url': '', 'self_file_url': '',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
}, },
workflow_status='self', workflow_status='self',
submission_uuid=submission['uuid'] submission_uuid=submission['uuid']
...@@ -290,7 +323,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -290,7 +323,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
create_rubric_dict(xblock.prompts, xblock.rubric_criteria) create_rubric_dict(xblock.prompts, xblock.rubric_criteria)
) )
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/self/oa_self_complete.html', {'allow_latex': False}, xblock, 'openassessmentblock/self/oa_self_complete.html',
{
'allow_latex': False,
'time_zone': pytz.utc,
},
workflow_status='self', workflow_status='self',
submission_uuid=submission['uuid'] submission_uuid=submission['uuid']
) )
...@@ -305,7 +342,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -305,7 +342,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
self._assert_path_and_context( self._assert_path_and_context(
xblock, xblock,
'openassessmentblock/self/oa_self_closed.html', 'openassessmentblock/self/oa_self_closed.html',
{'self_due': datetime.datetime(2000, 1, 1).replace(tzinfo=pytz.utc), 'allow_latex': False}, {
'self_due': datetime.datetime(2000, 1, 1).replace(tzinfo=pytz.utc),
'allow_latex': False,
'time_zone': pytz.utc,
},
workflow_status='self', workflow_status='self',
submission_uuid=submission['uuid'] submission_uuid=submission['uuid']
) )
...@@ -332,7 +373,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -332,7 +373,11 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
# we ALWAYS show complete, even if the workflow tells us we're still have status 'self'. # we ALWAYS show complete, even if the workflow tells us we're still have status 'self'.
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/self/oa_self_complete.html', xblock, 'openassessmentblock/self/oa_self_complete.html',
{'self_due': datetime.datetime(2000, 1, 1).replace(tzinfo=pytz.utc), 'allow_latex': False}, {
'self_due': datetime.datetime(2000, 1, 1).replace(tzinfo=pytz.utc),
'allow_latex': False,
'time_zone': pytz.utc,
},
workflow_status='self', workflow_status='self',
submission_uuid=submission['uuid'] submission_uuid=submission['uuid']
) )
......
...@@ -63,6 +63,7 @@ class StudentTrainingAssessTest(StudentTrainingTest): ...@@ -63,6 +63,7 @@ class StudentTrainingAssessTest(StudentTrainingTest):
@ddt.file_data('data/student_training_mixin.json') @ddt.file_data('data/student_training_mixin.json')
def test_correct(self, xblock, data): def test_correct(self, xblock, data):
xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION) xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION)
data["expected_context"]['time_zone'] = pytz.utc
self.assert_path_and_context(xblock, data["expected_template"], data["expected_context"]) self.assert_path_and_context(xblock, data["expected_template"], data["expected_context"])
# Agree with the course author's assessment # Agree with the course author's assessment
...@@ -83,6 +84,7 @@ class StudentTrainingAssessTest(StudentTrainingTest): ...@@ -83,6 +84,7 @@ class StudentTrainingAssessTest(StudentTrainingTest):
@ddt.file_data('data/student_training_mixin.json') @ddt.file_data('data/student_training_mixin.json')
def test_correct_with_error(self, xblock, data): def test_correct_with_error(self, xblock, data):
xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION) xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION)
data["expected_context"]['time_zone'] = pytz.utc
self.assert_path_and_context(xblock, data["expected_template"], data["expected_context"]) self.assert_path_and_context(xblock, data["expected_template"], data["expected_context"])
# Agree with the course author's assessment # Agree with the course author's assessment
...@@ -106,6 +108,7 @@ class StudentTrainingAssessTest(StudentTrainingTest): ...@@ -106,6 +108,7 @@ class StudentTrainingAssessTest(StudentTrainingTest):
@ddt.file_data('data/student_training_mixin.json') @ddt.file_data('data/student_training_mixin.json')
def test_incorrect(self, xblock, data): def test_incorrect(self, xblock, data):
xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION) xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION)
data["expected_context"]['time_zone'] = pytz.utc
self.assert_path_and_context(xblock, data["expected_template"], data["expected_context"]) self.assert_path_and_context(xblock, data["expected_template"], data["expected_context"])
# Disagree with the course author's assessment # Disagree with the course author's assessment
...@@ -128,6 +131,7 @@ class StudentTrainingAssessTest(StudentTrainingTest): ...@@ -128,6 +131,7 @@ class StudentTrainingAssessTest(StudentTrainingTest):
expected_context = data["expected_context"].copy() expected_context = data["expected_context"].copy()
expected_template = data["expected_template"] expected_template = data["expected_template"]
xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION) xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION)
expected_context['time_zone'] = pytz.utc
self.assert_path_and_context(xblock, expected_template, expected_context) self.assert_path_and_context(xblock, expected_template, expected_context)
# Agree with the course author's assessment # Agree with the course author's assessment
...@@ -173,7 +177,10 @@ class StudentTrainingAssessTest(StudentTrainingTest): ...@@ -173,7 +177,10 @@ class StudentTrainingAssessTest(StudentTrainingTest):
# Expect that we were correct # Expect that we were correct
self.assertTrue(resp['success'], msg=resp.get('msg')) self.assertTrue(resp['success'], msg=resp.get('msg'))
self.assertFalse(resp['corrections']) self.assertFalse(resp['corrections'])
expected_context = {"allow_latex": False} expected_context = {
"allow_latex": False,
'time_zone': pytz.utc,
}
expected_template = "openassessmentblock/student_training/student_training_complete.html" expected_template = "openassessmentblock/student_training/student_training_complete.html"
self.assert_path_and_context(xblock, expected_template, expected_context) self.assert_path_and_context(xblock, expected_template, expected_context)
...@@ -203,6 +210,7 @@ class StudentTrainingAssessTest(StudentTrainingTest): ...@@ -203,6 +210,7 @@ class StudentTrainingAssessTest(StudentTrainingTest):
xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION) xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION)
expected_context = data["expected_context"].copy() expected_context = data["expected_context"].copy()
expected_template = data["expected_template"] expected_template = data["expected_template"]
expected_context['time_zone'] = pytz.utc
self.assert_path_and_context(xblock, expected_template, expected_context) self.assert_path_and_context(xblock, expected_template, expected_context)
resp = self.request(xblock, 'training_assess', json.dumps({}), response_format='json') resp = self.request(xblock, 'training_assess', json.dumps({}), response_format='json')
self.assertFalse(resp['success'], msg=resp.get('msg')) self.assertFalse(resp['success'], msg=resp.get('msg'))
...@@ -219,6 +227,7 @@ class StudentTrainingAssessTest(StudentTrainingTest): ...@@ -219,6 +227,7 @@ class StudentTrainingAssessTest(StudentTrainingTest):
xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION) xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION)
expected_context = data["expected_context"].copy() expected_context = data["expected_context"].copy()
expected_template = data["expected_template"] expected_template = data["expected_template"]
expected_context['time_zone'] = pytz.utc
self.assert_path_and_context(xblock, expected_template, expected_context) self.assert_path_and_context(xblock, expected_template, expected_context)
selected_data = { selected_data = {
...@@ -302,6 +311,7 @@ class StudentTrainingRenderTest(StudentTrainingTest): ...@@ -302,6 +311,7 @@ class StudentTrainingRenderTest(StudentTrainingTest):
expected_context = { expected_context = {
'training_due': "2000-01-01T00:00:00+00:00", 'training_due': "2000-01-01T00:00:00+00:00",
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
self.assert_path_and_context(xblock, expected_template, expected_context) self.assert_path_and_context(xblock, expected_template, expected_context)
...@@ -315,6 +325,7 @@ class StudentTrainingRenderTest(StudentTrainingTest): ...@@ -315,6 +325,7 @@ class StudentTrainingRenderTest(StudentTrainingTest):
expected_template = "openassessmentblock/student_training/student_training_cancelled.html" expected_template = "openassessmentblock/student_training/student_training_cancelled.html"
expected_context = { expected_context = {
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
self.assert_path_and_context(xblock, expected_template, expected_context) self.assert_path_and_context(xblock, expected_template, expected_context)
...@@ -333,5 +344,6 @@ class StudentTrainingRenderTest(StudentTrainingTest): ...@@ -333,5 +344,6 @@ class StudentTrainingRenderTest(StudentTrainingTest):
expected_context = { expected_context = {
'training_start': datetime.datetime(3000, 1, 1).replace(tzinfo=pytz.utc), 'training_start': datetime.datetime(3000, 1, 1).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
self.assert_path_and_context(xblock, expected_template, expected_context) self.assert_path_and_context(xblock, expected_template, expected_context)
...@@ -182,6 +182,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -182,6 +182,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'file_upload_type': None, 'file_upload_type': None,
'submission_start': dt.datetime(4999, 4, 1).replace(tzinfo=pytz.utc), 'submission_start': dt.datetime(4999, 4, 1).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
) )
...@@ -204,6 +205,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -204,6 +205,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'peer_incomplete': True, 'peer_incomplete': True,
'self_incomplete': True, 'self_incomplete': True,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
) )
...@@ -222,6 +224,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -222,6 +224,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'submit_enabled': False, 'submit_enabled': False,
'submission_due': dt.datetime(2999, 5, 6).replace(tzinfo=pytz.utc), 'submission_due': dt.datetime(2999, 5, 6).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
) )
...@@ -239,6 +242,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -239,6 +242,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'save_status': 'This response has not been saved.', 'save_status': 'This response has not been saved.',
'submit_enabled': False, 'submit_enabled': False,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
) )
...@@ -262,6 +266,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -262,6 +266,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'submit_enabled': True, 'submit_enabled': True,
'submission_due': dt.datetime(2999, 5, 6).replace(tzinfo=pytz.utc), 'submission_due': dt.datetime(2999, 5, 6).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
) )
...@@ -285,6 +290,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -285,6 +290,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'submit_enabled': True, 'submit_enabled': True,
'submission_due': dt.datetime(2999, 5, 6).replace(tzinfo=pytz.utc), 'submission_due': dt.datetime(2999, 5, 6).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
) )
...@@ -303,6 +309,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -303,6 +309,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'peer_incomplete': True, 'peer_incomplete': True,
'self_incomplete': True, 'self_incomplete': True,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
) )
...@@ -333,7 +340,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -333,7 +340,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'cancelled_at': xblock.get_workflow_cancellation_info(submission['uuid']).get('cancelled_at'), 'cancelled_at': xblock.get_workflow_cancellation_info(submission['uuid']).get('cancelled_at'),
'cancelled_by_id': 'Bob', 'cancelled_by_id': 'Bob',
'cancelled_by': mock_staff 'cancelled_by': mock_staff
} },
'time_zone': pytz.utc,
} }
) )
...@@ -359,6 +367,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -359,6 +367,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'peer_incomplete': True, 'peer_incomplete': True,
'self_incomplete': True, 'self_incomplete': True,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
) )
...@@ -370,6 +379,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -370,6 +379,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'file_upload_type': None, 'file_upload_type': None,
'submission_due': dt.datetime(2014, 4, 5).replace(tzinfo=pytz.utc), 'submission_due': dt.datetime(2014, 4, 5).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
) )
...@@ -388,6 +398,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -388,6 +398,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'peer_incomplete': False, 'peer_incomplete': False,
'self_incomplete': True, 'self_incomplete': True,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
) )
...@@ -412,6 +423,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -412,6 +423,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'student_submission': create_submission_dict(submission, xblock.prompts), 'student_submission': create_submission_dict(submission, xblock.prompts),
'file_upload_type': None, 'file_upload_type': None,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
) )
...@@ -436,6 +448,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -436,6 +448,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'student_submission': create_submission_dict(submission, xblock.prompts), 'student_submission': create_submission_dict(submission, xblock.prompts),
'file_upload_type': None, 'file_upload_type': None,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc,
} }
) )
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
coverage==4.0.2 coverage==4.0.2
django-nose==1.4.1 django-nose==1.4.1
freezegun==0.1.11
mock==1.0.1 mock==1.0.1
moto==0.3.1 moto==0.3.1
pep8==1.7.0 pep8==1.7.0
......
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