Merge remote-tracking branch 'origin/master' into feature/alex/poll-merged
Conflicts: cms/djangoapps/contentstore/tests/test_contentstore.py cms/djangoapps/contentstore/views.py common/lib/xmodule/xmodule/combined_open_ended_module.py common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py common/lib/xmodule/xmodule/peer_grading_module.py common/lib/xmodule/xmodule/tests/test_combined_open_ended.py common/lib/xmodule/xmodule/tests/test_self_assessment.py lms/djangoapps/open_ended_grading/tests.py
This commit is contained in:
122
lms/djangoapps/courseware/management/commands/regrade_partial.py
Normal file
122
lms/djangoapps/courseware/management/commands/regrade_partial.py
Normal file
@@ -0,0 +1,122 @@
|
||||
'''
|
||||
This is a one-off command aimed at fixing a temporary problem encountered where partial credit was awarded for
|
||||
code problems, but the resulting score (or grade) was mistakenly set to zero because of a bug in
|
||||
CorrectMap.get_npoints().
|
||||
'''
|
||||
|
||||
import json
|
||||
import logging
|
||||
from optparse import make_option
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from courseware.models import StudentModule
|
||||
from capa.correctmap import CorrectMap
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
'''
|
||||
The fix here is to recalculate the score/grade based on the partial credit.
|
||||
To narrow down the set of problems that might need fixing, the StudentModule
|
||||
objects to be checked is filtered down to those:
|
||||
|
||||
created < '2013-03-08 15:45:00' (the problem must have been answered before the fix was installed,
|
||||
on Prod and Edge)
|
||||
modified > '2013-03-07 20:18:00' (the problem must have been visited after the bug was introduced)
|
||||
state like '%"npoints": 0.%' (the problem must have some form of partial credit).
|
||||
'''
|
||||
|
||||
num_visited = 0
|
||||
num_changed = 0
|
||||
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option('--save',
|
||||
action='store_true',
|
||||
dest='save_changes',
|
||||
default=False,
|
||||
help='Persist the changes that were encountered. If not set, no changes are saved.'), )
|
||||
|
||||
def fix_studentmodules(self, save_changes):
|
||||
'''Identify the list of StudentModule objects that might need fixing, and then fix each one'''
|
||||
modules = StudentModule.objects.filter(modified__gt='2013-03-07 20:18:00',
|
||||
created__lt='2013-03-08 15:45:00',
|
||||
state__contains='"npoints": 0.')
|
||||
|
||||
for module in modules:
|
||||
self.fix_studentmodule_grade(module, save_changes)
|
||||
|
||||
def fix_studentmodule_grade(self, module, save_changes):
|
||||
''' Fix the grade assigned to a StudentModule'''
|
||||
module_state = module.state
|
||||
if module_state is None:
|
||||
# not likely, since we filter on it. But in general...
|
||||
LOG.info("No state found for {type} module {id} for student {student} in course {course_id}"
|
||||
.format(type=module.module_type, id=module.module_state_key,
|
||||
student=module.student.username, course_id=module.course_id))
|
||||
return
|
||||
|
||||
state_dict = json.loads(module_state)
|
||||
self.num_visited += 1
|
||||
|
||||
# LoncapaProblem.get_score() checks student_answers -- if there are none, we will return a grade of 0
|
||||
# Check that this is the case, but do so sooner, before we do any of the other grading work.
|
||||
student_answers = state_dict['student_answers']
|
||||
if (not student_answers) or len(student_answers) == 0:
|
||||
# we should not have a grade here:
|
||||
if module.grade != 0:
|
||||
LOG.error("No answer found but grade {grade} exists for {type} module {id} for student {student} "
|
||||
"in course {course_id}".format(grade=module.grade,
|
||||
type=module.module_type, id=module.module_state_key,
|
||||
student=module.student.username, course_id=module.course_id))
|
||||
else:
|
||||
LOG.debug("No answer and no grade found for {type} module {id} for student {student} "
|
||||
"in course {course_id}".format(grade=module.grade,
|
||||
type=module.module_type, id=module.module_state_key,
|
||||
student=module.student.username, course_id=module.course_id))
|
||||
return
|
||||
|
||||
# load into a CorrectMap, as done in LoncapaProblem.__init__():
|
||||
correct_map = CorrectMap()
|
||||
if 'correct_map' in state_dict:
|
||||
correct_map.set_dict(state_dict['correct_map'])
|
||||
|
||||
# calculate score the way LoncapaProblem.get_score() works, by deferring to
|
||||
# CorrectMap's get_npoints implementation.
|
||||
correct = 0
|
||||
for key in correct_map:
|
||||
correct += correct_map.get_npoints(key)
|
||||
|
||||
if module.grade == correct:
|
||||
# nothing to change
|
||||
LOG.debug("Grade matches for {type} module {id} for student {student} in course {course_id}"
|
||||
.format(type=module.module_type, id=module.module_state_key,
|
||||
student=module.student.username, course_id=module.course_id))
|
||||
elif save_changes:
|
||||
# make the change
|
||||
LOG.info("Grade changing from {0} to {1} for {type} module {id} for student {student} "
|
||||
"in course {course_id}".format(module.grade, correct,
|
||||
type=module.module_type, id=module.module_state_key,
|
||||
student=module.student.username, course_id=module.course_id))
|
||||
module.grade = correct
|
||||
module.save()
|
||||
self.num_changed += 1
|
||||
else:
|
||||
# don't make the change, but log that the change would be made
|
||||
LOG.info("Grade would change from {0} to {1} for {type} module {id} for student {student} "
|
||||
"in course {course_id}".format(module.grade, correct,
|
||||
type=module.module_type, id=module.module_state_key,
|
||||
student=module.student.username, course_id=module.course_id))
|
||||
self.num_changed += 1
|
||||
|
||||
def handle(self, **options):
|
||||
'''Handle management command request'''
|
||||
|
||||
save_changes = options['save_changes']
|
||||
|
||||
LOG.info("Starting run: save_changes = {0}".format(save_changes))
|
||||
|
||||
self.fix_studentmodules(save_changes)
|
||||
|
||||
LOG.info("Finished run: updating {0} of {1} modules".format(self.num_changed, self.num_visited))
|
||||
@@ -22,7 +22,7 @@ NOTIFICATION_TYPES = (
|
||||
('staff_needs_to_grade', 'staff_grading', 'Staff Grading'),
|
||||
('new_student_grading_to_view', 'open_ended_problems', 'Problems you have submitted'),
|
||||
('flagged_submissions_exist', 'open_ended_flagged_problems', 'Flagged Submissions')
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def staff_grading_notifications(course, user):
|
||||
@@ -46,7 +46,9 @@ def staff_grading_notifications(course, user):
|
||||
#Non catastrophic error, so no real action
|
||||
notifications = {}
|
||||
#This is a dev_facing_error
|
||||
log.info("Problem with getting notifications from staff grading service for course {0} user {1}.".format(course_id, student_id))
|
||||
log.info(
|
||||
"Problem with getting notifications from staff grading service for course {0} user {1}.".format(course_id,
|
||||
student_id))
|
||||
|
||||
if pending_grading:
|
||||
img_path = "/static/images/grading_notification.png"
|
||||
@@ -87,7 +89,9 @@ def peer_grading_notifications(course, user):
|
||||
#Non catastrophic error, so no real action
|
||||
notifications = {}
|
||||
#This is a dev_facing_error
|
||||
log.info("Problem with getting notifications from peer grading service for course {0} user {1}.".format(course_id, student_id))
|
||||
log.info(
|
||||
"Problem with getting notifications from peer grading service for course {0} user {1}.".format(course_id,
|
||||
student_id))
|
||||
|
||||
if pending_grading:
|
||||
img_path = "/static/images/grading_notification.png"
|
||||
@@ -119,7 +123,9 @@ def combined_notifications(course, user):
|
||||
return notification_dict
|
||||
|
||||
min_time_to_query = user.last_login
|
||||
last_module_seen = StudentModule.objects.filter(student=user, course_id=course_id, modified__gt=min_time_to_query).values('modified').order_by('-modified')
|
||||
last_module_seen = StudentModule.objects.filter(student=user, course_id=course_id,
|
||||
modified__gt=min_time_to_query).values('modified').order_by(
|
||||
'-modified')
|
||||
last_module_seen_count = last_module_seen.count()
|
||||
|
||||
if last_module_seen_count > 0:
|
||||
@@ -131,7 +137,8 @@ def combined_notifications(course, user):
|
||||
|
||||
img_path = ""
|
||||
try:
|
||||
controller_response = controller_qs.check_combined_notifications(course.id, student_id, user_is_staff, last_time_viewed)
|
||||
controller_response = controller_qs.check_combined_notifications(course.id, student_id, user_is_staff,
|
||||
last_time_viewed)
|
||||
log.debug(controller_response)
|
||||
notifications = json.loads(controller_response)
|
||||
if notifications['success']:
|
||||
@@ -141,7 +148,9 @@ def combined_notifications(course, user):
|
||||
#Non catastrophic error, so no real action
|
||||
notifications = {}
|
||||
#This is a dev_facing_error
|
||||
log.exception("Problem with getting notifications from controller query service for course {0} user {1}.".format(course_id, student_id))
|
||||
log.exception(
|
||||
"Problem with getting notifications from controller query service for course {0} user {1}.".format(
|
||||
course_id, student_id))
|
||||
|
||||
if pending_grading:
|
||||
img_path = "/static/images/grading_notification.png"
|
||||
@@ -165,7 +174,8 @@ def set_value_in_cache(student_id, course_id, notification_type, value):
|
||||
|
||||
|
||||
def create_key_name(student_id, course_id, notification_type):
|
||||
key_name = "{prefix}{type}_{course}_{student}".format(prefix=KEY_PREFIX, type=notification_type, course=course_id, student=student_id)
|
||||
key_name = "{prefix}{type}_{course}_{student}".format(prefix=KEY_PREFIX, type=notification_type, course=course_id,
|
||||
student=student_id)
|
||||
return key_name
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ class StaffGrading(object):
|
||||
"""
|
||||
Wrap up functionality for staff grading of submissions--interface exposes get_html, ajax views.
|
||||
"""
|
||||
|
||||
def __init__(self, course):
|
||||
self.course = course
|
||||
|
||||
|
||||
@@ -20,10 +20,12 @@ log = logging.getLogger(__name__)
|
||||
|
||||
STAFF_ERROR_MESSAGE = 'Could not contact the external grading server. Please contact the development team. If you do not have a point of contact, you can contact Vik at vik@edx.org.'
|
||||
|
||||
|
||||
class MockStaffGradingService(object):
|
||||
"""
|
||||
A simple mockup of a staff grading service, testing.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.cnt = 0
|
||||
|
||||
@@ -43,15 +45,18 @@ class MockStaffGradingService(object):
|
||||
def get_problem_list(self, course_id, grader_id):
|
||||
self.cnt += 1
|
||||
return json.dumps({'success': True,
|
||||
'problem_list': [
|
||||
json.dumps({'location': 'i4x://MITx/3.091x/problem/open_ended_demo1',
|
||||
'problem_name': "Problem 1", 'num_graded': 3, 'num_pending': 5, 'min_for_ml': 10}),
|
||||
json.dumps({'location': 'i4x://MITx/3.091x/problem/open_ended_demo2',
|
||||
'problem_name': "Problem 2", 'num_graded': 1, 'num_pending': 5, 'min_for_ml': 10})
|
||||
]})
|
||||
'problem_list': [
|
||||
json.dumps({'location': 'i4x://MITx/3.091x/problem/open_ended_demo1',
|
||||
'problem_name': "Problem 1", 'num_graded': 3, 'num_pending': 5,
|
||||
'min_for_ml': 10}),
|
||||
json.dumps({'location': 'i4x://MITx/3.091x/problem/open_ended_demo2',
|
||||
'problem_name': "Problem 2", 'num_graded': 1, 'num_pending': 5,
|
||||
'min_for_ml': 10})
|
||||
]})
|
||||
|
||||
|
||||
def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores, submission_flagged):
|
||||
def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores,
|
||||
submission_flagged):
|
||||
return self.get_next(course_id, 'fake location', grader_id)
|
||||
|
||||
|
||||
@@ -59,6 +64,7 @@ class StaffGradingService(GradingService):
|
||||
"""
|
||||
Interface to staff grading backend.
|
||||
"""
|
||||
|
||||
def __init__(self, config):
|
||||
config['system'] = ModuleSystem(
|
||||
ajax_url=None,
|
||||
@@ -116,12 +122,13 @@ class StaffGradingService(GradingService):
|
||||
GradingServiceError: something went wrong with the connection.
|
||||
"""
|
||||
response = self.get(self.get_next_url,
|
||||
params={'location': location,
|
||||
'grader_id': grader_id})
|
||||
params={'location': location,
|
||||
'grader_id': grader_id})
|
||||
return json.dumps(self._render_rubric(response))
|
||||
|
||||
|
||||
def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores, submission_flagged):
|
||||
def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores,
|
||||
submission_flagged):
|
||||
"""
|
||||
Save a score and feedback for a submission.
|
||||
|
||||
@@ -260,14 +267,14 @@ def get_problem_list(request, course_id):
|
||||
try:
|
||||
response = staff_grading_service().get_problem_list(course_id, unique_id_for_user(request.user))
|
||||
return HttpResponse(response,
|
||||
mimetype="application/json")
|
||||
mimetype="application/json")
|
||||
except GradingServiceError:
|
||||
#This is a dev_facing_error
|
||||
log.exception("Error from staff grading service in open ended grading. server url: {0}"
|
||||
.format(staff_grading_service().url))
|
||||
.format(staff_grading_service().url))
|
||||
#This is a staff_facing_error
|
||||
return HttpResponse(json.dumps({'success': False,
|
||||
'error': STAFF_ERROR_MESSAGE}))
|
||||
'error': STAFF_ERROR_MESSAGE}))
|
||||
|
||||
|
||||
def _get_next(course_id, grader_id, location):
|
||||
@@ -279,7 +286,7 @@ def _get_next(course_id, grader_id, location):
|
||||
except GradingServiceError:
|
||||
#This is a dev facing error
|
||||
log.exception("Error from staff grading service in open ended grading. server url: {0}"
|
||||
.format(staff_grading_service().url))
|
||||
.format(staff_grading_service().url))
|
||||
#This is a staff_facing_error
|
||||
return json.dumps({'success': False,
|
||||
'error': STAFF_ERROR_MESSAGE})
|
||||
@@ -304,7 +311,7 @@ def save_grade(request, course_id):
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
required = set(['score', 'feedback', 'submission_id', 'location','submission_flagged', 'rubric_scores[]'])
|
||||
required = set(['score', 'feedback', 'submission_id', 'location', 'submission_flagged', 'rubric_scores[]'])
|
||||
actual = set(request.POST.keys())
|
||||
missing = required - actual
|
||||
if len(missing) > 0:
|
||||
@@ -314,22 +321,23 @@ def save_grade(request, course_id):
|
||||
grader_id = unique_id_for_user(request.user)
|
||||
p = request.POST
|
||||
|
||||
|
||||
location = p['location']
|
||||
skipped = 'skipped' in p
|
||||
skipped = 'skipped' in p
|
||||
|
||||
try:
|
||||
result_json = staff_grading_service().save_grade(course_id,
|
||||
grader_id,
|
||||
p['submission_id'],
|
||||
p['score'],
|
||||
p['feedback'],
|
||||
skipped,
|
||||
p.getlist('rubric_scores[]'),
|
||||
p['submission_flagged'])
|
||||
grader_id,
|
||||
p['submission_id'],
|
||||
p['score'],
|
||||
p['feedback'],
|
||||
skipped,
|
||||
p.getlist('rubric_scores[]'),
|
||||
p['submission_flagged'])
|
||||
except GradingServiceError:
|
||||
#This is a dev_facing_error
|
||||
log.exception("Error saving grade in the staff grading interface in open ended grading. Request: {0} Course ID: {1}".format(request, course_id))
|
||||
log.exception(
|
||||
"Error saving grade in the staff grading interface in open ended grading. Request: {0} Course ID: {1}".format(
|
||||
request, course_id))
|
||||
#This is a staff_facing_error
|
||||
return _err_response(STAFF_ERROR_MESSAGE)
|
||||
|
||||
@@ -337,13 +345,16 @@ def save_grade(request, course_id):
|
||||
result = json.loads(result_json)
|
||||
except ValueError:
|
||||
#This is a dev_facing_error
|
||||
log.exception("save_grade returned broken json in the staff grading interface in open ended grading: {0}".format(result_json))
|
||||
log.exception(
|
||||
"save_grade returned broken json in the staff grading interface in open ended grading: {0}".format(
|
||||
result_json))
|
||||
#This is a staff_facing_error
|
||||
return _err_response(STAFF_ERROR_MESSAGE)
|
||||
|
||||
if not result.get('success', False):
|
||||
#This is a dev_facing_error
|
||||
log.warning('Got success=False from staff grading service in open ended grading. Response: {0}'.format(result_json))
|
||||
log.warning(
|
||||
'Got success=False from staff grading service in open ended grading. Response: {0}'.format(result_json))
|
||||
return _err_response(STAFF_ERROR_MESSAGE)
|
||||
|
||||
# Ok, save_grade seemed to work. Get the next submission to grade.
|
||||
|
||||
@@ -7,7 +7,7 @@ django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/open
|
||||
from django.test import TestCase
|
||||
from open_ended_grading import staff_grading_service
|
||||
from xmodule.open_ended_grading_classes import peer_grading_service
|
||||
from xmodule import peer_grading_module
|
||||
from xmodule import peer_grading_module
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
@@ -22,6 +22,7 @@ from xmodule.x_module import ModuleSystem
|
||||
from mitxmako.shortcuts import render_to_string
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
from django.test.utils import override_settings
|
||||
from django.http import QueryDict
|
||||
@@ -36,6 +37,7 @@ class TestStaffGradingService(ct.PageLoader):
|
||||
access control and error handling logic -- all the actual work is on the
|
||||
backend.
|
||||
'''
|
||||
|
||||
def setUp(self):
|
||||
xmodule.modulestore.django._MODULESTORES = {}
|
||||
|
||||
@@ -50,6 +52,7 @@ class TestStaffGradingService(ct.PageLoader):
|
||||
|
||||
self.course_id = "edX/toy/2012_Fall"
|
||||
self.toy = modulestore().get_course(self.course_id)
|
||||
|
||||
def make_instructor(course):
|
||||
group_name = _course_staff_group_name(course.location)
|
||||
g = Group.objects.create(name=group_name)
|
||||
@@ -130,6 +133,7 @@ class TestPeerGradingService(ct.PageLoader):
|
||||
access control and error handling logic -- all the actual work is on the
|
||||
backend.
|
||||
'''
|
||||
|
||||
def setUp(self):
|
||||
xmodule.modulestore.django._MODULESTORES = {}
|
||||
|
||||
@@ -145,16 +149,16 @@ class TestPeerGradingService(ct.PageLoader):
|
||||
self.course_id = "edX/toy/2012_Fall"
|
||||
self.toy = modulestore().get_course(self.course_id)
|
||||
location = "i4x://edX/toy/peergrading/init"
|
||||
model_data = {'data' : "<peergrading/>"}
|
||||
model_data = {'data': "<peergrading/>"}
|
||||
self.mock_service = peer_grading_service.MockPeerGradingService()
|
||||
self.system = ModuleSystem(
|
||||
ajax_url=location,
|
||||
track_function=None,
|
||||
get_module = None,
|
||||
get_module=None,
|
||||
render_template=render_to_string,
|
||||
replace_urls=None,
|
||||
xblock_model_data= {},
|
||||
s3_interface = test_util_open_ended.S3_INTERFACE,
|
||||
xblock_model_data={},
|
||||
s3_interface=test_util_open_ended.S3_INTERFACE,
|
||||
open_ended_grading_interface=test_util_open_ended.OPEN_ENDED_GRADING_INTERFACE
|
||||
)
|
||||
self.descriptor = peer_grading_module.PeerGradingDescriptor(self.system, location, model_data)
|
||||
@@ -182,18 +186,20 @@ class TestPeerGradingService(ct.PageLoader):
|
||||
|
||||
def test_save_grade_success(self):
|
||||
data = {
|
||||
'rubric_scores[]': [0, 0],
|
||||
'location': self.location,
|
||||
'submission_id': 1,
|
||||
'submission_key': 'fake key',
|
||||
'score': 2,
|
||||
'feedback': 'feedback',
|
||||
'submission_flagged': 'false'
|
||||
}
|
||||
'rubric_scores[]': [0, 0],
|
||||
'location': self.location,
|
||||
'submission_id': 1,
|
||||
'submission_key': 'fake key',
|
||||
'score': 2,
|
||||
'feedback': 'feedback',
|
||||
'submission_flagged': 'false'
|
||||
}
|
||||
|
||||
qdict = MagicMock()
|
||||
|
||||
def fake_get_item(key):
|
||||
return data[key]
|
||||
|
||||
qdict.__getitem__.side_effect = fake_get_item
|
||||
qdict.getlist = fake_get_item
|
||||
qdict.keys = data.keys
|
||||
@@ -244,18 +250,20 @@ class TestPeerGradingService(ct.PageLoader):
|
||||
|
||||
def test_save_calibration_essay_success(self):
|
||||
data = {
|
||||
'rubric_scores[]': [0, 0],
|
||||
'location': self.location,
|
||||
'submission_id': 1,
|
||||
'submission_key': 'fake key',
|
||||
'score': 2,
|
||||
'feedback': 'feedback',
|
||||
'submission_flagged': 'false'
|
||||
}
|
||||
'rubric_scores[]': [0, 0],
|
||||
'location': self.location,
|
||||
'submission_id': 1,
|
||||
'submission_key': 'fake key',
|
||||
'score': 2,
|
||||
'feedback': 'feedback',
|
||||
'submission_flagged': 'false'
|
||||
}
|
||||
|
||||
qdict = MagicMock()
|
||||
|
||||
def fake_get_item(key):
|
||||
return data[key]
|
||||
|
||||
qdict.__getitem__.side_effect = fake_get_item
|
||||
qdict.getlist = fake_get_item
|
||||
qdict.keys = data.keys
|
||||
|
||||
@@ -57,22 +57,24 @@ def _reverse_without_slash(url_name, course_id):
|
||||
ajax_url = reverse(url_name, kwargs={'course_id': course_id})
|
||||
return ajax_url
|
||||
|
||||
|
||||
DESCRIPTION_DICT = {
|
||||
'Peer Grading': "View all problems that require peer assessment in this particular course.",
|
||||
'Staff Grading': "View ungraded submissions submitted by students for the open ended problems in the course.",
|
||||
'Problems you have submitted': "View open ended problems that you have previously submitted for grading.",
|
||||
'Flagged Submissions': "View submissions that have been flagged by students as inappropriate."
|
||||
}
|
||||
'Peer Grading': "View all problems that require peer assessment in this particular course.",
|
||||
'Staff Grading': "View ungraded submissions submitted by students for the open ended problems in the course.",
|
||||
'Problems you have submitted': "View open ended problems that you have previously submitted for grading.",
|
||||
'Flagged Submissions': "View submissions that have been flagged by students as inappropriate."
|
||||
}
|
||||
ALERT_DICT = {
|
||||
'Peer Grading': "New submissions to grade",
|
||||
'Staff Grading': "New submissions to grade",
|
||||
'Problems you have submitted': "New grades have been returned",
|
||||
'Flagged Submissions': "Submissions have been flagged for review"
|
||||
}
|
||||
'Peer Grading': "New submissions to grade",
|
||||
'Staff Grading': "New submissions to grade",
|
||||
'Problems you have submitted': "New grades have been returned",
|
||||
'Flagged Submissions': "Submissions have been flagged for review"
|
||||
}
|
||||
|
||||
STUDENT_ERROR_MESSAGE = "Error occured while contacting the grading service. Please notify course staff."
|
||||
STAFF_ERROR_MESSAGE = "Error occured while contacting the grading service. Please notify the development team. If you do not have a point of contact, please email Vik at vik@edx.org"
|
||||
|
||||
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
def staff_grading(request, course_id):
|
||||
"""
|
||||
@@ -99,10 +101,10 @@ def peer_grading(request, course_id):
|
||||
#Get the current course
|
||||
course = get_course_with_access(request.user, course_id, 'load')
|
||||
course_id_parts = course.id.split("/")
|
||||
false_dict = [False,"False", "false", "FALSE"]
|
||||
false_dict = [False, "False", "false", "FALSE"]
|
||||
|
||||
#Reverse the base course url
|
||||
base_course_url = reverse('courses')
|
||||
base_course_url = reverse('courses')
|
||||
try:
|
||||
#TODO: This will not work with multiple runs of a course. Make it work. The last key in the Location passed
|
||||
#to get_items is called revision. Is this the same as run?
|
||||
@@ -154,7 +156,7 @@ def student_problem_list(request, course_id):
|
||||
success = False
|
||||
error_text = ""
|
||||
problem_list = []
|
||||
base_course_url = reverse('courses')
|
||||
base_course_url = reverse('courses')
|
||||
|
||||
try:
|
||||
problem_list_json = controller_qs.get_grading_status_list(course_id, unique_id_for_user(request.user))
|
||||
@@ -181,7 +183,7 @@ def student_problem_list(request, course_id):
|
||||
except:
|
||||
#This is a student_facing_error
|
||||
eta_string = "Error getting ETA."
|
||||
problem_list[i].update({'eta_string' : eta_string})
|
||||
problem_list[i].update({'eta_string': eta_string})
|
||||
|
||||
except GradingServiceError:
|
||||
#This is a student_facing_error
|
||||
@@ -222,7 +224,7 @@ def flagged_problem_list(request, course_id):
|
||||
success = False
|
||||
error_text = ""
|
||||
problem_list = []
|
||||
base_course_url = reverse('courses')
|
||||
base_course_url = reverse('courses')
|
||||
|
||||
try:
|
||||
problem_list_json = controller_qs.get_flagged_problem_list(course_id)
|
||||
@@ -250,14 +252,14 @@ def flagged_problem_list(request, course_id):
|
||||
|
||||
ajax_url = _reverse_with_slash('open_ended_flagged_problems', course_id)
|
||||
context = {
|
||||
'course': course,
|
||||
'course_id': course_id,
|
||||
'ajax_url': ajax_url,
|
||||
'success': success,
|
||||
'problem_list': problem_list,
|
||||
'error_text': error_text,
|
||||
# Checked above
|
||||
'staff_access': True,
|
||||
'course': course,
|
||||
'course_id': course_id,
|
||||
'ajax_url': ajax_url,
|
||||
'success': success,
|
||||
'problem_list': problem_list,
|
||||
'error_text': error_text,
|
||||
# Checked above
|
||||
'staff_access': True,
|
||||
}
|
||||
return render_to_response('open_ended_problems/open_ended_flagged_problems.html', context)
|
||||
|
||||
@@ -312,7 +314,7 @@ def combined_notifications(request, course_id):
|
||||
}
|
||||
|
||||
return render_to_response('open_ended_problems/combined_notifications.html',
|
||||
combined_dict
|
||||
combined_dict
|
||||
)
|
||||
|
||||
|
||||
@@ -325,13 +327,14 @@ def take_action_on_flags(request, course_id):
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
|
||||
required = ['submission_id', 'action_type', 'student_id']
|
||||
for key in required:
|
||||
if key not in request.POST:
|
||||
#This is a staff_facing_error
|
||||
return HttpResponse(json.dumps({'success': False, 'error': STAFF_ERROR_MESSAGE + 'Missing key {0} from submission. Please reload and try again.'.format(key)}),
|
||||
mimetype="application/json")
|
||||
return HttpResponse(json.dumps({'success': False,
|
||||
'error': STAFF_ERROR_MESSAGE + 'Missing key {0} from submission. Please reload and try again.'.format(
|
||||
key)}),
|
||||
mimetype="application/json")
|
||||
|
||||
p = request.POST
|
||||
submission_id = p['submission_id']
|
||||
@@ -345,5 +348,7 @@ def take_action_on_flags(request, course_id):
|
||||
return HttpResponse(response, mimetype="application/json")
|
||||
except GradingServiceError:
|
||||
#This is a dev_facing_error
|
||||
log.exception("Error taking action on flagged peer grading submissions, submission_id: {0}, action_type: {1}, grader_id: {2}".format(submission_id, action_type, grader_id))
|
||||
log.exception(
|
||||
"Error taking action on flagged peer grading submissions, submission_id: {0}, action_type: {1}, grader_id: {2}".format(
|
||||
submission_id, action_type, grader_id))
|
||||
return _err_response(STAFF_ERROR_MESSAGE)
|
||||
|
||||
Reference in New Issue
Block a user