Commit 42e70c98 by muzaffaryousaf

Adding more functionality to staff regrade.

TNL-898
parent fc56649d
...@@ -5,10 +5,11 @@ the workflow for a given submission. ...@@ -5,10 +5,11 @@ the workflow for a given submission.
""" """
import logging import logging
from django.utils import timezone from django.utils import timezone
from django.db import DatabaseError, IntegrityError, transaction from django.db import DatabaseError, IntegrityError, transaction
from dogapi import dog_stats_api
from dogapi import dog_stats_api
from openassessment.assessment.models import ( from openassessment.assessment.models import (
Assessment, AssessmentFeedback, AssessmentPart, Assessment, AssessmentFeedback, AssessmentPart,
InvalidRubricSelection, PeerWorkflow, PeerWorkflowItem, InvalidRubricSelection, PeerWorkflow, PeerWorkflowItem,
...@@ -23,6 +24,7 @@ from openassessment.assessment.errors import ( ...@@ -23,6 +24,7 @@ from openassessment.assessment.errors import (
) )
from submissions import api as sub_api from submissions import api as sub_api
logger = logging.getLogger("openassessment.assessment.api.peer") logger = logging.getLogger("openassessment.assessment.api.peer")
PEER_TYPE = "PE" PEER_TYPE = "PE"
...@@ -295,6 +297,7 @@ def create_assessment( ...@@ -295,6 +297,7 @@ def create_assessment(
logger.exception(error_message) logger.exception(error_message)
raise PeerAssessmentInternalError(error_message) raise PeerAssessmentInternalError(error_message)
@transaction.commit_on_success @transaction.commit_on_success
def _complete_assessment( def _complete_assessment(
rubric_dict, rubric_dict,
...@@ -896,9 +899,9 @@ def _log_assessment(assessment, scorer_workflow): ...@@ -896,9 +899,9 @@ def _log_assessment(assessment, scorer_workflow):
try: try:
workflow_item = assessment.peerworkflowitem_set.get() workflow_item = assessment.peerworkflowitem_set.get()
except ( except (
PeerWorkflowItem.DoesNotExist, PeerWorkflowItem.DoesNotExist,
PeerWorkflowItem.MultipleObjectsReturned, PeerWorkflowItem.MultipleObjectsReturned,
DatabaseError DatabaseError
): ):
msg = u"Could not retrieve peer workflow item for assessment: {assessment}".format( msg = u"Could not retrieve peer workflow item for assessment: {assessment}".format(
assessment=assessment.id assessment=assessment.id
...@@ -967,13 +970,16 @@ def create_overridden_assessment(assessment_id, points, scorer_id, comments=None ...@@ -967,13 +970,16 @@ def create_overridden_assessment(assessment_id, points, scorer_id, comments=None
""" """
try: try:
points = int(points) points = int(points)
# Get the particular assessment to Override/Regrade # Get the particular assessment to Override/Regrade
assessment = Assessment.objects.get(pk=assessment_id) assessment = Assessment.objects.get(pk=assessment_id)
return AssessmentOverride.create(assessment=assessment, points=points, comments=comments, scorer_id=scorer_id, if points > assessment.points_possible:
scored_at=scored_at) raise PeerAssessmentRequestError(u'New grade points must be less than or equal to possible points.')
return AssessmentOverride.create(assessment=assessment, points=points, comments=comments,
scorer_id=scorer_id, scored_at=scored_at)
except ValueError: except ValueError:
raise PeerAssessmentRequestError(u'New grade must be an integer') raise PeerAssessmentRequestError(u'New grade must be an integer.')
except Assessment.DoesNotExist: except Assessment.DoesNotExist:
error_message = u"There is no assessment associated with the given assessment ID {}.".format(assessment_id) error_message = u"There is no assessment associated with the given assessment ID {}.".format(assessment_id)
......
...@@ -593,6 +593,11 @@ class Assessment(models.Model): ...@@ -593,6 +593,11 @@ class Assessment(models.Model):
cache.set(cache_key, scores) cache.set(cache_key, scores)
return scores return scores
@property
def latest_overridden_assessment(self):
if self.overridden.exists():
return self.overridden.order_by('-scored_at')[0]
class AssessmentPart(models.Model): class AssessmentPart(models.Model):
"""Part of an Assessment corresponding to a particular Criterion. """Part of an Assessment corresponding to a particular Criterion.
...@@ -832,7 +837,7 @@ class AssessmentOverride(models.Model): ...@@ -832,7 +837,7 @@ class AssessmentOverride(models.Model):
""" """
MAX_COMMENT_SIZE = 1000 * 10 MAX_COMMENT_SIZE = 1000 * 10
assessment = models.ForeignKey(Assessment, related_name='override') assessment = models.ForeignKey(Assessment, related_name='overridden')
scored_at = models.DateTimeField(default=now, db_index=True) scored_at = models.DateTimeField(default=now, db_index=True)
scorer_id = models.CharField(max_length=40, db_index=True) scorer_id = models.CharField(max_length=40, db_index=True)
...@@ -872,6 +877,6 @@ class AssessmentOverride(models.Model): ...@@ -872,6 +877,6 @@ class AssessmentOverride(models.Model):
# Truncate the comment if it exceeds the maximum size # Truncate the comment if it exceeds the maximum size
if comments is not None: if comments is not None:
assessment_params['comment'] = comments[0:cls.MAX_COMMENT_SIZE] assessment_params['comments'] = comments[0:cls.MAX_COMMENT_SIZE]
return cls.objects.create(**assessment_params) return cls.objects.create(**assessment_params)
...@@ -160,7 +160,7 @@ class AssessmentSerializer(serializers.ModelSerializer): ...@@ -160,7 +160,7 @@ class AssessmentSerializer(serializers.ModelSerializer):
def serialize_assessments(assessments_qset): def serialize_assessments(assessments_qset):
assessments = list(assessments_qset.select_related("rubric")) assessments = list(assessments_qset.select_related("rubric", "override"))
rubric_cache = {} rubric_cache = {}
return [ return [
...@@ -188,6 +188,7 @@ def full_assessment_dict(assessment, rubric_dict=None): ...@@ -188,6 +188,7 @@ def full_assessment_dict(assessment, rubric_dict=None):
Returns: Returns:
dict with keys 'rubric' (serialized Rubric model) and 'parts' (serialized assessment parts) dict with keys 'rubric' (serialized Rubric model) and 'parts' (serialized assessment parts)
and overridden staff grade if exists.
""" """
assessment_cache_key = "assessment.full_assessment_dict.{}.{}.{}".format( assessment_cache_key = "assessment.full_assessment_dict.{}.{}.{}".format(
assessment.id, assessment.submission_uuid, assessment.scored_at.isoformat() assessment.id, assessment.submission_uuid, assessment.scored_at.isoformat()
...@@ -233,6 +234,9 @@ def full_assessment_dict(assessment, rubric_dict=None): ...@@ -233,6 +234,9 @@ def full_assessment_dict(assessment, rubric_dict=None):
assessment_dict["points_possible"] = rubric_dict["points_possible"] assessment_dict["points_possible"] = rubric_dict["points_possible"]
assessment_dict["id"] = assessment.id assessment_dict["id"] = assessment.id
# Get the latest overridden staff grade
assessment_dict["staff_overridden_grade"] = assessment.latest_overridden_assessment
cache.set(assessment_cache_key, assessment_dict) cache.set(assessment_cache_key, assessment_dict)
return assessment_dict return assessment_dict
......
{% load i18n %} {% load i18n %}
{% load tz %} {% load tz %}
<h5 class="title--sub">{% trans "New Staff Grade" %}</h5> <h5 class="title--sub">{% trans "New Staff Grade" %}</h5>
<div class="student__answer__display__content"> <div class="student__answer__display__content">
{{ overridden_assessment.points }} {{ overridden_points }}
</div> </div>
<h5 class="title--sub">{% trans "Regrade Comments" %}</h5> <h5 class="title--sub">{% trans "Regrade Comments" %}</h5>
<div class="student__answer__display__content"> <div class="student__answer__display__content">
{{ overridden_assessment.comments|linebreaks }} {{ overridden_comments|linebreaks }}
</div> </div>
...@@ -29,7 +29,9 @@ ...@@ -29,7 +29,9 @@
<h3 class="title">{% trans "Peer Assessments for This Student" %}</h3> <h3 class="title">{% trans "Peer Assessments for This Student" %}</h3>
{% for assessment in peer_assessments %} {% for assessment in peer_assessments %}
{% with peer_num=forloop.counter %} {% with peer_num=forloop.counter %}
<h4 class="title--sub"> {% trans "Peer" %} {{ peer_num }}: </h4> <h4 class="title--sub"> {% trans "Peer" %} {{ peer_num }}:
{%if assessment.staff_overridden_grade %}{% trans "(Staff Re-graded)" %}{% endif %}
</h4>
<table class="staff-info__status__table" summary="{% trans "Assessment" %}"> <table class="staff-info__status__table" summary="{% trans "Assessment" %}">
<thead> <thead>
<tr> <tr>
...@@ -61,6 +63,11 @@ ...@@ -61,6 +63,11 @@
<div class="student__answer__display__content"> <div class="student__answer__display__content">
{{ assessment.feedback|linebreaks }} {{ assessment.feedback|linebreaks }}
</div> </div>
<div id="openassessment__staff-info__regrade-info" class="staff-info__regrade-info">
{% if assessment.staff_overridden_grade %}
{% include "openassessmentblock/staff_debug/staff_regrade_info.html" with overridden_points=assessment.staff_overridden_grade.points overridden_comments=assessment.staff_overridden_grade.comments %}
{% endif %}
</div>
<div id="openassessment__staff-info__regrade" class="wrapper--staff-info wrapper--ui-staff wrapper--ui--regrade"> <div id="openassessment__staff-info__regrade" class="wrapper--staff-info wrapper--ui-staff wrapper--ui--regrade">
...@@ -76,14 +83,14 @@ ...@@ -76,14 +83,14 @@
<form id="openassessment_staff_regrade_form" data-assessment-id="{{ assessment.id }}"> <form id="openassessment_staff_regrade_form" data-assessment-id="{{ assessment.id }}">
<ul> <ul>
<li> <li>
<label for="openassessment__staff-info__regrade_points" class="label">{% trans "New Total Garde" %}</label> <label for="openassessment__staff-info__regrade_points" class="label">{% trans "New Total Grade" %}</label>
</li> </li>
<li> <li>
<input id="openassessment__staff-info__regrade_points" type="number" <input id="openassessment__staff-info__regrade_points" type="number"
maxlength="3" min="0" max="100"> maxlength="3" min="0" max="100">
</li> </li>
<li> <li>
<label for="openassessment__staff-info__regrade_comments" class="label">{% trans "(OPTIONAL) Comments" %}</label> <label for="openassessment__staff-info__regrade_comments" class="label">{% trans "(Optional) Comments" %}</label>
</li> </li>
<li> <li>
<textarea <textarea
...@@ -108,7 +115,6 @@ ...@@ -108,7 +115,6 @@
</div> </div>
</div> </div>
</div> </div>
<div id="openassessment__staff-info__regrade-info" class="staff-info__regrade-info"></div>
......
...@@ -283,34 +283,34 @@ class StaffInfoMixin(object): ...@@ -283,34 +283,34 @@ class StaffInfoMixin(object):
@XBlock.handler @XBlock.handler
@require_course_staff("STAFF_INFO") @require_course_staff("STAFF_INFO")
def override_assessment(self, data, suffix=''): def staff_override_assessment(self, data, suffix=''):
assessment_id = data.params.get('assessment_id', '') assessment_id = data.params.get('assessment_id', '')
points = data.params.get('points', None) points = data.params.get('points', None)
comments = data.params.get('comments', '') comments = data.params.get('comments', '')
if points is None: if points is None:
return ( return self.render_error(self._(u'"override_assessment" required new grade value.'))
False,
'EBADARGS',
self._(u'"override_assessment" required new grade value.')
)
try: try:
overridden_assessment = peer_api.create_overridden_assessment(assessment_id=assessment_id, points=points, comments=comments, overridden_assessment = peer_api.create_overridden_assessment(assessment_id=assessment_id, points=points, comments=comments,
scorer_id=self.get_student_item_dict()["student_id"]) scorer_id=self.get_student_item_dict()["student_id"])
path='openassessmentblock/staff_debug/staff_regrade_info.html' path='openassessmentblock/staff_debug/staff_regrade_info.html'
context = { context = {
'overridden_assessment': overridden_assessment 'overridden_points': overridden_assessment.points,
'overridden_comments': overridden_assessment.comments
} }
return self.render_assessment(path, context) return self.render_assessment(path, context)
except PeerAssessmentRequestError as ex: except PeerAssessmentRequestError as ex:
msg = ex.message msg = ex.message
logger.exception(msg) logger.exception(msg)
return {'success': False, 'msg': msg} return self.render_error(self._(msg))
except PeerAssessmentInternalError as ex: except PeerAssessmentInternalError as ex:
msg = ex.message msg = ex.message
logger.exception(msg) logger.exception(msg)
return {'success': False, 'msg': msg} return self.render_error(self._(msg))
@XBlock.json_handler @XBlock.json_handler
......
...@@ -62,20 +62,16 @@ OpenAssessment.StaffInfoView.prototype = { ...@@ -62,20 +62,16 @@ OpenAssessment.StaffInfoView.prototype = {
// Install key handler for new staff grade Save button. // Install key handler for new staff grade Save button.
var selStudentInfo = $('#openassessment__staff-info__regrade', this.element); var selStudentInfo = $('#openassessment__staff-info__regrade', this.element);
selStudentInfo.on('click', '#submit_new_staff_grade', function (eventObject) { selStudentInfo.on('click', '#submit_new_staff_grade', function (eventObject) {
var assessmentId = $(this).data('assessment-id');
eventObject.preventDefault(); eventObject.preventDefault();
view.overrideStaffGrade(assessmentId); view.overrideStaffGrade($(this).data('assessment-id'));
} }
); );
// Install key handler for new staff grade field. // Install key handler for new staff grade field.
selStudentInfo.on('submit', '#openassessment_staff_regrade_form', function (eventObject) { selStudentInfo.on('submit', '#openassessment_staff_regrade_form', function (eventObject) {
var assessmentId = $(this).data('assessment-id');
eventObject.preventDefault(); eventObject.preventDefault();
view.overrideStaffGrade(assessmentId); view.overrideStaffGrade($(this).data('assessment-id'));
} }
); );
} }
).fail(function(errMsg) { ).fail(function(errMsg) {
view.showLoadError('student_info'); view.showLoadError('student_info');
...@@ -85,16 +81,19 @@ OpenAssessment.StaffInfoView.prototype = { ...@@ -85,16 +81,19 @@ OpenAssessment.StaffInfoView.prototype = {
/** /**
Upon request, overrides the current assessment grade. Upon request, overrides the current assessment grade.
**/ **/
overrideStaffGrade: function(assessment_uuid) { overrideStaffGrade: function(assessmentId) {
var view = this; var view = this;
var sel = $('#openassessment__staff-info', this.element); var sel = $('#openassessment__staff-info', this.element);
var points = sel.find('#openassessment__staff-info__regrade_points').val(); var points = sel.find('#openassessment__staff-info__regrade_points').val();
var comments = sel.find('#openassessment__staff-info__regrade_comments').val(); var comments = sel.find('#openassessment__staff-info__regrade_comments').val();
this.server.assessmentOverride(assessment_uuid, points, comments).done( this.server.assessmentOverride(assessmentId, points, comments).done(
function(html) { function(html) {
// Load the HTML and install event handlers // Load the HTML and install event handlers
$('#openassessment__staff-info__regrade-info', view.element).replaceWith(html); if (html) {
}
$('#openassessment__staff-info__regrade-info', view.element).html(html);
} }
).fail(function(errMsg) { ).fail(function(errMsg) {
view.showLoadError('student_info'); view.showLoadError('student_info');
......
...@@ -141,7 +141,7 @@ if (typeof OpenAssessment.Server == "undefined" || !OpenAssessment.Server) { ...@@ -141,7 +141,7 @@ if (typeof OpenAssessment.Server == "undefined" || !OpenAssessment.Server) {
         Save the re-graded info.          Save the re-graded info.
         **/          **/
assessmentOverride: function (assessmentId, points, comments) { assessmentOverride: function (assessmentId, points, comments) {
var url = this.url('override_assessment'); var url = this.url('staff_override_assessment');
return $.Deferred(function (defer) { return $.Deferred(function (defer) {
$.ajax({ $.ajax({
url: url, url: url,
......
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