Commit ee0c25a9 by cahrens Committed by Andy Armstrong

Show Staff Grade section to the learner.

TNL-3464
parent 50b39be9
{% load i18n %}
{% spaceless %}
<li id="openassessment__grade" class="openassessment__steps__step step--grade is--complete">
<header class="step__header ui-toggle-visibility__control">
<h2 class="step__title">
<span class="wrapper--copy">
<span class="step__label">{% trans "Your Grade" %}: </span>
<span class="grade__value">
<span class="grade__value__title">{% trans "Incomplete" %}</span>
<p class="grade__value__description">{% trans "You have not completed all the steps of this problem." %}</p>
</span>
</span>
</h2>
</header>
</li>
{% endspaceless %}
......@@ -22,7 +22,7 @@
<h2 class="step__title">
<span class="step__counter"></span>
<span class="wrapper--copy">
<span class="step__label">{{ assessment.title }}</span>
<span class="step__label">{% trans assessment.title %}</span>
</span>
</h2>
......
{% load i18n %}
<li id="openassessment__staff-assessment" class="openassessment__steps__step step--staff-assessment {{ step_classes }}">
<header class="step__header">
<h2 class="step__title">
<span class="step__counter"></span>
<span class="wrapper--copy">
<span class="step__label">{% trans "Staff Grade" %} </span>
</span>
</h2>
<span class="step__status">
<span class="step__status__label">{% trans "This step's status" %}:</span>
<span class="step__status__value">
{% if icon_class %}
<i class="icon fa {{ icon_class }}" aria-hidden="true"></i>
{% endif %}
<span class="copy">{{ status_value }}</span>
</span>
</span>
</header>
{% if message_title %}
<div class="wrapper--step__content">
<div class="step__message message message--incomplete">
<h3 class="message__title">{{ message_title }}</h3>
<div class="message__content">
<p>{{ message_content }}</p>
</div>
</div>
</div>
{% endif %}
</li>
......@@ -342,11 +342,9 @@ def _get_workflow_model(submission_uuid):
raise AssessmentWorkflowRequestError("submission_uuid must be a string type")
try:
workflow = AssessmentWorkflow.objects.get(submission_uuid=submission_uuid)
except AssessmentWorkflow.DoesNotExist:
raise AssessmentWorkflowNotFoundError(
u"No assessment workflow matching submission_uuid {}".format(submission_uuid)
)
workflow = AssessmentWorkflow.get_by_submission_uuid(submission_uuid)
except AssessmentWorkflowError as exc:
raise AssessmentWorkflowInternalError(repr(exc))
except Exception as exc:
# Something very unexpected has just happened (like DB misconfig)
err_msg = (
......@@ -356,6 +354,11 @@ def _get_workflow_model(submission_uuid):
logger.exception(err_msg)
raise AssessmentWorkflowInternalError(err_msg)
if workflow is None:
raise AssessmentWorkflowNotFoundError(
u"No assessment workflow matching submission_uuid {}".format(submission_uuid)
)
return workflow
......
......@@ -574,10 +574,10 @@ class AssessmentWorkflow(TimeStampedModel, StatusModel):
return cls.objects.get(submission_uuid=submission_uuid)
except cls.DoesNotExist:
return None
except DatabaseError:
error_message = u"Error finding workflow for submission UUID {}.".format(submission_uuid)
logger.exception(error_message)
raise AssessmentWorkflowError(error_message)
except DatabaseError as exc:
message = u"Error finding workflow for submission UUID {} due to error: {}.".format(submission_uuid, exc)
logger.exception(message)
raise AssessmentWorkflowError(message)
@property
def is_cancelled(self):
......
......@@ -46,43 +46,36 @@ UI_MODELS = {
"submission": {
"name": "submission",
"class_id": "openassessment__response",
"navigation_text": "Your response to this assignment",
"title": "Your Response"
},
"student-training": {
"name": "student-training",
"class_id": "openassessment__student-training",
"navigation_text": "Learn to assess responses",
"title": "Learn to Assess"
},
"peer-assessment": {
"name": "peer-assessment",
"class_id": "openassessment__peer-assessment",
"navigation_text": "Your assessment(s) of peer responses",
"title": "Assess Peers' Responses"
},
"self-assessment": {
"name": "self-assessment",
"class_id": "openassessment__self-assessment",
"navigation_text": "Your assessment of your response",
"title": "Assess Your Response"
},
"staff-assessment": {
"name": "staff-assessment",
"class_id": "openassessment__staff-assessment",
"navigation_text": "Staff assessment of your response",
"title": "Staff Assessment"
"title": "Staff Grade"
},
"grade": {
"name": "grade",
"class_id": "openassessment__grade",
"navigation_text": "Your grade for this assignment",
"title": "Your Grade:"
},
"leaderboard": {
"name": "leaderboard",
"class_id": "openassessment__leaderboard",
"navigation_text": "A leaderboard of the top submissions",
"title": "Leaderboard"
}
}
......@@ -455,15 +448,20 @@ class OpenAssessmentBlock(
"""
ui_models = [UI_MODELS["submission"]]
staff_assessment_required = False
for assessment in self.valid_assessments:
if assessment["name"] == "staff-assessment" and assessment["required"] == False:
# If we don't have a staff grade, and it's not required, hide
# this UI model.
if not self.staff_assessment_exists(self.submission_uuid):
if assessment["name"] == "staff-assessment":
if not assessment["required"]:
continue
else:
staff_assessment_required = True
ui_model = UI_MODELS.get(assessment["name"])
if ui_model:
ui_models.append(dict(assessment, **ui_model))
if not staff_assessment_required and self.staff_assessment_exists(self.submission_uuid):
ui_models.append(UI_MODELS["staff-assessment"])
ui_models.append(UI_MODELS["grade"])
if self.leaderboard_show > 0:
......
......@@ -13,7 +13,7 @@ from openassessment.assessment.errors import (
)
from .data_conversion import create_rubric_dict
from .data_conversion import clean_criterion_feedback, create_submission_dict, verify_assessment_parameters
from .data_conversion import clean_criterion_feedback, verify_assessment_parameters
logger = logging.getLogger(__name__)
......@@ -69,3 +69,64 @@ class StaffAssessmentMixin(object):
return {'success': False, 'msg': msg}
else:
return {'success': True, 'msg': u""}
@XBlock.handler
def render_staff_assessment(self, data, suffix=''):
"""
Renders the Staff Assessment HTML section of the XBlock
Generates the staff assessment HTML for the Open
Assessment XBlock. See OpenAssessmentBlock.render_assessment() for
more information on rendering XBlock sections.
Args:
data (dict):
"""
path, context_dict = self.staff_path_and_context()
return self.render_assessment(path, context_dict)
def staff_path_and_context(self):
"""
Retrieve the correct template path and template context for the handler to render.
"""
workflow = self.get_workflow_info()
status = workflow.get('status')
path = 'openassessmentblock/staff/oa_staff_grade.html'
not_available_context = {
'status_value': self._('Not Available'),
'step_classes': 'is--unavailable is--empty is--collapsed',
}
if status == 'cancelled':
context = {
'status_value': self._('Cancelled'),
'icon_class': 'fa-exclamation-triangle',
}
elif status == 'done': # Staff grade exists and all steps completed.
context = {
'status_value': self._('Complete'),
'icon_class': 'fa-check',
'step_classes': 'is--complete is--empty is--collapsed',
}
elif status == 'waiting':
# If we are in the 'waiting' workflow, this means that a staff grade cannot exist
# (because if a staff grade did exist, we would be in 'done' regardless of whether other
# peers have assessed). Therefore we show that we are waiting on staff to provide a grade.
context = {
'status_value': self._('Not Available'),
'message_title': self._('Waiting for a Staff Grade'),
'message_content': self._('Check back later to see if a course staff member has assessed your response. You will receive your grade after the assessment is complete.'),
}
elif status is None: # not started
context = not_available_context
else: # status is 'self' or 'peer', indicating that the student still has work to do.
if self.staff_assessment_exists(self.submission_uuid):
context = {
'status_value': self._('Complete'),
'icon_class': 'fa-check',
'message_title': self._('You Must Complete the Above Steps to View Your Grade'),
'message_content': self._('Although a course staff member has assessed your response, you will receive your grade only after you have completed all the required steps of this problem.'),
}
else: # Both student and staff still have work to do, just show "Not Available".
context = not_available_context
return path, context
if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}if(typeof window.ngetgext==="undefined"){window.ngettext=function(singular_text,plural_text,n){if(n>1){return plural_text}else{return singular_text}}}if(typeof window.Logger==="undefined"){window.Logger={log:function(){}}}if(typeof window.MathJax==="undefined"){window.MathJax={Hub:{Typeset:function(){},Queue:function(){}}}}if(typeof OpenAssessment.Server==="undefined"||!OpenAssessment.Server){OpenAssessment.Server=function(runtime,element){this.runtime=runtime;this.element=element};var jsonContentType="application/json; charset=utf-8";OpenAssessment.Server.prototype={url:function(handler){return this.runtime.handlerUrl(this.element,handler)},render:function(component){var view=this;var url=this.url("render_"+component);return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(view,[data])}).fail(function(){defer.rejectWith(view,[gettext("This section could not be loaded.")])})}).promise()},renderLatex:function(element){element.filter(".allow--latex").each(function(){MathJax.Hub.Queue(["Typeset",MathJax.Hub,this])})},renderContinuedPeer:function(){var view=this;var url=this.url("render_peer_assessment");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:{continue_grading:true}}).done(function(data){defer.resolveWith(view,[data])}).fail(function(){defer.rejectWith(view,[gettext("This section could not be loaded.")])})}).promise()},studentInfo:function(student_username,options){var url=this.url("render_student_info");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:_.extend({student_username:student_username},options)}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("This section could not be loaded.")])})}).promise()},submit:function(submission){var url=this.url("submit");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission}),contentType:jsonContentType}).done(function(data){var success=data[0];if(success){var studentId=data[1];var attemptNum=data[2];defer.resolveWith(this,[studentId,attemptNum])}else{var errorNum=data[1];var errorMsg=data[2];defer.rejectWith(this,[errorNum,errorMsg])}}).fail(function(){defer.rejectWith(this,["AJAX",gettext("This response could not be submitted.")])})}).promise()},save:function(submission){var url=this.url("save_submission");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This response could not be saved.")])})}).promise()},submitFeedbackOnAssessment:function(text,options){var url=this.url("submit_feedback");var payload=JSON.stringify({feedback_text:text,feedback_options:options});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This feedback could not be submitted.")])})}).promise()},submitAssessment:function(assessmentType,payload){var url=this.url(assessmentType);return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify(payload),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})}).promise()},peerAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID){return this.submitAssessment("peer_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID})},selfAssess:function(optionsSelected,criterionFeedback,overallFeedback){return this.submitAssessment("self_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback})},staffAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID){return this.submitAssessment("staff_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID})},trainingAssess:function(optionsSelected){var url=this.url("training_assess");var payload=JSON.stringify({options_selected:optionsSelected});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.corrections])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},scheduleTraining:function(){var url=this.url("schedule_training");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""',contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},rescheduleUnfinishedTasks:function(){var url=this.url("reschedule_unfinished_tasks");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""',contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("One or more rescheduling tasks failed.")])})})},updateEditorContext:function(options){var url=this.url("update_editor_context");var payload=JSON.stringify({prompts:options.prompts,feedback_prompt:options.feedbackPrompt,feedback_default_text:options.feedback_default_text,title:options.title,submission_start:options.submissionStart,submission_due:options.submissionDue,criteria:options.criteria,assessments:options.assessments,editor_assessments_order:options.editorAssessmentsOrder,file_upload_type:options.fileUploadType,white_listed_file_types:options.fileTypeWhiteList,allow_latex:options.latexEnabled,leaderboard_show:options.leaderboardNum});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This problem could not be saved.")])})}).promise()},checkReleased:function(){var url=this.url("check_released");var payload='""';return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.is_released])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("The server could not be contacted.")])})}).promise()},getUploadUrl:function(contentType,filename){var url=this.url("upload_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({contentType:contentType,filename:filename}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve(data.url)}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve upload url.")])})}).promise()},getDownloadUrl:function(){var url=this.url("download_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve(data.url)}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve download url.")])})}).promise()},cancelSubmission:function(submissionID,comments){var url=this.url("cancel_submission");var payload=JSON.stringify({submission_uuid:submissionID,comments:comments});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("The submission could not be removed from the grading pool.")])})}).promise()}}}if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}if(typeof window.ngetgext==="undefined"){window.ngettext=function(singular_text,plural_text,n){if(n>1){return plural_text}else{return singular_text}}}if(typeof window.Logger==="undefined"){window.Logger={log:function(){}}}if(typeof window.MathJax==="undefined"){window.MathJax={Hub:{Typeset:function(){},Queue:function(){}}}}OpenAssessment.BaseView=function(runtime,element,server,data){this.runtime=runtime;this.element=element;this.server=server;this.fileUploader=new OpenAssessment.FileUploader;this.responseView=new OpenAssessment.ResponseView(this.element,this.server,this.fileUploader,this,data);this.trainingView=new OpenAssessment.StudentTrainingView(this.element,this.server,this);this.selfView=new OpenAssessment.SelfView(this.element,this.server,this);this.peerView=new OpenAssessment.PeerView(this.element,this.server,this);this.gradeView=new OpenAssessment.GradeView(this.element,this.server,this);this.leaderboardView=new OpenAssessment.LeaderboardView(this.element,this.server,this);this.messageView=new OpenAssessment.MessageView(this.element,this.server,this);this.staffAreaView=new OpenAssessment.StaffAreaView(this.element,this.server,this)};OpenAssessment.BaseView.prototype={scrollToTop:function(){if($.scrollTo instanceof Function){$(window).scrollTo($("#openassessment__steps",this.element),800,{offset:-50})}},setUpCollapseExpand:function(parentSel){parentSel.on("click",".ui-toggle-visibility__control",function(eventData){var sel=$(eventData.target).closest(".ui-toggle-visibility");sel.toggleClass("is--collapsed")})},load:function(){this.responseView.load();this.loadAssessmentModules();this.staffAreaView.load()},loadAssessmentModules:function(){this.trainingView.load();this.peerView.load();this.selfView.load();this.gradeView.load();this.leaderboardView.load()},loadMessageView:function(){this.messageView.load()},toggleActionError:function(type,message){var element=this.element;var container=null;if(type==="save"){container=".response__submission__actions"}else if(type==="submit"||type==="peer"||type==="self"||type==="student-training"){container=".step__actions"}else if(type==="feedback_assess"){container=".submission__feedback__actions"}else if(type==="upload"){container="#upload__error"}if(container===null){if(message!==null){console.log(message)}}else{$(container+" .message__content",element).html("<p>"+(message?_.escape(message):"")+"</p>");$(container,element).toggleClass("has--error",message!==null)}},showLoadError:function(stepName,errorMessage){if(!errorMessage){errorMessage=gettext("Unable to load")}var $container=$("#openassessment__"+stepName);$container.toggleClass("has--error",true);$container.find(".step__status__value i").removeClass().addClass("icon fa fa-exclamation-triangle");$container.find(".step__status__value .copy").html(_.escape(errorMessage))}};function OpenAssessmentBlock(runtime,element,data){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.BaseView(runtime,element,server,data);view.load()}OpenAssessment.FileUploader=function(){this.upload=function(url,file){return $.Deferred(function(defer){$.ajax({url:url,type:"PUT",data:file,async:false,processData:false,contentType:file.type}).done(function(){Logger.log("openassessment.upload_file",{fileName:file.name,fileSize:file.size,fileType:file.type});defer.resolve()}).fail(function(data,textStatus){defer.rejectWith(this,[textStatus])})}).promise()}};OpenAssessment.GradeView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.GradeView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("grade").done(function(html){$("#openassessment__grade",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__grade",view.element));view.installHandlers()}).fail(function(errMsg){baseView.showLoadError("grade",errMsg)})},installHandlers:function(){var sel=$("#openassessment__grade",this.element);this.baseView.setUpCollapseExpand(sel);var view=this;sel.find("#feedback__submit").click(function(eventObject){eventObject.preventDefault();view.submitFeedbackOnAssessment()})},feedbackText:function(text){if(typeof text==="undefined"){return $("#feedback__remarks__value",this.element).val()}else{$("#feedback__remarks__value",this.element).val(text)}},feedbackOptions:function(options){var view=this;if(typeof options==="undefined"){return $.map($(".feedback__overall__value:checked",view.element),function(element){return $(element).val()})}else{$(".feedback__overall__value",this.element).prop("checked",false);$.each(options,function(index,opt){$("#feedback__overall__value--"+opt,view.element).prop("checked",true)})}},setHidden:function(sel,hidden){sel.toggleClass("is--hidden",hidden);sel.attr("aria-hidden",hidden?"true":"false")},isHidden:function(sel){return sel.hasClass("is--hidden")&&sel.attr("aria-hidden")==="true"},feedbackState:function(newState){var containerSel=$(".submission__feedback__content",this.element);var instructionsSel=containerSel.find(".submission__feedback__instructions");var fieldsSel=containerSel.find(".submission__feedback__fields");var actionsSel=containerSel.find(".submission__feedback__actions");var transitionSel=containerSel.find(".transition__status");var messageSel=containerSel.find(".message--complete");if(typeof newState==="undefined"){var isSubmitting=containerSel.hasClass("is--transitioning")&&containerSel.hasClass("is--submitting")&&!this.isHidden(transitionSel)&&this.isHidden(messageSel)&&this.isHidden(instructionsSel)&&this.isHidden(fieldsSel)&&this.isHidden(actionsSel);var hasSubmitted=containerSel.hasClass("is--submitted")&&this.isHidden(transitionSel)&&!this.isHidden(messageSel)&&this.isHidden(instructionsSel)&&this.isHidden(fieldsSel)&&this.isHidden(actionsSel);var isOpen=!containerSel.hasClass("is--submitted")&&!containerSel.hasClass("is--transitioning")&&!containerSel.hasClass("is--submitting")&&this.isHidden(transitionSel)&&this.isHidden(messageSel)&&!this.isHidden(instructionsSel)&&!this.isHidden(fieldsSel)&&!this.isHidden(actionsSel);if(isOpen){return"open"}else if(isSubmitting){return"submitting"}else if(hasSubmitted){return"submitted"}else{throw"Invalid feedback state"}}else{if(newState==="open"){containerSel.toggleClass("is--transitioning",false);containerSel.toggleClass("is--submitting",false);containerSel.toggleClass("is--submitted",false);this.setHidden(instructionsSel,false);this.setHidden(fieldsSel,false);this.setHidden(actionsSel,false);this.setHidden(transitionSel,true);this.setHidden(messageSel,true)}else if(newState==="submitting"){containerSel.toggleClass("is--transitioning",true);containerSel.toggleClass("is--submitting",true);containerSel.toggleClass("is--submitted",false);this.setHidden(instructionsSel,true);this.setHidden(fieldsSel,true);this.setHidden(actionsSel,true);this.setHidden(transitionSel,false);this.setHidden(messageSel,true)}else if(newState==="submitted"){containerSel.toggleClass("is--transitioning",false);containerSel.toggleClass("is--submitting",false);containerSel.toggleClass("is--submitted",true);this.setHidden(instructionsSel,true);this.setHidden(fieldsSel,true);this.setHidden(actionsSel,true);this.setHidden(transitionSel,true);this.setHidden(messageSel,false)}}},submitFeedbackOnAssessment:function(){var view=this;var baseView=this.baseView;$("#feedback__submit",this.element).toggleClass("is--disabled",true);view.feedbackState("submitting");this.server.submitFeedbackOnAssessment(this.feedbackText(),this.feedbackOptions()).done(function(){view.feedbackState("submitted")}).fail(function(errMsg){baseView.toggleActionError("feedback_assess",errMsg)})}};OpenAssessment.LeaderboardView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.LeaderboardView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("leaderboard").done(function(html){$("#openassessment__leaderboard",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__leaderboard",view.element))}).fail(function(errMsg){baseView.showLoadError("leaderboard",errMsg)})}};OpenAssessment.MessageView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.MessageView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("message").done(function(html){$("#openassessment__message",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__message",view.element))}).fail(function(errMsg){baseView.showLoadError("message",errMsg)})}};OpenAssessment.PeerView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.PeerView.prototype={load:function(){var view=this;this.server.render("peer_assessment").done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__peer-assessment",view.element));view.installHandlers(false)}).fail(function(){view.baseView.showLoadError("peer-assessment")});view.baseView.loadMessageView()},loadContinuedAssessment:function(){var view=this;view.continueAssessmentEnabled(false);this.server.renderContinuedPeer().done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__peer-assessment",view.element));view.installHandlers(true)}).fail(function(){view.baseView.showLoadError("peer-assessment");view.continueAssessmentEnabled(true)})},continueAssessmentEnabled:function(enabled){var button=$("#peer-assessment__continue__grading",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled)}},installHandlers:function(isContinuedAssessment){var sel=$("#openassessment__peer-assessment",this.element);var view=this;this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#peer-assessment--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(view.peerSubmitEnabled,view))}sel.find("#peer-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();if(!isContinuedAssessment){view.peerAssess()}else{view.continuedPeerAssess()}});sel.find("#peer-assessment__continue__grading").click(function(eventObject){eventObject.preventDefault();view.loadContinuedAssessment()})},peerSubmitEnabled:function(enabled){var button=$("#peer-assessment--001__assessment__submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},peerAssess:function(){var view=this;var baseView=view.baseView;this.peerAssessRequest(function(){baseView.loadAssessmentModules();baseView.scrollToTop()})},continuedPeerAssess:function(){var view=this;var gradeView=this.baseView.gradeView;var baseView=view.baseView;view.peerAssessRequest(function(){view.loadContinuedAssessment();gradeView.load();baseView.scrollToTop()})},peerAssessRequest:function(successFunction){var view=this;var uuid=$("#openassessment__peer-assessment").data("submission-uuid");view.baseView.toggleActionError("peer",null);view.peerSubmitEnabled(false);this.server.peerAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.rubric.overallFeedback(),uuid).done(successFunction).fail(function(errMsg){view.baseView.toggleActionError("peer",errMsg);view.peerSubmitEnabled(true)})}};OpenAssessment.ResponseView=function(element,server,fileUploader,baseView,data){this.element=element;this.server=server;this.fileUploader=fileUploader;this.baseView=baseView;this.savedResponse=[];this.files=null;this.fileType=null;this.lastChangeTime=Date.now();this.errorOnLastSave=false;this.autoSaveTimerId=null;this.data=data;this.fileUploaded=false};OpenAssessment.ResponseView.prototype={AUTO_SAVE_POLL_INTERVAL:2e3,AUTO_SAVE_WAIT:3e4,MAX_FILE_SIZE:5242880,load:function(){var view=this;this.server.render("submission").done(function(html){$("#openassessment__response",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__response",view.element));view.installHandlers();view.setAutoSaveEnabled(true)}).fail(function(){view.baseView.showLoadError("response")})},installHandlers:function(){var sel=$("#openassessment__response",this.element);var view=this;var uploadType="";if(sel.find(".submission__answer__display__file").length){uploadType=sel.find(".submission__answer__display__file").data("upload-type")}this.baseView.setUpCollapseExpand(sel);this.savedResponse=this.response();var handleChange=function(){view.handleResponseChanged()};sel.find(".submission__answer__part__text__value").on("change keyup drop paste",handleChange);var handlePrepareUpload=function(eventData){view.prepareUpload(eventData.target.files,uploadType)};sel.find("input[type=file]").on("change",handlePrepareUpload);sel.find("#submission__preview__item").hide();sel.find("#step--response__submit").click(function(eventObject){eventObject.preventDefault();view.submit()});sel.find("#submission__save").click(function(eventObject){eventObject.preventDefault();view.save()});sel.find("#submission__preview").click(function(eventObject){eventObject.preventDefault();var preview_text=sel.find(".submission__answer__part__text__value").val();var preview_container=sel.find("#preview_content");preview_container.html(preview_text.replace(/\r\n|\r|\n/g,"<br />"));sel.find("#submission__preview__item").show();MathJax.Hub.Queue(["Typeset",MathJax.Hub,preview_container[0]])});sel.find("#file__upload").click(function(eventObject){eventObject.preventDefault();$(".submission__answer__display__file",view.element).removeClass("is--hidden");view.fileUpload()})},setAutoSaveEnabled:function(enabled){if(enabled){if(this.autoSaveTimerId===null){this.autoSaveTimerId=setInterval($.proxy(this.autoSave,this),this.AUTO_SAVE_POLL_INTERVAL)}}else{if(this.autoSaveTimerId!==null){clearInterval(this.autoSaveTimerId)}}},submitEnabled:function(enabled){var sel=$("#step--response__submit",this.element);if(typeof enabled==="undefined"){return!sel.hasClass("is--disabled")}else{sel.toggleClass("is--disabled",!enabled);return enabled}},saveEnabled:function(enabled){var sel=$("#submission__save",this.element);if(typeof enabled==="undefined"){return!sel.hasClass("is--disabled")}else{sel.toggleClass("is--disabled",!enabled)}},previewEnabled:function(enabled){var sel=$("#submission__preview",this.element);if(typeof enabled==="undefined"){return!sel.hasClass("is--disabled")}else{sel.toggleClass("is--disabled",!enabled)}},saveStatus:function(msg){var sel=$("#response__save_status h3",this.element);if(typeof msg==="undefined"){return sel.text()}else{var label=gettext("Status of Your Response");sel.html('<span class="sr">'+_.escape(label)+":"+"</span>\n"+msg)}},unsavedWarningEnabled:function(enabled){if(typeof enabled==="undefined"){return window.onbeforeunload!==null}else{if(enabled){window.onbeforeunload=function(){return gettext("If you leave this page without saving or submitting your response, you'll lose any work you've done on the response.")}}else{window.onbeforeunload=null}}},response:function(texts){var sel=$(".submission__answer__part__text__value",this.element);if(typeof texts==="undefined"){return sel.map(function(){return $.trim($(this).val())}).get()}else{sel.map(function(index){$(this).val(texts[index])})}},responseChanged:function(){var savedResponse=this.savedResponse;return this.response().some(function(element,index){return element!==savedResponse[index]})},autoSave:function(){var timeSinceLastChange=Date.now()-this.lastChangeTime;if(this.responseChanged()&&timeSinceLastChange>this.AUTO_SAVE_WAIT&&!this.errorOnLastSave){this.save()}},handleResponseChanged:function(){var isNotBlank=!this.response().every(function(element){return $.trim(element)===""});this.submitEnabled(isNotBlank);if(this.responseChanged()){this.saveEnabled(isNotBlank);this.previewEnabled(isNotBlank);this.saveStatus(gettext("This response has not been saved."));this.unsavedWarningEnabled(true)}this.lastChangeTime=Date.now()},save:function(){this.errorOnLastSave=false;this.saveStatus(gettext("Saving..."));this.baseView.toggleActionError("save",null);this.unsavedWarningEnabled(false);var view=this;var savedResponse=this.response();this.server.save(savedResponse).done(function(){view.savedResponse=savedResponse;var currentResponse=view.response();var currentResponseIsEmpty=currentResponse.every(function(element){return element===""});view.submitEnabled(!currentResponseIsEmpty);var currentResponseEqualsSaved=currentResponse.every(function(element,index){return element===savedResponse[index]});if(currentResponseEqualsSaved){view.saveEnabled(false);view.saveStatus(gettext("This response has been saved but not submitted."))}}).fail(function(errMsg){view.saveStatus(gettext("Error"));view.baseView.toggleActionError("save",errMsg);view.errorOnLastSave=true})},submit:function(){this.submitEnabled(false);var view=this;var baseView=this.baseView;var fileDefer=$.Deferred();if(view.files!==null&&!view.fileUploaded){var msg=gettext("Do you want to upload your file before submitting?");if(confirm(msg)){fileDefer=view.fileUpload()}else{view.submitEnabled(true);return}}else{fileDefer.resolve()}fileDefer.pipe(function(){return view.confirmSubmission().pipe(function(){var submission=view.response();baseView.toggleActionError("response",null);return view.server.submit(submission)})}).done($.proxy(view.moveToNextStep,view)).fail(function(errCode,errMsg){if(errCode==="ENOMULTI"){view.moveToNextStep()}else{if(errMsg){baseView.toggleActionError("submit",errMsg)}view.submitEnabled(true)}})},moveToNextStep:function(){this.load();this.baseView.loadAssessmentModules();this.unsavedWarningEnabled(false)},confirmSubmission:function(){var msg=gettext("You're about to submit your response for this assignment. After you submit this response, you can't change it or submit a new response.");return $.Deferred(function(defer){if(confirm(msg)){defer.resolve()}else{defer.reject()}})},prepareUpload:function(files,uploadType){this.files=null;this.fileType=files[0].type;var ext=files[0].name.split(".").pop().toLowerCase();if(files[0].size>this.MAX_FILE_SIZE){this.baseView.toggleActionError("upload",gettext("File size must be 5MB or less."))}else if(uploadType==="image"&&this.data.ALLOWED_IMAGE_MIME_TYPES.indexOf(this.fileType)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+"JPG, PNG or GIF")}else if(uploadType==="pdf-and-image"&&this.data.ALLOWED_FILE_MIME_TYPES.indexOf(this.fileType)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+"JPG, PNG, GIF or PDF")}else if(uploadType==="custom"&&this.data.FILE_TYPE_WHITE_LIST.indexOf(ext)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+this.data.FILE_TYPE_WHITE_LIST.join(", "))}else if(this.data.FILE_EXT_BLACK_LIST.indexOf(ext)!==-1){this.baseView.toggleActionError("upload",gettext("File type is not allowed."))}else{this.baseView.toggleActionError("upload",null);this.files=files}$("#file__upload").toggleClass("is--disabled",this.files===null)},fileUpload:function(){var view=this;var fileUpload=$("#file__upload");fileUpload.addClass("is--disabled");var handleError=function(errMsg){view.baseView.toggleActionError("upload",errMsg);fileUpload.removeClass("is--disabled")};return this.server.getUploadUrl(view.fileType,view.files[0].name).done(function(url){var file=view.files[0];view.fileUploader.upload(url,file).done(function(){view.fileUrl();view.baseView.toggleActionError("upload",null);view.fileUploaded=true}).fail(handleError)}).fail(handleError)},fileUrl:function(){var view=this;var file=$("#submission__answer__file",view.element);view.server.getDownloadUrl().done(function(url){if(file.prop("tagName")==="IMG"){file.attr("src",url)}else{file.attr("href",url)}return url})}};OpenAssessment.Rubric=function(element){this.element=element};OpenAssessment.Rubric.prototype={criterionFeedback:function(criterionFeedback){var selector="textarea.answer__value";var feedback={};$(selector,this.element).each(function(index,sel){if(typeof criterionFeedback!=="undefined"){$(sel).val(criterionFeedback[sel.name]);feedback[sel.name]=criterionFeedback[sel.name]}else{feedback[sel.name]=$(sel).val()}});return feedback},overallFeedback:function(overallFeedback){var selector=".assessment__rubric__question--feedback__value";if(typeof overallFeedback==="undefined"){return $(selector,this.element).val()}else{$(selector,this.element).val(overallFeedback)}},optionsSelected:function(optionsSelected){var selector="input[type=radio]";if(typeof optionsSelected==="undefined"){var options={};$(selector+":checked",this.element).each(function(index,sel){options[sel.name]=sel.value});return options}else{$(selector,this.element).prop("checked",false);$(selector,this.element).each(function(index,sel){if(optionsSelected.hasOwnProperty(sel.name)){if(sel.value===optionsSelected[sel.name]){$(sel).prop("checked",true)}}})}},canSubmitCallback:function(callback){var rubric=this;callback(rubric.canSubmit());$(this.element).on("change keyup drop paste",function(){callback(rubric.canSubmit())})},canSubmit:function(){var numChecked=$("input[type=radio]:checked",this.element).length;var numAvailable=$(".field--radio.assessment__rubric__question.has--options",this.element).length;var completedRequiredComments=true;$("textarea[required]",this.element).each(function(){var trimmedText=$.trim($(this).val());if(trimmedText===""){completedRequiredComments=false}});return numChecked===numAvailable&&completedRequiredComments},showCorrections:function(corrections){var selector="input[type=radio]";var hasErrors=false;$(selector,this.element).each(function(index,sel){var listItem=$(sel).parents(".assessment__rubric__question");if(corrections.hasOwnProperty(sel.name)){hasErrors=true;listItem.find(".message--incorrect").removeClass("is--hidden");listItem.find(".message--correct").addClass("is--hidden")}else{listItem.find(".message--correct").removeClass("is--hidden");listItem.find(".message--incorrect").addClass("is--hidden")}});return hasErrors}};OpenAssessment.SelfView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.SelfView.prototype={load:function(){var view=this;this.server.render("self_assessment").done(function(html){$("#openassessment__self-assessment",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__self-assessment",view.element));view.installHandlers()}).fail(function(){view.showLoadError("self-assessment")})},installHandlers:function(){var view=this;var sel=$("#openassessment__self-assessment",view.element);this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#self-assessment--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(this.selfSubmitEnabled,this))}sel.find("#self-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.selfAssess()
})},selfSubmitEnabled:function(enabled){var button=$("#self-assessment--001__assessment__submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},selfAssess:function(){var view=this;var baseView=this.baseView;baseView.toggleActionError("self",null);view.selfSubmitEnabled(false);this.server.selfAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.rubric.overallFeedback()).done(function(){baseView.loadAssessmentModules();baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("self",errMsg);view.selfSubmitEnabled(true)})}};!function(OpenAssessment){"use strict";OpenAssessment.StaffAreaView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.StaffAreaView.prototype={load:function(){var view=this;if($(".openassessment__staff-area",view.element).length>0){this.server.render("staff_area").done(function(html){$(".openassessment__staff-area",view.element).replaceWith(html);view.server.renderLatex($(".openassessment__staff-area",view.element));view.installHandlers()}).fail(function(){view.baseView.showLoadError("staff_area")})}},loadStudentInfo:function(options){var view=this;var $staffTools=$(".openassessment__staff-tools",this.element);var $form=$staffTools.find(".openassessment_student_info_form");var student_username=$staffTools.find(".openassessment__student_username").val();var showFormError=function(errorMessage){$form.find(".form--error").text(errorMessage)};var deferred=$.Deferred();$(".openassessment__student-info",view.element).text("");if(student_username.trim()){this.server.studentInfo(student_username,options).done(function(html){showFormError("");$(".openassessment__student-info",view.element).replaceWith(html);$staffTools.on("click",".action--submit-cancel-submission",function(eventObject){eventObject.preventDefault();view.cancelSubmission($(this).data("submission-uuid"))});var handleChange=function(eventData){view.handleCommentChanged(eventData)};$staffTools.find(".cancel_submission_comments").on("change keyup drop paste",handleChange);var $rubric=$(".staff-assessment__assessment",view.element);if($rubric.size()>0){var rubricElement=$rubric.get(0);var rubric=new OpenAssessment.Rubric(rubricElement);rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled,view));$(".wrapper--staff-assessment .action--submit",view.element).click(function(eventObject){var target=$(eventObject.currentTarget),rootElement=target.closest(".openassessment__student-info"),submissionID=rootElement.data("submission-uuid");eventObject.preventDefault();view.submitStaffAssessment(submissionID,rubric)})}deferred.resolve()}).fail(function(){showFormError(gettext("Unexpected server error."));deferred.reject()})}else{showFormError(gettext("A learner name must be provided."));deferred.reject()}return deferred.promise()},installHandlers:function(){var view=this;var $staffArea=$(".openassessment__staff-area",this.element);var $staffTools=$(".openassessment__staff-tools",$staffArea);var $staffInfo=$(".openassessment__student-info",$staffArea);if($staffArea.length<=0){return}this.baseView.setUpCollapseExpand($staffTools,function(){});this.baseView.setUpCollapseExpand($staffInfo,function(){});$staffArea.find(".ui-staff__button").click(function(eventObject){var $button=$(eventObject.currentTarget),panelClass=$button.data("panel"),$panel=$staffArea.find("."+panelClass).first();if($button.hasClass("is--active")){$button.removeClass("is--active");$panel.addClass("is--hidden")}else{$staffArea.find(".ui-staff__button").removeClass("is--active");$button.addClass("is--active");$staffArea.find(".wrapper--ui-staff").addClass("is--hidden");$panel.removeClass("is--hidden")}});$staffArea.find(".ui-staff_close_button").click(function(eventObject){var $button=$(eventObject.currentTarget),$panel=$button.closest(".wrapper--ui-staff");$staffArea.find(".ui-staff__button").removeClass("is--active");$panel.addClass("is--hidden")});$staffTools.find(".openassessment_student_info_form").submit(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$staffTools.find(".action--submit-username").click(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$staffTools.find(".action--submit-training").click(function(eventObject){eventObject.preventDefault();view.scheduleTraining()});$staffTools.find(".action--submit-unfinished-tasks").click(function(eventObject){eventObject.preventDefault();view.rescheduleUnfinishedTasks()})},scheduleTraining:function(){var view=this;this.server.scheduleTraining().done(function(msg){$(".schedule_training_message",view.element).text(msg)}).fail(function(errMsg){$(".schedule_training_message",view.element).text(errMsg)})},rescheduleUnfinishedTasks:function(){var view=this;this.server.rescheduleUnfinishedTasks().done(function(msg){$(".reschedule_unfinished_tasks_message",view.element).text(msg)}).fail(function(errMsg){$(".reschedule_unfinished_tasks_message",view.element).text(errMsg)})},cancelSubmission:function(submissionUUID){this.cancelSubmissionEnabled(false);var view=this;var comments=$(".cancel_submission_comments",this.element).val();this.server.cancelSubmission(submissionUUID,comments).done(function(){view.loadStudentInfo({expanded_view:"final-grade"})}).fail(function(errorMessage){$(".cancel-submission-error").html(_.escape(errorMessage))})},cancelSubmissionEnabled:function(enabled){var $cancelButton=$(".action--submit-cancel-submission",this.element);if(typeof enabled==="undefined"){return!$cancelButton.hasClass("is--disabled")}else{$cancelButton.toggleClass("is--disabled",!enabled)}},comment:function(text){var $submissionComments=$(".cancel_submission_comments",this.element);if(typeof text==="undefined"){return $submissionComments.val()}else{$submissionComments.val(text)}},handleCommentChanged:function(){var isBlank=$.trim(this.comment())!=="";this.cancelSubmissionEnabled(isBlank)},staffSubmitEnabled:function(enabled){var button=$(".wrapper--staff-assessment .action--submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},submitStaffAssessment:function(submissionID,rubric){var view=this;var baseView=this.baseView;baseView.toggleActionError("staff",null);view.staffSubmitEnabled(false);this.server.staffAssess(rubric.optionsSelected(),rubric.criterionFeedback(),rubric.overallFeedback(),submissionID).done(function(){view.loadStudentInfo({expanded_view:"final-grade"})}).fail(function(errorMessage){$(".staff-override-error").html(_.escape(errorMessage));view.staffSubmitEnabled(true)})}}}(OpenAssessment);OpenAssessment.StudentTrainingView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.StudentTrainingView.prototype={load:function(){var view=this;this.server.render("student_training").done(function(html){$("#openassessment__student-training",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__student-training",view.element));view.installHandlers()}).fail(function(){view.baseView.showLoadError("student-training")})},installHandlers:function(){var sel=$("#openassessment__student-training",this.element);var view=this;this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#student-training--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(this.assessButtonEnabled,this))}sel.find("#student-training--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.assess()})},assess:function(){this.assessButtonEnabled(false);var options={};if(this.rubric!==null){options=this.rubric.optionsSelected()}var view=this;var baseView=this.baseView;this.server.trainingAssess(options).done(function(corrections){var incorrect=$("#openassessment__student-training--incorrect",view.element);var instructions=$("#openassessment__student-training--instructions",view.element);if(!view.rubric.showCorrections(corrections)){view.load();baseView.loadAssessmentModules();incorrect.addClass("is--hidden");instructions.removeClass("is--hidden")}else{instructions.addClass("is--hidden");incorrect.removeClass("is--hidden")}baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("student-training",errMsg);view.assessButtonEnabled(true)})},assessButtonEnabled:function(isEnabled){var button=$("#student-training--001__assessment__submit",this.element);if(typeof isEnabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!isEnabled)}}};
\ No newline at end of file
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()},submit:function(submission){var url=this.url("submit");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission}),contentType:jsonContentType}).done(function(data){var success=data[0];if(success){var studentId=data[1];var attemptNum=data[2];defer.resolveWith(this,[studentId,attemptNum])}else{var errorNum=data[1];var errorMsg=data[2];defer.rejectWith(this,[errorNum,errorMsg])}}).fail(function(){defer.rejectWith(this,["AJAX",gettext("This response could not be submitted.")])})}).promise()},save:function(submission){var url=this.url("save_submission");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This response could not be saved.")])})}).promise()},submitFeedbackOnAssessment:function(text,options){var url=this.url("submit_feedback");var payload=JSON.stringify({feedback_text:text,feedback_options:options});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This feedback could not be submitted.")])})}).promise()},submitAssessment:function(assessmentType,payload){var url=this.url(assessmentType);return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify(payload),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})}).promise()},peerAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID){return this.submitAssessment("peer_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID})},selfAssess:function(optionsSelected,criterionFeedback,overallFeedback){return this.submitAssessment("self_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback})},staffAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID){return this.submitAssessment("staff_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID})},trainingAssess:function(optionsSelected){var url=this.url("training_assess");var payload=JSON.stringify({options_selected:optionsSelected});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.corrections])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},scheduleTraining:function(){var url=this.url("schedule_training");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""',contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},rescheduleUnfinishedTasks:function(){var url=this.url("reschedule_unfinished_tasks");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""',contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("One or more rescheduling tasks failed.")])})})},updateEditorContext:function(options){var url=this.url("update_editor_context");var payload=JSON.stringify({prompts:options.prompts,feedback_prompt:options.feedbackPrompt,feedback_default_text:options.feedback_default_text,title:options.title,submission_start:options.submissionStart,submission_due:options.submissionDue,criteria:options.criteria,assessments:options.assessments,editor_assessments_order:options.editorAssessmentsOrder,file_upload_type:options.fileUploadType,white_listed_file_types:options.fileTypeWhiteList,allow_latex:options.latexEnabled,leaderboard_show:options.leaderboardNum});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This problem could not be saved.")])})}).promise()},checkReleased:function(){var url=this.url("check_released");var payload='""';return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.is_released])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("The server could not be contacted.")])})}).promise()},getUploadUrl:function(contentType,filename){var url=this.url("upload_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({contentType:contentType,filename:filename}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve(data.url)}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve upload url.")])})}).promise()},getDownloadUrl:function(){var url=this.url("download_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve(data.url)}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve download url.")])})}).promise()},cancelSubmission:function(submissionID,comments){var url=this.url("cancel_submission");var payload=JSON.stringify({submission_uuid:submissionID,comments:comments});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("The submission could not be removed from the grading pool.")])})}).promise()}}}if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}if(typeof window.ngetgext==="undefined"){window.ngettext=function(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)};OpenAssessment.BaseView.prototype={scrollToTop:function(){if($.scrollTo instanceof Function){$(window).scrollTo($("#openassessment__steps",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")})},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))}};function OpenAssessmentBlock(runtime,element,data){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.BaseView(runtime,element,server,data);view.load()}OpenAssessment.FileUploader=function(){this.upload=function(url,file){return $.Deferred(function(defer){$.ajax({url:url,type:"PUT",data:file,async:false,processData:false,contentType:file.type}).done(function(){Logger.log("openassessment.upload_file",{fileName:file.name,fileSize:file.size,fileType:file.type});defer.resolve()}).fail(function(data,textStatus){defer.rejectWith(this,[textStatus])})}).promise()}};OpenAssessment.GradeView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.GradeView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("grade").done(function(html){$("#openassessment__grade",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__grade",view.element));view.installHandlers()}).fail(function(errMsg){baseView.showLoadError("grade",errMsg)})},installHandlers:function(){var sel=$("#openassessment__grade",this.element);this.baseView.setUpCollapseExpand(sel);var view=this;sel.find("#feedback__submit").click(function(eventObject){eventObject.preventDefault();view.submitFeedbackOnAssessment()})},feedbackText:function(text){if(typeof text==="undefined"){return $("#feedback__remarks__value",this.element).val()}else{$("#feedback__remarks__value",this.element).val(text)}},feedbackOptions:function(options){var view=this;if(typeof options==="undefined"){return $.map($(".feedback__overall__value:checked",view.element),function(element){return $(element).val()})}else{$(".feedback__overall__value",this.element).prop("checked",false);$.each(options,function(index,opt){$("#feedback__overall__value--"+opt,view.element).prop("checked",true)})}},setHidden:function(sel,hidden){sel.toggleClass("is--hidden",hidden);sel.attr("aria-hidden",hidden?"true":"false")},isHidden:function(sel){return sel.hasClass("is--hidden")&&sel.attr("aria-hidden")==="true"},feedbackState:function(newState){var containerSel=$(".submission__feedback__content",this.element);var instructionsSel=containerSel.find(".submission__feedback__instructions");var fieldsSel=containerSel.find(".submission__feedback__fields");var actionsSel=containerSel.find(".submission__feedback__actions");var transitionSel=containerSel.find(".transition__status");var messageSel=containerSel.find(".message--complete");if(typeof newState==="undefined"){var isSubmitting=containerSel.hasClass("is--transitioning")&&containerSel.hasClass("is--submitting")&&!this.isHidden(transitionSel)&&this.isHidden(messageSel)&&this.isHidden(instructionsSel)&&this.isHidden(fieldsSel)&&this.isHidden(actionsSel);var hasSubmitted=containerSel.hasClass("is--submitted")&&this.isHidden(transitionSel)&&!this.isHidden(messageSel)&&this.isHidden(instructionsSel)&&this.isHidden(fieldsSel)&&this.isHidden(actionsSel);var isOpen=!containerSel.hasClass("is--submitted")&&!containerSel.hasClass("is--transitioning")&&!containerSel.hasClass("is--submitting")&&this.isHidden(transitionSel)&&this.isHidden(messageSel)&&!this.isHidden(instructionsSel)&&!this.isHidden(fieldsSel)&&!this.isHidden(actionsSel);if(isOpen){return"open"}else if(isSubmitting){return"submitting"}else if(hasSubmitted){return"submitted"}else{throw"Invalid feedback state"}}else{if(newState==="open"){containerSel.toggleClass("is--transitioning",false);containerSel.toggleClass("is--submitting",false);containerSel.toggleClass("is--submitted",false);this.setHidden(instructionsSel,false);this.setHidden(fieldsSel,false);this.setHidden(actionsSel,false);this.setHidden(transitionSel,true);this.setHidden(messageSel,true)}else if(newState==="submitting"){containerSel.toggleClass("is--transitioning",true);containerSel.toggleClass("is--submitting",true);containerSel.toggleClass("is--submitted",false);this.setHidden(instructionsSel,true);this.setHidden(fieldsSel,true);this.setHidden(actionsSel,true);this.setHidden(transitionSel,false);this.setHidden(messageSel,true)}else if(newState==="submitted"){containerSel.toggleClass("is--transitioning",false);containerSel.toggleClass("is--submitting",false);containerSel.toggleClass("is--submitted",true);this.setHidden(instructionsSel,true);this.setHidden(fieldsSel,true);this.setHidden(actionsSel,true);this.setHidden(transitionSel,true);this.setHidden(messageSel,false)}}},submitFeedbackOnAssessment:function(){var view=this;var baseView=this.baseView;$("#feedback__submit",this.element).toggleClass("is--disabled",true);view.feedbackState("submitting");this.server.submitFeedbackOnAssessment(this.feedbackText(),this.feedbackOptions()).done(function(){view.feedbackState("submitted")}).fail(function(errMsg){baseView.toggleActionError("feedback_assess",errMsg)})}};OpenAssessment.LeaderboardView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.LeaderboardView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("leaderboard").done(function(html){$("#openassessment__leaderboard",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__leaderboard",view.element))}).fail(function(errMsg){baseView.showLoadError("leaderboard",errMsg)})}};OpenAssessment.MessageView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.MessageView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("message").done(function(html){$("#openassessment__message",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__message",view.element))}).fail(function(errMsg){baseView.showLoadError("message",errMsg)})}};OpenAssessment.PeerView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.PeerView.prototype={load:function(){var view=this;this.server.render("peer_assessment").done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__peer-assessment",view.element));view.installHandlers(false)}).fail(function(){view.baseView.showLoadError("peer-assessment")});view.baseView.loadMessageView()},loadContinuedAssessment:function(){var view=this;view.continueAssessmentEnabled(false);this.server.renderContinuedPeer().done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__peer-assessment",view.element));view.installHandlers(true)}).fail(function(){view.baseView.showLoadError("peer-assessment");view.continueAssessmentEnabled(true)})},continueAssessmentEnabled:function(enabled){var button=$("#peer-assessment__continue__grading",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled)}},installHandlers:function(isContinuedAssessment){var sel=$("#openassessment__peer-assessment",this.element);var view=this;this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#peer-assessment--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(view.peerSubmitEnabled,view))}sel.find("#peer-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();if(!isContinuedAssessment){view.peerAssess()}else{view.continuedPeerAssess()}});sel.find("#peer-assessment__continue__grading").click(function(eventObject){eventObject.preventDefault();view.loadContinuedAssessment()})},peerSubmitEnabled:function(enabled){var button=$("#peer-assessment--001__assessment__submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},peerAssess:function(){var view=this;var baseView=view.baseView;this.peerAssessRequest(function(){baseView.loadAssessmentModules();baseView.scrollToTop()})},continuedPeerAssess:function(){var view=this;var gradeView=this.baseView.gradeView;var baseView=view.baseView;view.peerAssessRequest(function(){view.loadContinuedAssessment();gradeView.load();baseView.scrollToTop()})},peerAssessRequest:function(successFunction){var view=this;var uuid=$("#openassessment__peer-assessment").data("submission-uuid");view.baseView.toggleActionError("peer",null);view.peerSubmitEnabled(false);this.server.peerAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.rubric.overallFeedback(),uuid).done(successFunction).fail(function(errMsg){view.baseView.toggleActionError("peer",errMsg);view.peerSubmitEnabled(true)})}};OpenAssessment.ResponseView=function(element,server,fileUploader,baseView,data){this.element=element;this.server=server;this.fileUploader=fileUploader;this.baseView=baseView;this.savedResponse=[];this.files=null;this.fileType=null;this.lastChangeTime=Date.now();this.errorOnLastSave=false;this.autoSaveTimerId=null;this.data=data;this.fileUploaded=false};OpenAssessment.ResponseView.prototype={AUTO_SAVE_POLL_INTERVAL:2e3,AUTO_SAVE_WAIT:3e4,MAX_FILE_SIZE:5242880,load:function(){var view=this;this.server.render("submission").done(function(html){$("#openassessment__response",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__response",view.element));view.installHandlers();view.setAutoSaveEnabled(true)}).fail(function(){view.baseView.showLoadError("response")})},installHandlers:function(){var sel=$("#openassessment__response",this.element);var view=this;var uploadType="";if(sel.find(".submission__answer__display__file").length){uploadType=sel.find(".submission__answer__display__file").data("upload-type")}this.baseView.setUpCollapseExpand(sel);this.savedResponse=this.response();var handleChange=function(){view.handleResponseChanged()};sel.find(".submission__answer__part__text__value").on("change keyup drop paste",handleChange);var handlePrepareUpload=function(eventData){view.prepareUpload(eventData.target.files,uploadType)};sel.find("input[type=file]").on("change",handlePrepareUpload);sel.find("#submission__preview__item").hide();sel.find("#step--response__submit").click(function(eventObject){eventObject.preventDefault();view.submit()});sel.find("#submission__save").click(function(eventObject){eventObject.preventDefault();view.save()});sel.find("#submission__preview").click(function(eventObject){eventObject.preventDefault();var 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 h3",this.element);if(typeof msg==="undefined"){return sel.text()}else{var label=gettext("Status of Your Response");sel.html('<span class="sr">'+_.escape(label)+":"+"</span>\n"+msg)}},unsavedWarningEnabled:function(enabled){if(typeof enabled==="undefined"){return window.onbeforeunload!==null}else{if(enabled){window.onbeforeunload=function(){return gettext("If you leave this page without saving or submitting your response, you'll lose any work you've done on the response.")}}else{window.onbeforeunload=null}}},response:function(texts){var sel=$(".submission__answer__part__text__value",this.element);if(typeof texts==="undefined"){return sel.map(function(){return $.trim($(this).val())}).get()}else{sel.map(function(index){$(this).val(texts[index])})}},responseChanged:function(){var savedResponse=this.savedResponse;return this.response().some(function(element,index){return element!==savedResponse[index]})},autoSave:function(){var timeSinceLastChange=Date.now()-this.lastChangeTime;if(this.responseChanged()&&timeSinceLastChange>this.AUTO_SAVE_WAIT&&!this.errorOnLastSave){this.save()}},handleResponseChanged:function(){var isNotBlank=!this.response().every(function(element){return $.trim(element)===""});this.submitEnabled(isNotBlank);if(this.responseChanged()){this.saveEnabled(isNotBlank);this.previewEnabled(isNotBlank);this.saveStatus(gettext("This response has not been saved."));this.unsavedWarningEnabled(true)}this.lastChangeTime=Date.now()},save:function(){this.errorOnLastSave=false;this.saveStatus(gettext("Saving..."));this.baseView.toggleActionError("save",null);this.unsavedWarningEnabled(false);var view=this;var savedResponse=this.response();this.server.save(savedResponse).done(function(){view.savedResponse=savedResponse;var currentResponse=view.response();var currentResponseIsEmpty=currentResponse.every(function(element){return element===""});view.submitEnabled(!currentResponseIsEmpty);var currentResponseEqualsSaved=currentResponse.every(function(element,index){return element===savedResponse[index]});if(currentResponseEqualsSaved){view.saveEnabled(false);view.saveStatus(gettext("This response has been saved but not submitted."))}}).fail(function(errMsg){view.saveStatus(gettext("Error"));view.baseView.toggleActionError("save",errMsg);view.errorOnLastSave=true})},submit:function(){this.submitEnabled(false);var view=this;var baseView=this.baseView;var fileDefer=$.Deferred();if(view.files!==null&&!view.fileUploaded){var msg=gettext("Do you want to upload your file before submitting?");if(confirm(msg)){fileDefer=view.fileUpload()}else{view.submitEnabled(true);return}}else{fileDefer.resolve()}fileDefer.pipe(function(){return view.confirmSubmission().pipe(function(){var submission=view.response();baseView.toggleActionError("response",null);return view.server.submit(submission)})}).done($.proxy(view.moveToNextStep,view)).fail(function(errCode,errMsg){if(errCode==="ENOMULTI"){view.moveToNextStep()}else{if(errMsg){baseView.toggleActionError("submit",errMsg)}view.submitEnabled(true)}})},moveToNextStep:function(){this.load();this.baseView.loadAssessmentModules();this.unsavedWarningEnabled(false)},confirmSubmission:function(){var msg=gettext("You're about to submit your response for this assignment. After you submit this response, you can't change it or submit a new response.");return $.Deferred(function(defer){if(confirm(msg)){defer.resolve()}else{defer.reject()}})},prepareUpload:function(files,uploadType){this.files=null;this.fileType=files[0].type;var ext=files[0].name.split(".").pop().toLowerCase();if(files[0].size>this.MAX_FILE_SIZE){this.baseView.toggleActionError("upload",gettext("File size must be 5MB or less."))}else if(uploadType==="image"&&this.data.ALLOWED_IMAGE_MIME_TYPES.indexOf(this.fileType)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+"JPG, PNG or GIF")}else if(uploadType==="pdf-and-image"&&this.data.ALLOWED_FILE_MIME_TYPES.indexOf(this.fileType)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+"JPG, PNG, GIF or PDF")}else if(uploadType==="custom"&&this.data.FILE_TYPE_WHITE_LIST.indexOf(ext)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+this.data.FILE_TYPE_WHITE_LIST.join(", "))}else if(this.data.FILE_EXT_BLACK_LIST.indexOf(ext)!==-1){this.baseView.toggleActionError("upload",gettext("File type is not allowed."))}else{this.baseView.toggleActionError("upload",null);this.files=files}$("#file__upload").toggleClass("is--disabled",this.files===null)},fileUpload:function(){var view=this;var fileUpload=$("#file__upload");fileUpload.addClass("is--disabled");var handleError=function(errMsg){view.baseView.toggleActionError("upload",errMsg);fileUpload.removeClass("is--disabled")};return this.server.getUploadUrl(view.fileType,view.files[0].name).done(function(url){var file=view.files[0];view.fileUploader.upload(url,file).done(function(){view.fileUrl();view.baseView.toggleActionError("upload",null);view.fileUploaded=true}).fail(handleError)}).fail(handleError)},fileUrl:function(){var view=this;var file=$("#submission__answer__file",view.element);view.server.getDownloadUrl().done(function(url){if(file.prop("tagName")==="IMG"){file.attr("src",url)}else{file.attr("href",url)}return url})}};OpenAssessment.Rubric=function(element){this.element=element};OpenAssessment.Rubric.prototype={criterionFeedback:function(criterionFeedback){var selector="textarea.answer__value";var feedback={};$(selector,this.element).each(function(index,sel){if(typeof criterionFeedback!=="undefined"){$(sel).val(criterionFeedback[sel.name]);feedback[sel.name]=criterionFeedback[sel.name]}else{feedback[sel.name]=$(sel).val()}});return feedback},overallFeedback:function(overallFeedback){var selector=".assessment__rubric__question--feedback__value";if(typeof overallFeedback==="undefined"){return $(selector,this.element).val()}else{$(selector,this.element).val(overallFeedback)}},optionsSelected:function(optionsSelected){var selector="input[type=radio]";if(typeof optionsSelected==="undefined"){var options={};$(selector+":checked",this.element).each(function(index,sel){options[sel.name]=sel.value});return options}else{$(selector,this.element).prop("checked",false);$(selector,this.element).each(function(index,sel){if(optionsSelected.hasOwnProperty(sel.name)){if(sel.value===optionsSelected[sel.name]){$(sel).prop("checked",true)}}})}},canSubmitCallback:function(callback){var rubric=this;callback(rubric.canSubmit());$(this.element).on("change keyup drop paste",function(){callback(rubric.canSubmit())})},canSubmit:function(){var numChecked=$("input[type=radio]:checked",this.element).length;var numAvailable=$(".field--radio.assessment__rubric__question.has--options",this.element).length;var completedRequiredComments=true;$("textarea[required]",this.element).each(function(){var trimmedText=$.trim($(this).val());if(trimmedText===""){completedRequiredComments=false}});return numChecked===numAvailable&&completedRequiredComments},showCorrections:function(corrections){var selector="input[type=radio]";var hasErrors=false;$(selector,this.element).each(function(index,sel){var listItem=$(sel).parents(".assessment__rubric__question");if(corrections.hasOwnProperty(sel.name)){hasErrors=true;listItem.find(".message--incorrect").removeClass("is--hidden");listItem.find(".message--correct").addClass("is--hidden")}else{listItem.find(".message--correct").removeClass("is--hidden");listItem.find(".message--incorrect").addClass("is--hidden")}});return hasErrors}};OpenAssessment.SelfView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.SelfView.prototype={load:function(){var view=this;this.server.render("self_assessment").done(function(html){$("#openassessment__self-assessment",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__self-assessment",view.element));view.installHandlers()}).fail(function(){view.showLoadError("self-assessment")})},installHandlers:function(){var view=this;var sel=$("#openassessment__self-assessment",view.element);this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#self-assessment--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(this.selfSubmitEnabled,this))}sel.find("#self-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();
view.selfAssess()})},selfSubmitEnabled:function(enabled){var button=$("#self-assessment--001__assessment__submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},selfAssess:function(){var view=this;var baseView=this.baseView;baseView.toggleActionError("self",null);view.selfSubmitEnabled(false);this.server.selfAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.rubric.overallFeedback()).done(function(){baseView.loadAssessmentModules();baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("self",errMsg);view.selfSubmitEnabled(true)})}};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={load:function(){var view=this;if($(".openassessment__staff-area",view.element).length>0){this.server.render("staff_area").done(function(html){$(".openassessment__staff-area",view.element).replaceWith(html);view.server.renderLatex($(".openassessment__staff-area",view.element));view.installHandlers()}).fail(function(){view.baseView.showLoadError("staff_area")})}},loadStudentInfo:function(options){var view=this;var $staffTools=$(".openassessment__staff-tools",this.element);var $form=$staffTools.find(".openassessment_student_info_form");var studentUsername=$staffTools.find(".openassessment__student_username").val();var showFormError=function(errorMessage){$form.find(".form--error").text(errorMessage)};var deferred=$.Deferred();$(".openassessment__student-info",view.element).text("");if(studentUsername.trim()){this.server.studentInfo(studentUsername,options).done(function(html){showFormError("");$(".openassessment__student-info",view.element).replaceWith(html);$staffTools.on("click",".action--submit-cancel-submission",function(eventObject){eventObject.preventDefault();view.cancelSubmission($(this).data("submission-uuid"))});var handleChange=function(eventData){view.handleCommentChanged(eventData)};$staffTools.find(".cancel_submission_comments").on("change keyup drop paste",handleChange);var $rubric=$(".staff-assessment__assessment",view.element);if($rubric.size()>0){var rubricElement=$rubric.get(0);var rubric=new OpenAssessment.Rubric(rubricElement);rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled,view));$(".wrapper--staff-assessment .action--submit",view.element).click(function(eventObject){var target=$(eventObject.currentTarget),rootElement=target.closest(".openassessment__student-info"),submissionID=rootElement.data("submission-uuid");eventObject.preventDefault();view.submitStaffAssessment(submissionID,rubric)})}deferred.resolve()}).fail(function(){showFormError(gettext("Unexpected server error."));deferred.reject()})}else{showFormError(gettext("A learner name must be provided."));deferred.reject()}return deferred.promise()},installHandlers:function(){var view=this;var $staffArea=$(".openassessment__staff-area",this.element);var $staffTools=$(".openassessment__staff-tools",$staffArea);var $staffInfo=$(".openassessment__student-info",$staffArea);if($staffArea.length<=0){return}this.baseView.setUpCollapseExpand($staffTools,function(){});this.baseView.setUpCollapseExpand($staffInfo,function(){});$staffArea.find(".ui-staff__button").click(function(eventObject){var $button=$(eventObject.currentTarget),panelClass=$button.data("panel"),$panel=$staffArea.find("."+panelClass).first();if($button.hasClass("is--active")){$button.removeClass("is--active");$panel.addClass("is--hidden")}else{$staffArea.find(".ui-staff__button").removeClass("is--active");$button.addClass("is--active");$staffArea.find(".wrapper--ui-staff").addClass("is--hidden");$panel.removeClass("is--hidden")}});$staffArea.find(".ui-staff_close_button").click(function(eventObject){var $button=$(eventObject.currentTarget),$panel=$button.closest(".wrapper--ui-staff");$staffArea.find(".ui-staff__button").removeClass("is--active");$panel.addClass("is--hidden")});$staffTools.find(".openassessment_student_info_form").submit(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$staffTools.find(".action--submit-username").click(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$staffTools.find(".action--submit-training").click(function(eventObject){eventObject.preventDefault();view.scheduleTraining()});$staffTools.find(".action--submit-unfinished-tasks").click(function(eventObject){eventObject.preventDefault();view.rescheduleUnfinishedTasks()})},scheduleTraining:function(){var view=this;this.server.scheduleTraining().done(function(msg){$(".schedule_training_message",view.element).text(msg)}).fail(function(errMsg){$(".schedule_training_message",view.element).text(errMsg)})},rescheduleUnfinishedTasks:function(){var view=this;this.server.rescheduleUnfinishedTasks().done(function(msg){$(".reschedule_unfinished_tasks_message",view.element).text(msg)}).fail(function(errMsg){$(".reschedule_unfinished_tasks_message",view.element).text(errMsg)})},cancelSubmission:function(submissionUUID){this.cancelSubmissionEnabled(false);var view=this;var comments=$(".cancel_submission_comments",this.element).val();this.server.cancelSubmission(submissionUUID,comments).done(function(){view.loadStudentInfo({expanded_view:"final-grade"})}).fail(function(errorMessage){$(".cancel-submission-error").html(_.escape(errorMessage))})},cancelSubmissionEnabled:function(enabled){var $cancelButton=$(".action--submit-cancel-submission",this.element);if(typeof enabled==="undefined"){return!$cancelButton.hasClass("is--disabled")}else{$cancelButton.toggleClass("is--disabled",!enabled)}},comment:function(text){var $submissionComments=$(".cancel_submission_comments",this.element);if(typeof text==="undefined"){return $submissionComments.val()}else{$submissionComments.val(text)}},handleCommentChanged:function(){var isBlank=$.trim(this.comment())!=="";this.cancelSubmissionEnabled(isBlank)},staffSubmitEnabled:function(enabled){var button=$(".wrapper--staff-assessment .action--submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},submitStaffAssessment:function(submissionID,rubric){var view=this;var baseView=this.baseView;baseView.toggleActionError("staff",null);view.staffSubmitEnabled(false);this.server.staffAssess(rubric.optionsSelected(),rubric.criterionFeedback(),rubric.overallFeedback(),submissionID).done(function(){view.loadStudentInfo({expanded_view:"final-grade"})}).fail(function(errorMessage){$(".staff-override-error").html(_.escape(errorMessage));view.staffSubmitEnabled(true)})}}}(OpenAssessment);OpenAssessment.StudentTrainingView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.StudentTrainingView.prototype={load:function(){var view=this;this.server.render("student_training").done(function(html){$("#openassessment__student-training",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__student-training",view.element));view.installHandlers()}).fail(function(){view.baseView.showLoadError("student-training")})},installHandlers:function(){var sel=$("#openassessment__student-training",this.element);var view=this;this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#student-training--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(this.assessButtonEnabled,this))}sel.find("#student-training--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.assess()})},assess:function(){this.assessButtonEnabled(false);var options={};if(this.rubric!==null){options=this.rubric.optionsSelected()}var view=this;var baseView=this.baseView;this.server.trainingAssess(options).done(function(corrections){var incorrect=$("#openassessment__student-training--incorrect",view.element);var instructions=$("#openassessment__student-training--instructions",view.element);if(!view.rubric.showCorrections(corrections)){view.load();baseView.loadAssessmentModules();incorrect.addClass("is--hidden");instructions.removeClass("is--hidden")}else{instructions.addClass("is--hidden");incorrect.removeClass("is--hidden")}baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("student-training",errMsg);view.assessButtonEnabled(true)})},assessButtonEnabled:function(isEnabled){var button=$("#student-training--001__assessment__submit",this.element);if(typeof isEnabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!isEnabled)}}};
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -64,6 +64,7 @@ describe("OpenAssessment.BaseView", function() {
expect(server.fragmentsLoaded).toContain("student_training");
expect(server.fragmentsLoaded).toContain("self_assessment");
expect(server.fragmentsLoaded).toContain("peer_assessment");
expect(server.fragmentsLoaded).toContain("staff_assessment");
expect(server.fragmentsLoaded).toContain("grade");
});
......@@ -72,7 +73,7 @@ describe("OpenAssessment.BaseView", function() {
view.peerView.peerAssess();
var numPeerLoads = 0;
for (var i = 0; i < server.fragmentsLoaded.length; i++) {
if (server.fragmentsLoaded[i] == 'peer_assessment') {
if (server.fragmentsLoaded[i] === 'peer_assessment') {
numPeerLoads++;
}
}
......
......@@ -20,6 +20,7 @@ OpenAssessment.BaseView = function(runtime, element, server, 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);
......@@ -71,6 +72,7 @@ OpenAssessment.BaseView.prototype = {
loadAssessmentModules: function() {
this.trainingView.load();
this.peerView.load();
this.staffView.load();
this.selfView.load();
this.gradeView.load();
this.leaderboardView.load();
......
/**
* Interface for staff assessment view.
*
* @param {Element} element - The DOM element representing the XBlock.
* @param {OpenAssessment.Server} server - The interface to the XBlock server.
* @param {OpenAssessment.BaseView} baseView - Container view.
*/
OpenAssessment.StaffView = function(element, server, baseView) {
this.element = element;
this.server = server;
this.baseView = baseView;
};
OpenAssessment.StaffView.prototype = {
/**
* Load the staff assessment view.
**/
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');
});
}
};
......@@ -15,12 +15,11 @@ from openassessment.xblock.openassessmentblock import OpenAssessmentBlock
from .base import XBlockHandlerTestCase, scenario
@ddt.ddt
class TestGrade(XBlockHandlerTestCase):
class SubmitAssessmentsMixin(object):
"""
View-level tests for the XBlock grade handlers.
A mixin for creating a submission and peer/self assessments so that the user can
receive a grade. This is useful for getting into the "waiting for peer assessment" state.
"""
PEERS = ['McNulty', 'Moreland']
ASSESSMENTS = [
......@@ -45,6 +44,88 @@ class TestGrade(XBlockHandlerTestCase):
STEPS = ['peer', 'self']
def _create_submission_and_assessments(
self, xblock, submission_text, peers, peer_assessments, self_assessment,
waiting_for_peer=False,
):
"""
Create a submission and peer/self assessments, so that the user can receive a grade.
Args:
xblock (OpenAssessmentBlock): The XBlock, loaded for the user who needs a grade.
submission_text (unicode): Text of the submission from the user.
peers (list of unicode): List of user IDs of peers who will assess the user.
peer_assessments (list of dict): List of assessment dictionaries for peer assessments.
self_assessment (dict): Dict of assessment for self-assessment.
Keyword Arguments:
waiting_for_peer (bool): If true, skip creation of peer assessments for the user's submission.
Returns:
the submission
"""
# Create a submission from the user
student_item = xblock.get_student_item_dict()
student_id = student_item['student_id']
submission = xblock.create_submission(student_item, submission_text)
# Create submissions and assessments from other users
scorer_submissions = []
for scorer_name, assessment in zip(peers, peer_assessments):
# Create a submission for each scorer for the same problem
scorer = copy.deepcopy(student_item)
scorer['student_id'] = scorer_name
scorer_sub = sub_api.create_submission(scorer, {'text': submission_text})
workflow_api.create_workflow(scorer_sub['uuid'], self.STEPS)
submission = peer_api.get_submission_to_assess(scorer_sub['uuid'], len(peers))
# Store the scorer's submission so our user can assess it later
scorer_submissions.append(scorer_sub)
# Create an assessment of the user's submission
if not waiting_for_peer:
peer_api.create_assessment(
scorer_sub['uuid'], scorer_name,
assessment['options_selected'],
assessment['criterion_feedback'],
assessment['overall_feedback'],
{'criteria': xblock.rubric_criteria},
xblock.get_assessment_module('peer-assessment')['must_be_graded_by']
)
# Have our user make assessments (so she can get a score)
for assessment in peer_assessments:
peer_api.get_submission_to_assess(submission['uuid'], len(peers))
peer_api.create_assessment(
submission['uuid'],
student_id,
assessment['options_selected'],
assessment['criterion_feedback'],
assessment['overall_feedback'],
{'criteria': xblock.rubric_criteria},
xblock.get_assessment_module('peer-assessment')['must_be_graded_by']
)
# Have the user submit a self-assessment (so she can get a score)
if self_assessment is not None:
self_api.create_assessment(
submission['uuid'], student_id, self_assessment['options_selected'],
self_assessment['criterion_feedback'], self_assessment['overall_feedback'],
{'criteria': xblock.rubric_criteria}
)
return submission
@ddt.ddt
class TestGrade(XBlockHandlerTestCase, SubmitAssessmentsMixin):
"""
View-level tests for the XBlock grade handlers.
"""
AI_ALGORITHMS = {
'fake': 'openassessment.assessment.worker.algorithm.FakeAIAlgorithm'
}
......@@ -367,77 +448,3 @@ class TestGrade(XBlockHandlerTestCase):
self.assertEqual(part['criterion']['label'], expected_criterion_label)
expected_option_label = option_labels[(part['criterion']['name'], part['option']['name'])]
self.assertEqual(part['option']['label'], expected_option_label)
def _create_submission_and_assessments(
self, xblock, submission_text, peers, peer_assessments, self_assessment,
waiting_for_peer=False,
):
"""
Create a submission and peer/self assessments, so that the user can receive a grade.
Args:
xblock (OpenAssessmentBlock): The XBlock, loaded for the user who needs a grade.
submission_text (unicode): Text of the submission from the user.
peers (list of unicode): List of user IDs of peers who will assess the user.
peer_assessments (list of dict): List of assessment dictionaries for peer assessments.
self_assessment (dict): Dict of assessment for self-assessment.
Keyword Arguments:
waiting_for_peer (bool): If true, skip creation of peer assessments for the user's submission.
Returns:
None
"""
# Create a submission from the user
student_item = xblock.get_student_item_dict()
student_id = student_item['student_id']
submission = xblock.create_submission(student_item, submission_text)
# Create submissions and assessments from other users
scorer_submissions = []
for scorer_name, assessment in zip(peers, peer_assessments):
# Create a submission for each scorer for the same problem
scorer = copy.deepcopy(student_item)
scorer['student_id'] = scorer_name
scorer_sub = sub_api.create_submission(scorer, {'text': submission_text})
workflow_api.create_workflow(scorer_sub['uuid'], self.STEPS)
submission = peer_api.get_submission_to_assess(scorer_sub['uuid'], len(peers))
# Store the scorer's submission so our user can assess it later
scorer_submissions.append(scorer_sub)
# Create an assessment of the user's submission
if not waiting_for_peer:
peer_api.create_assessment(
scorer_sub['uuid'], scorer_name,
assessment['options_selected'],
assessment['criterion_feedback'],
assessment['overall_feedback'],
{'criteria': xblock.rubric_criteria},
xblock.get_assessment_module('peer-assessment')['must_be_graded_by']
)
# Have our user make assessments (so she can get a score)
for asmnt in peer_assessments:
peer_api.get_submission_to_assess(submission['uuid'], len(peers))
peer_api.create_assessment(
submission['uuid'],
student_id,
asmnt['options_selected'],
asmnt['criterion_feedback'],
asmnt['overall_feedback'],
{'criteria': xblock.rubric_criteria},
xblock.get_assessment_module('peer-assessment')['must_be_graded_by']
)
# Have the user submit a self-assessment (so she can get a score)
if self_assessment is not None:
self_api.create_assessment(
submission['uuid'], student_id, self_assessment['options_selected'],
self_assessment['criterion_feedback'], self_assessment['overall_feedback'],
{'criteria': xblock.rubric_criteria}
)
......@@ -43,6 +43,11 @@ class TestOpenAssessment(XBlockHandlerTestCase):
self.assertIsNotNone(self_response)
self.assertTrue(self_response.body.find("openassessment__peer-assessment"))
# Validate Staff Grade.
staff_response = xblock.render_staff_assessment(request)
self.assertIsNotNone(self_response)
self.assertTrue(staff_response.body.find("openassessment__staff-assessment"))
# Validate Grading.
grade_response = xblock.render_grade({})
self.assertIsNotNone(grade_response)
......
......@@ -6,9 +6,8 @@ import json
import mock
import copy
from openassessment.assessment.api import staff as staff_api
from openassessment.xblock.data_conversion import create_rubric_dict
from .base import XBlockHandlerTestCase, scenario
from .test_grade import SubmitAssessmentsMixin
class StaffAssessmentTestBase(XBlockHandlerTestCase):
maxDiff = None
......@@ -25,6 +24,126 @@ class StaffAssessmentTestBase(XBlockHandlerTestCase):
xblock.xmodule_runtime = mock.Mock(user_is_staff=True)
xblock.xmodule_runtime.anonymous_student_id = 'Bob'
def _assert_path_and_context(self, xblock, expected_context):
path, context = xblock.staff_path_and_context()
self.assertEqual('openassessmentblock/staff/oa_staff_grade.html', path)
self.assertItemsEqual(expected_context, context)
# Verify that we render without error
resp = self.request(xblock, 'render_staff_assessment', json.dumps({}))
self.assertGreater(len(resp), 0)
@staticmethod
def _set_mock_workflow_info(xblock, workflow_status, status_details, submission_uuid):
xblock.get_workflow_info = mock.Mock(return_value={
'status': workflow_status,
'status_details': status_details,
'submission_uuid': submission_uuid
})
def _submit_staff_assessment(self, xblock, submission):
# Submit a staff-assessment
self.set_staff_access(xblock)
self.ASSESSMENT['submission_uuid'] = submission['uuid']
resp = self.request(xblock, 'staff_assess', json.dumps(self.ASSESSMENT), response_format='json')
self.assertTrue(resp['success'])
class TestStaffAssessmentRender(StaffAssessmentTestBase, SubmitAssessmentsMixin):
@scenario('data/self_assessment_scenario.xml', user_id='Bob')
def test_staff_grade_templates(self, xblock):
self._verify_grade_templates_workflow(xblock)
@scenario('data/self_assessment_closed.xml', user_id='Bob')
def test_staff_grade_templates_closed(self, xblock):
# Whether or not a problem is closed (past due date) has no impact on Staff Grade section.
self._verify_grade_templates_workflow(xblock)
def _verify_grade_templates_workflow(self, xblock):
unavailable_context = {
'status_value': 'Not Available',
'step_classes': 'is--unavailable is--empty is--collapsed',
}
# Problem not yet started, Staff Grade section is marked "Not Available"
self._assert_path_and_context(xblock, unavailable_context)
# Create a submission for the student
submission = xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION)
# Response has been created, waiting for self assessment (no staff assessment exists either)
self._assert_path_and_context(xblock, unavailable_context)
# Submit a staff-assessment
self._submit_staff_assessment(xblock, submission)
# Staff assessment exists, still waiting for self assessment.
self._assert_path_and_context(
xblock,
{
'status_value': 'Complete',
'icon_class': 'fa-check',
'message_title': 'You Must Complete the Above Steps to View Your Grade',
'message_content': 'Although a course staff member has assessed your response, you will receive your grade only after you have completed all the required steps of this problem.'
}
)
# Verify that once the required step (self assessment) is done, the staff grade is shown as complete.
status_details = {'peer': {'complete': True}}
self._set_mock_workflow_info(
xblock, workflow_status='done', status_details=status_details, submission_uuid=submission['uuid']
)
self._assert_path_and_context(
xblock,
{
'status_value': 'Complete',
'icon_class': 'fa-check',
'step_classes': 'is--complete is--empty is--collapsed',
}
)
# Verify that if the problem is cancelled, the staff grade reflects this.
self._set_mock_workflow_info(
xblock, workflow_status='cancelled', status_details=status_details, submission_uuid=submission['uuid']
)
self._assert_path_and_context(
xblock,
{
'status_value': 'Cancelled',
'icon_class': 'fa-exclamation-triangle',
}
)
@scenario('data/grade_waiting_scenario.xml', user_id='Omar')
def test_staff_grade_templates_no_peer(self, xblock):
# Waiting to be assessed by a peer
submission = self._create_submission_and_assessments(
xblock, self.SUBMISSION, self.PEERS, self.ASSESSMENTS, self.ASSESSMENTS[0], waiting_for_peer=True
)
# Waiting for a peer assessment (though it is not used because staff grading is required),
# no staff grade exists.
self._assert_path_and_context(
xblock,
{
'status_value': 'Not Available',
'message_title': 'Waiting for a Staff Grade',
'message_content': 'Check back later to see if a course staff member has assessed your response. You will receive your grade after the assessment is complete.',
}
)
# Submit a staff-assessment. The student can now see the score even though no peer assessments have been done.
self._submit_staff_assessment(xblock, submission)
self._assert_path_and_context(
xblock,
{
'status_value': 'Complete',
'icon_class': 'fa-check',
'step_classes': 'is--complete is--empty is--collapsed',
}
)
class TestStaffAssessment(StaffAssessmentTestBase):
......@@ -36,10 +155,7 @@ class TestStaffAssessment(StaffAssessmentTestBase):
submission = xblock.create_submission(student_item, self.SUBMISSION)
# Submit a staff-assessment
self.set_staff_access(xblock)
self.ASSESSMENT['submission_uuid'] = submission['uuid']
resp = self.request(xblock, 'staff_assess', json.dumps(self.ASSESSMENT), response_format='json')
self.assertTrue(resp['success'])
self._submit_staff_assessment(xblock, submission)
# Expect that a staff-assessment was created
assessment = staff_api.get_latest_staff_assessment(submission['uuid'])
......
......@@ -114,6 +114,31 @@ class StaffAreaA11yTest(OpenAssessmentA11yTest):
self._check_a11y(self.staff_area_page)
def test_staff_grade(self):
"""
Check the accessibility of the Staff Grade section, as shown to the learner.
"""
self.auto_auth_page.visit()
username = self.auto_auth_page.get_username()
self.submission_page.visit().submit_response(self.SUBMISSION)
self.assertTrue(self.submission_page.has_submitted)
# Submit a staff override
self.staff_area_page.visit()
self.staff_area_page.show_learner(username)
self.staff_area_page.expand_learner_report_sections()
self.staff_area_page.assess("staff", self.STAFF_OVERRIDE_OPTIONS_SELECTED)
# Refresh the page, and learner completes a self-assessment.
# Then verify accessibility of the Staff Grade section (marked Complete).
self.browser.refresh()
self.self_asmnt_page.wait_for_page().wait_for_response()
self.self_asmnt_page.assess("self", self.OPTIONS_SELECTED).wait_for_complete()
self.assertTrue(self.self_asmnt_page.is_complete)
self._verify_staff_grade_section("COMPLETE", None)
self._check_a11y(self.staff_asmnt_page)
if __name__ == "__main__":
......
......@@ -204,7 +204,7 @@ class AssessmentPage(OpenAssessmentPage, AssessmentMixin):
Page object representing an "assessment" step in an ORA problem.
"""
ASSESSMENT_TYPES = ['self-assessment', 'peer-assessment', 'student-training']
ASSESSMENT_TYPES = ['self-assessment', 'peer-assessment', 'student-training', 'staff-assessment']
def __init__(self, assessment_type, *args):
"""
......@@ -223,6 +223,13 @@ class AssessmentPage(OpenAssessmentPage, AssessmentMixin):
raise PageConfigurationError(msg)
self._assessment_type = assessment_type
def _bounded_selector(self, selector):
"""
Return `selector`, but limited to this Assignment Page.
"""
return '#openassessment__{assessment_type} {selector}'.format(
assessment_type=self._assessment_type, selector=selector)
def is_browser_on_page(self):
css_id = "#openassessment__{assessment_type}".format(
assessment_type=self._assessment_type
......@@ -337,6 +344,48 @@ class AssessmentPage(OpenAssessmentPage, AssessmentMixin):
candidates = [int(x) for x in self.q(css=".step__status__value--completed").text]
return candidates[0] if len(candidates) > 0 else None
@property
def label(self):
"""
Returns the label of this assessment step.
Returns:
string
"""
return self.q(css=self._bounded_selector(".step__label")).text[0]
@property
def status_value(self):
"""
Returns the status value (ie., "COMPLETE", "CANCELLED", etc.) of this assessment step.
Returns:
string
"""
return self.q(css=self._bounded_selector(".step__status__value")).text[0]
@property
def message_title(self):
"""
Returns the message title, if present, of this assesment step.
Returns:
string is message title is present, else None
"""
message_title = self.q(css=self._bounded_selector(".message__title"))
if len(message_title) == 0:
return None
return message_title.text[0]
def verify_status_value(self, expected_value):
"""
Waits until the expected status value appears. If it does not appear, fails the test.
"""
EmptyPromise(
lambda: self.status_value == expected_value,
"Expected status value present"
).fulfill()
class GradePage(OpenAssessmentPage):
"""
......
......@@ -68,6 +68,7 @@ class OpenAssessmentTest(WebAppTest):
SUBMISSION = u"This is a test submission."
LATEX_SUBMISSION = u"[mathjaxinline]( \int_{0}^{1}xdx \)[/mathjaxinline]"
OPTIONS_SELECTED = [1, 2]
STAFF_OVERRIDE_OPTIONS_SELECTED = [0, 1]
EXPECTED_SCORE = 6
def setUp(self, problem_type, staff=False):
......@@ -88,6 +89,7 @@ class OpenAssessmentTest(WebAppTest):
self.self_asmnt_page = AssessmentPage('self-assessment', self.browser, self.problem_loc)
self.peer_asmnt_page = AssessmentPage('peer-assessment', self.browser, self.problem_loc)
self.student_training_page = AssessmentPage('student-training', self.browser, self.problem_loc)
self.staff_asmnt_page = AssessmentPage('staff-assessment', self.browser, self.problem_loc)
self.grade_page = GradePage(self.browser, self.problem_loc)
def do_self_assessment(self):
......@@ -111,6 +113,12 @@ class OpenAssessmentTest(WebAppTest):
return username
def _verify_staff_grade_section(self, expected_status, expected_message_title):
self.staff_asmnt_page.wait_for_page()
self.assertEqual("Staff Grade", self.staff_asmnt_page.label)
self.staff_asmnt_page.verify_status_value(expected_status)
self.assertEqual(expected_message_title, self.staff_asmnt_page.message_title)
class SelfAssessmentTest(OpenAssessmentTest):
"""
......@@ -179,6 +187,69 @@ class PeerAssessmentTest(OpenAssessmentTest):
self.fail("Did not complete at least one peer assessment.")
class PeerAssessmentTestStaffOverride(OpenAssessmentTest):
"""
Test setting a staff override on a problem which requires peer assessment.
"""
def setUp(self):
super(PeerAssessmentTestStaffOverride, self).setUp('peer_only', staff=True)
self.staff_area_page = StaffAreaPage(self.browser, self.problem_loc)
@retry()
@attr('acceptance')
def test_staff_override(self):
"""
Scenario: staff can override a learner's grade
Given I am viewing a new peer assessment problem as a learner
And if I create a response to the problem
Then there is no Staff Grade section present
And if a staff member creates a grade override
Then when I refresh the page, I see that a staff override exists
And the message says that I must complete my steps to view the grade
And if I submit required peer assessments
Then the Staff Grade section is marked complete with no message
And I can see my final grade, even though no peers have assessed me
"""
# Create two students with a submission each so that there are 2 submissions to assess.
for _ in range(0, 2):
self.auto_auth_page.visit()
self.submission_page.visit().submit_response(self.SUBMISSION)
# Create a submission for the third student (used for the remainder of the test).
self.auto_auth_page.visit()
username = self.auto_auth_page.get_username()
self.submission_page.visit().submit_response(self.SUBMISSION)
# Staff Grade field should not be visible yet.
self.assertFalse(self.staff_asmnt_page.is_browser_on_page())
# Submit a staff override.
self.staff_area_page.visit()
self.staff_area_page.show_learner(username)
self.staff_area_page.expand_learner_report_sections()
self.staff_area_page.assess("staff", self.STAFF_OVERRIDE_OPTIONS_SELECTED)
# Refresh the page so the learner sees the Staff Grade section.
self.browser.refresh()
self._verify_staff_grade_section("COMPLETE", "YOU MUST COMPLETE THE ABOVE STEPS TO VIEW YOUR GRADE")
# Verify no final grade yet.
self.assertIsNone(self.grade_page.wait_for_page().score)
# Assess two submissions
for count_assessed in range(1, 3):
self.peer_asmnt_page.wait_for_page().wait_for_response().assess("peer", self.OPTIONS_SELECTED)
self.peer_asmnt_page.wait_for_num_completed(count_assessed)
# Staff grade section is now marked complete, even though no students have submitted
# assessments for this particular student (no longer required since staff grade exists).
self._verify_staff_grade_section("COMPLETE", None)
# Verify the staff override grade
self.assertEqual(self.grade_page.wait_for_page().score, 1)
class StudentTrainingTest(OpenAssessmentTest):
"""
Test student training (the "learning to assess" step).
......@@ -315,7 +386,7 @@ class StaffAreaTest(OpenAssessmentTest):
# Click on staff tools and search for user
self.staff_area_page.show_learner(username)
self.assertEqual(
[u'Learner Response', u"Learner's Self Assessment", u"Learner's Final Grade",
[u"Learner's Response", u"Learner's Self Assessment", u"Learner's Final Grade",
u"Submit Assessment Grade Override", u"Remove Submission From Peer Grading"],
self.staff_area_page.learner_report_sections
)
......@@ -365,7 +436,7 @@ class StaffAreaTest(OpenAssessmentTest):
self.staff_area_page.verify_learner_final_score("Final grade: 6 out of 8")
# Do staff override and wait for final score to change.
self.staff_area_page.assess("staff", [0, 1])
self.staff_area_page.assess("staff", self.STAFF_OVERRIDE_OPTIONS_SELECTED)
# Verify that the new student score is different from the original one.
# Unfortunately there is no indication presently that this was a staff override.
......@@ -405,7 +476,7 @@ class StaffAreaTest(OpenAssessmentTest):
# Verify that the staff override and submission removal sections are now gone.
self.assertEqual(
[u'Learner Response', u"Learner's Self Assessment", u"Learner's Final Grade"],
[u"Learner's Response", u"Learner's Self Assessment", u"Learner's Final Grade"],
self.staff_area_page.learner_report_sections
)
......@@ -413,6 +484,86 @@ class StaffAreaTest(OpenAssessmentTest):
self.staff_area_page.expand_learner_report_sections()
self.assertIn("Learner submission removed", self.staff_area_page.learner_response)
@retry()
@attr('acceptance')
def test_staff_grade_override(self):
"""
Scenario: the staff grade section displays correctly
Given I am viewing a new self assessment problem as a learner
Then there is no Staff Grade section present
And if I create a response to the problem
Then there is no Staff Grade section present
And if a staff member creates a grade override
Then when I refresh the page, I see that a staff override exists
And the message says that I must complete my steps to view the grade
And if I submit my self-assessment
Then the Staff Grade section is marked complete with no message
And I can see my final grade
"""
# View the problem-- no Staff Grade area.
self.auto_auth_page.visit()
username = self.auto_auth_page.get_username()
self.submission_page.visit()
self.assertFalse(self.staff_asmnt_page.is_browser_on_page())
self.submission_page.submit_response(self.SUBMISSION)
self.assertTrue(self.submission_page.has_submitted)
self.assertFalse(self.staff_asmnt_page.is_browser_on_page())
# Submit a staff override
self.staff_area_page.visit()
self.staff_area_page.show_learner(username)
self.staff_area_page.expand_learner_report_sections()
self.staff_area_page.assess("staff", self.STAFF_OVERRIDE_OPTIONS_SELECTED)
# Refresh the page so the learner sees the Staff Grade section.
self.browser.refresh()
self._verify_staff_grade_section("COMPLETE", "YOU MUST COMPLETE THE ABOVE STEPS TO VIEW YOUR GRADE")
# Verify no final grade yet.
self.assertIsNone(self.grade_page.wait_for_page().score)
# Learner does required self-assessment
self.self_asmnt_page.wait_for_page().wait_for_response()
self.assertIn(self.SUBMISSION, self.self_asmnt_page.response_text)
self.self_asmnt_page.assess("self", self.OPTIONS_SELECTED).wait_for_complete()
self.assertTrue(self.self_asmnt_page.is_complete)
self._verify_staff_grade_section("COMPLETE", None)
# Verify the staff override grade
self.assertEqual(self.grade_page.wait_for_page().score, 1)
@retry()
@attr('acceptance')
def test_staff_grade_override_cancelled(self):
"""
Scenario: the staff grade section displays cancelled when the submission is cancelled
Given I have created a response and a self-assessment
And a staff member creates a grade override and then cancels my submission
Then when I refresh the page, the Staff Grade section is marked cancelled
And I have no final grade
"""
username = self.do_self_assessment()
# Submit a staff override
self.staff_area_page.visit()
self.staff_area_page.show_learner(username)
self.staff_area_page.expand_learner_report_sections()
# Do staff override.
self.staff_area_page.assess("staff", self.STAFF_OVERRIDE_OPTIONS_SELECTED)
# And cancel the submission
self.staff_area_page.expand_learner_report_sections()
self.staff_area_page.cancel_submission()
# Refresh the page so the learner sees the Staff Grade section shows the submission has been cancelled.
self.browser.refresh()
self._verify_staff_grade_section("CANCELLED", None)
self.assertIsNone(self.grade_page.wait_for_page().score)
class FileUploadTest(OpenAssessmentTest):
"""
......
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