Commit 1bd0bcb8 by Calen Pennington

Merge pull request #3091 from cpennington/ora-metrics

Add metrics around old ORA requests
parents 4b1228be 0513e144
from dogapi import dog_stats_api
import logging import logging
from .grading_service_module import GradingService from .grading_service_module import GradingService
...@@ -9,33 +11,28 @@ class ControllerQueryService(GradingService): ...@@ -9,33 +11,28 @@ class ControllerQueryService(GradingService):
Interface to controller query backend. Interface to controller query backend.
""" """
METRIC_NAME = 'edxapp.open_ended_grading.controller_query_service'
def __init__(self, config, system): def __init__(self, config, system):
config['system'] = system config['system'] = system
super(ControllerQueryService, self).__init__(config) super(ControllerQueryService, self).__init__(config)
self.url = config['url'] + config['grading_controller'] self.url = config['url'] + config['grading_controller']
self.login_url = self.url + '/login/' self.login_url = self.url + '/login/'
self.check_eta_url = self.url + '/get_submission_eta/' self.check_eta_url = self.url + '/get_submission_eta/'
self.is_unique_url = self.url + '/is_name_unique/'
self.combined_notifications_url = self.url + '/combined_notifications/' self.combined_notifications_url = self.url + '/combined_notifications/'
self.grading_status_list_url = self.url + '/get_grading_status_list/' self.grading_status_list_url = self.url + '/get_grading_status_list/'
self.flagged_problem_list_url = self.url + '/get_flagged_problem_list/' self.flagged_problem_list_url = self.url + '/get_flagged_problem_list/'
self.take_action_on_flags_url = self.url + '/take_action_on_flags/' self.take_action_on_flags_url = self.url + '/take_action_on_flags/'
def check_if_name_is_unique(self, location, problem_id, course_id):
params = {
'course_id': course_id,
'location': location,
'problem_id': problem_id
}
response = self.get(self.is_unique_url, params)
return response
def check_for_eta(self, location): def check_for_eta(self, location):
params = { params = {
'location': location, 'location': location,
} }
response = self.get(self.check_eta_url, params) data = self.get(self.check_eta_url, params)
return response self._record_result('check_for_eta', data)
dog_stats_api.histogram(self._metric_name('check_for_eta.eta'), data.get('eta', 0))
return data
def check_combined_notifications(self, course_id, student_id, user_is_staff, last_time_viewed): def check_combined_notifications(self, course_id, student_id, user_is_staff, last_time_viewed):
params = { params = {
...@@ -45,8 +42,16 @@ class ControllerQueryService(GradingService): ...@@ -45,8 +42,16 @@ class ControllerQueryService(GradingService):
'last_time_viewed': last_time_viewed, 'last_time_viewed': last_time_viewed,
} }
log.debug(self.combined_notifications_url) log.debug(self.combined_notifications_url)
response = self.get(self.combined_notifications_url, params) data = self.get(self.combined_notifications_url, params)
return response
tags = [u'course_id:{}'.format(course_id), u'user_is_staff:{}'.format(user_is_staff)]
tags.extend(
u'{}:{}'.format(key, value)
for key, value in data.items()
if key not in ('success', 'version', 'error')
)
self._record_result('check_combined_notifications', data, tags)
return data
def get_grading_status_list(self, course_id, student_id): def get_grading_status_list(self, course_id, student_id):
params = { params = {
...@@ -54,16 +59,31 @@ class ControllerQueryService(GradingService): ...@@ -54,16 +59,31 @@ class ControllerQueryService(GradingService):
'course_id': course_id, 'course_id': course_id,
} }
response = self.get(self.grading_status_list_url, params) data = self.get(self.grading_status_list_url, params)
return response
tags = [u'course_id:{}'.format(course_id)]
self._record_result('get_grading_status_list', data, tags)
dog_stats_api.histogram(
self._metric_name('get_grading_status_list.length'),
len(data.get('problem_list', [])),
tags=tags
)
return data
def get_flagged_problem_list(self, course_id): def get_flagged_problem_list(self, course_id):
params = { params = {
'course_id': course_id, 'course_id': course_id,
} }
response = self.get(self.flagged_problem_list_url, params) data = self.get(self.flagged_problem_list_url, params)
return response
tags = [u'course_id:{}'.format(course_id)]
self._record_result('get_flagged_problem_list', data, tags)
dog_stats_api.histogram(
self._metric_name('get_flagged_problem_list.length'),
len(data.get('flagged_submissions', []))
)
return data
def take_action_on_flags(self, course_id, student_id, submission_id, action_type): def take_action_on_flags(self, course_id, student_id, submission_id, action_type):
params = { params = {
...@@ -73,8 +93,11 @@ class ControllerQueryService(GradingService): ...@@ -73,8 +93,11 @@ class ControllerQueryService(GradingService):
'action_type': action_type 'action_type': action_type
} }
response = self.post(self.take_action_on_flags_url, params) data = self.post(self.take_action_on_flags_url, params)
return response
tags = [u'course_id:{}'.format(course_id), u'action_type:{}'.format(action_type)]
self._record_result('take_action_on_flags', data, tags)
return data
class MockControllerQueryService(object): class MockControllerQueryService(object):
...@@ -85,14 +108,6 @@ class MockControllerQueryService(object): ...@@ -85,14 +108,6 @@ class MockControllerQueryService(object):
def __init__(self, config, system): def __init__(self, config, system):
pass pass
def check_if_name_is_unique(self, *args, **kwargs):
"""
Mock later if needed. Stub function for now.
@param params:
@return:
"""
pass
def check_for_eta(self, *args, **kwargs): def check_for_eta(self, *args, **kwargs):
""" """
Mock later if needed. Stub function for now. Mock later if needed. Stub function for now.
...@@ -102,15 +117,47 @@ class MockControllerQueryService(object): ...@@ -102,15 +117,47 @@ class MockControllerQueryService(object):
pass pass
def check_combined_notifications(self, *args, **kwargs): def check_combined_notifications(self, *args, **kwargs):
combined_notifications = '{"flagged_submissions_exist": false, "version": 1, "new_student_grading_to_view": false, "success": true, "staff_needs_to_grade": false, "student_needs_to_peer_grade": true, "overall_need_to_check": true}' combined_notifications = {
"flagged_submissions_exist": False,
"version": 1,
"new_student_grading_to_view": False,
"success": True,
"staff_needs_to_grade": False,
"student_needs_to_peer_grade": True,
"overall_need_to_check": True
}
return combined_notifications return combined_notifications
def get_grading_status_list(self, *args, **kwargs): def get_grading_status_list(self, *args, **kwargs):
grading_status_list = '{"version": 1, "problem_list": [{"problem_name": "Science Question -- Machine Assessed", "grader_type": "NA", "eta_available": true, "state": "Waiting to be Graded", "eta": 259200, "location": "i4x://MITx/oe101x/combinedopenended/Science_SA_ML"}, {"problem_name": "Humanities Question -- Peer Assessed", "grader_type": "NA", "eta_available": true, "state": "Waiting to be Graded", "eta": 259200, "location": "i4x://MITx/oe101x/combinedopenended/Humanities_SA_Peer"}], "success": true}' grading_status_list = {
"version": 1,
"problem_list": [
{
"problem_name": "Science Question -- Machine Assessed",
"grader_type": "NA",
"eta_available": True,
"state": "Waiting to be Graded",
"eta": 259200,
"location": "i4x://MITx/oe101x/combinedopenended/Science_SA_ML"
}, {
"problem_name": "Humanities Question -- Peer Assessed",
"grader_type": "NA",
"eta_available": True,
"state": "Waiting to be Graded",
"eta": 259200,
"location": "i4x://MITx/oe101x/combinedopenended/Humanities_SA_Peer"
}
],
"success": True
}
return grading_status_list return grading_status_list
def get_flagged_problem_list(self, *args, **kwargs): def get_flagged_problem_list(self, *args, **kwargs):
flagged_problem_list = '{"version": 1, "success": false, "error": "No flagged submissions exist for course: MITx/oe101x/2012_Fall"}' flagged_problem_list = {
"version": 1,
"success": False,
"error": "No flagged submissions exist for course: MITx/oe101x/2012_Fall"
}
return flagged_problem_list return flagged_problem_list
def take_action_on_flags(self, *args, **kwargs): def take_action_on_flags(self, *args, **kwargs):
...@@ -131,5 +178,4 @@ def convert_seconds_to_human_readable(seconds): ...@@ -131,5 +178,4 @@ def convert_seconds_to_human_readable(seconds):
else: else:
human_string = "{0} days".format(round(seconds / (60 * 60 * 24), 1)) human_string = "{0} days".format(round(seconds / (60 * 60 * 24), 1))
eta_string = "{0}".format(human_string) return human_string
return eta_string
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
import json import json
import logging import logging
import requests import requests
from dogapi import dog_stats_api
from requests.exceptions import RequestException, ConnectionError, HTTPError from requests.exceptions import RequestException, ConnectionError, HTTPError
from .combined_open_ended_rubric import CombinedOpenEndedRubric from .combined_open_ended_rubric import CombinedOpenEndedRubric, RubricParsingError
from lxml import etree from lxml import etree
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -44,40 +45,66 @@ class GradingService(object): ...@@ -44,40 +45,66 @@ class GradingService(object):
return response.json() return response.json()
def _metric_name(self, suffix):
"""
Return a metric name for datadog, using `self.METRIC_NAME` as
a prefix, and `suffix` as the suffix.
Arguments:
suffix (str): The metric suffix to use.
"""
return '{}.{}'.format(self.METRIC_NAME, suffix)
def _record_result(self, action, data, tags=None):
"""
Log results from an API call to an ORA service to datadog.
Arguments:
action (str): The ORA action being recorded.
data (dict): The data returned from the ORA service. Should contain the key 'success'.
tags (list): A list of tags to attach to the logged metric.
"""
if tags is None:
tags = []
tags.append(u'result:{}'.format(data.get('success', False)))
tags.append(u'action:{}'.format(action))
dog_stats_api.increment(self._metric_name('request.count'), tags=tags)
def post(self, url, data, allow_redirects=False): def post(self, url, data, allow_redirects=False):
""" """
Make a post request to the grading controller Make a post request to the grading controller. Returns the parsed json results of that request.
""" """
try: try:
op = lambda: self.session.post(url, data=data, op = lambda: self.session.post(url, data=data,
allow_redirects=allow_redirects) allow_redirects=allow_redirects)
r = self._try_with_login(op) response_json = self._try_with_login(op)
except (RequestException, ConnectionError, HTTPError) as err: except (RequestException, ConnectionError, HTTPError, ValueError) as err:
# reraise as promised GradingServiceError, but preserve stacktrace. # reraise as promised GradingServiceError, but preserve stacktrace.
#This is a dev_facing_error #This is a dev_facing_error
error_string = "Problem posting data to the grading controller. URL: {0}, data: {1}".format(url, data) error_string = "Problem posting data to the grading controller. URL: {0}, data: {1}".format(url, data)
log.error(error_string) log.error(error_string)
raise GradingServiceError(error_string) raise GradingServiceError(error_string)
return r.text return response_json
def get(self, url, params, allow_redirects=False): def get(self, url, params, allow_redirects=False):
""" """
Make a get request to the grading controller Make a get request to the grading controller. Returns the parsed json results of that request.
""" """
op = lambda: self.session.get(url, op = lambda: self.session.get(url,
allow_redirects=allow_redirects, allow_redirects=allow_redirects,
params=params) params=params)
try: try:
r = self._try_with_login(op) response_json = self._try_with_login(op)
except (RequestException, ConnectionError, HTTPError) as err: except (RequestException, ConnectionError, HTTPError, ValueError) as err:
# reraise as promised GradingServiceError, but preserve stacktrace. # reraise as promised GradingServiceError, but preserve stacktrace.
#This is a dev_facing_error #This is a dev_facing_error
error_string = "Problem getting data from the grading controller. URL: {0}, params: {1}".format(url, params) error_string = "Problem getting data from the grading controller. URL: {0}, params: {1}".format(url, params)
log.error(error_string) log.error(error_string)
raise GradingServiceError(error_string) raise GradingServiceError(error_string)
return r.text return response_json
def _try_with_login(self, operation): def _try_with_login(self, operation):
""" """
...@@ -101,35 +128,29 @@ class GradingService(object): ...@@ -101,35 +128,29 @@ class GradingService(object):
response = operation() response = operation()
response.raise_for_status() response.raise_for_status()
return response return resp_json
def _render_rubric(self, response, view_only=False): def _render_rubric(self, response, view_only=False):
""" """
Given an HTTP Response with the key 'rubric', render out the html Given an HTTP Response json with the key 'rubric', render out the html
required to display the rubric and put it back into the response required to display the rubric and put it back into the response
returns the updated response as a dictionary that can be serialized later returns the updated response as a dictionary that can be serialized later
""" """
try: try:
response_json = json.loads(response) if 'rubric' in response:
except: rubric = response['rubric']
response_json = response
try:
if 'rubric' in response_json:
rubric = response_json['rubric']
rubric_renderer = CombinedOpenEndedRubric(self.system, view_only) rubric_renderer = CombinedOpenEndedRubric(self.system, view_only)
rubric_dict = rubric_renderer.render_rubric(rubric) rubric_dict = rubric_renderer.render_rubric(rubric)
success = rubric_dict['success'] success = rubric_dict['success']
rubric_html = rubric_dict['html'] rubric_html = rubric_dict['html']
response_json['rubric'] = rubric_html response['rubric'] = rubric_html
return response_json return response
# if we can't parse the rubric into HTML, # if we can't parse the rubric into HTML,
except etree.XMLSyntaxError, RubricParsingError: except (etree.XMLSyntaxError, RubricParsingError):
#This is a dev_facing_error #This is a dev_facing_error
log.exception("Cannot parse rubric string. Raw string: {0}" log.exception("Cannot parse rubric string. Raw string: {0}".format(response['rubric']))
.format(rubric))
return {'success': False, return {'success': False,
'error': 'Error displaying submission'} 'error': 'Error displaying submission'}
except ValueError: except ValueError:
......
...@@ -533,10 +533,6 @@ class OpenEndedChild(object): ...@@ -533,10 +533,6 @@ class OpenEndedChild(object):
def get_eta(self): def get_eta(self):
if self.controller_qs: if self.controller_qs:
response = self.controller_qs.check_for_eta(self.location_string) response = self.controller_qs.check_for_eta(self.location_string)
try:
response = json.loads(response)
except:
pass
else: else:
return "" return ""
......
import json import json
import logging import logging
from dogapi import dog_stats_api
from .grading_service_module import GradingService, GradingServiceError from .grading_service_module import GradingService, GradingServiceError
...@@ -11,6 +12,8 @@ class PeerGradingService(GradingService): ...@@ -11,6 +12,8 @@ class PeerGradingService(GradingService):
Interface with the grading controller for peer grading Interface with the grading controller for peer grading
""" """
METRIC_NAME = 'edxapp.open_ended_grading.peer_grading_service'
def __init__(self, config, system): def __init__(self, config, system):
config['system'] = system config['system'] = system
super(PeerGradingService, self).__init__(config) super(PeerGradingService, self).__init__(config)
...@@ -28,54 +31,78 @@ class PeerGradingService(GradingService): ...@@ -28,54 +31,78 @@ class PeerGradingService(GradingService):
def get_data_for_location(self, problem_location, student_id): def get_data_for_location(self, problem_location, student_id):
params = {'location': problem_location, 'student_id': student_id} params = {'location': problem_location, 'student_id': student_id}
response = self.get(self.get_data_for_location_url, params) result = self.get(self.get_data_for_location_url, params)
return self.try_to_decode(response) self._record_result('get_data_for_location', result)
for key in result.keys():
if key in ('success', 'error', 'version'):
continue
dog_stats_api.histogram(
self._metric_name('get_data_for_location.{}'.format(key)),
result[key],
)
return result
def get_next_submission(self, problem_location, grader_id): def get_next_submission(self, problem_location, grader_id):
response = self.get( result = self._render_rubric(self.get(
self.get_next_submission_url, self.get_next_submission_url,
{ {
'location': problem_location, 'location': problem_location,
'grader_id': grader_id 'grader_id': grader_id
} }
) ))
return self.try_to_decode(self._render_rubric(response)) self._record_result('get_next_submission', result)
return result
def save_grade(self, **kwargs): def save_grade(self, **kwargs):
data = kwargs data = kwargs
data.update({'rubric_scores_complete': True}) data.update({'rubric_scores_complete': True})
return self.try_to_decode(self.post(self.save_grade_url, data)) result = self.post(self.save_grade_url, data)
self._record_result('save_grade', result)
return result
def is_student_calibrated(self, problem_location, grader_id): def is_student_calibrated(self, problem_location, grader_id):
params = {'problem_id': problem_location, 'student_id': grader_id} params = {'problem_id': problem_location, 'student_id': grader_id}
return self.try_to_decode(self.get(self.is_student_calibrated_url, params)) result = self.get(self.is_student_calibrated_url, params)
self._record_result(
'is_student_calibrated',
result,
tags=['calibrated:{}'.format(result.get('calibrated'))]
)
return result
def show_calibration_essay(self, problem_location, grader_id): def show_calibration_essay(self, problem_location, grader_id):
params = {'problem_id': problem_location, 'student_id': grader_id} params = {'problem_id': problem_location, 'student_id': grader_id}
response = self.get(self.show_calibration_essay_url, params) result = self._render_rubric(self.get(self.show_calibration_essay_url, params))
return self.try_to_decode(self._render_rubric(response)) self._record_result('show_calibration_essay', result)
return result
def save_calibration_essay(self, **kwargs): def save_calibration_essay(self, **kwargs):
data = kwargs data = kwargs
data.update({'rubric_scores_complete': True}) data.update({'rubric_scores_complete': True})
return self.try_to_decode(self.post(self.save_calibration_essay_url, data)) result = self.post(self.save_calibration_essay_url, data)
self._record_result('show_calibration_essay', result)
return result
def get_problem_list(self, course_id, grader_id): def get_problem_list(self, course_id, grader_id):
params = {'course_id': course_id, 'student_id': grader_id} params = {'course_id': course_id, 'student_id': grader_id}
response = self.get(self.get_problem_list_url, params) result = self.get(self.get_problem_list_url, params)
return self.try_to_decode(response) self._record_result('get_problem_list', result)
dog_stats_api.histogram(
self._metric_name('get_problem_list.result.length'),
len(result.get('problem_list',[]))
)
return result
def get_notifications(self, course_id, grader_id): def get_notifications(self, course_id, grader_id):
params = {'course_id': course_id, 'student_id': grader_id} params = {'course_id': course_id, 'student_id': grader_id}
response = self.get(self.get_notifications_url, params) result = self.get(self.get_notifications_url, params)
return self.try_to_decode(response) self._record_result(
'get_notifications',
def try_to_decode(self, text): result,
try: tags=['needs_to_peer_grade:{}'.format(result.get('student_needs_to_peer_grade'))]
text = json.loads(text) )
except: return result
pass
return text
""" """
......
...@@ -539,8 +539,7 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -539,8 +539,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
error_text = "" error_text = ""
problem_list = [] problem_list = []
try: try:
problem_list_json = self.peer_gs.get_problem_list(self.course_id, self.system.anonymous_student_id) problem_list_dict = self.peer_gs.get_problem_list(self.course_id, self.system.anonymous_student_id)
problem_list_dict = problem_list_json
success = problem_list_dict['success'] success = problem_list_dict['success']
if 'error' in problem_list_dict: if 'error' in problem_list_dict:
error_text = problem_list_dict['error'] error_text = problem_list_dict['error']
......
...@@ -165,9 +165,8 @@ def combined_notifications(course, user): ...@@ -165,9 +165,8 @@ def combined_notifications(course, user):
try: try:
#Get the notifications from the grading controller #Get the notifications from the grading controller
controller_response = controller_qs.check_combined_notifications(course.id, student_id, user_is_staff, notifications = controller_qs.check_combined_notifications(course.id, student_id, user_is_staff,
last_time_viewed) last_time_viewed)
notifications = json.loads(controller_response)
if notifications.get('success'): if notifications.get('success'):
if (notifications.get('staff_needs_to_grade') or if (notifications.get('staff_needs_to_grade') or
notifications.get('student_needs_to_peer_grade')): notifications.get('student_needs_to_peer_grade')):
......
...@@ -44,28 +44,28 @@ class MockStaffGradingService(object): ...@@ -44,28 +44,28 @@ class MockStaffGradingService(object):
def get_next(self, course_id, location, grader_id): def get_next(self, course_id, location, grader_id):
self.cnt += 1 self.cnt += 1
return json.dumps({'success': True, return {'success': True,
'submission_id': self.cnt, 'submission_id': self.cnt,
'submission': 'Test submission {cnt}'.format(cnt=self.cnt), 'submission': 'Test submission {cnt}'.format(cnt=self.cnt),
'num_graded': 3, 'num_graded': 3,
'min_for_ml': 5, 'min_for_ml': 5,
'num_pending': 4, 'num_pending': 4,
'prompt': 'This is a fake prompt', 'prompt': 'This is a fake prompt',
'ml_error_info': 'ML info', 'ml_error_info': 'ML info',
'max_score': 2 + self.cnt % 3, 'max_score': 2 + self.cnt % 3,
'rubric': 'A rubric'}) 'rubric': 'A rubric'}
def get_problem_list(self, course_id, grader_id): def get_problem_list(self, course_id, grader_id):
self.cnt += 1 self.cnt += 1
return json.dumps({'success': True, return {'success': True,
'problem_list': [ 'problem_list': [
json.dumps({'location': 'i4x://MITx/3.091x/problem/open_ended_demo1', json.dumps({'location': 'i4x://MITx/3.091x/problem/open_ended_demo1',
'problem_name': "Problem 1", 'num_graded': 3, 'num_pending': 5, 'problem_name': "Problem 1", 'num_graded': 3, 'num_pending': 5,
'min_for_ml': 10}), 'min_for_ml': 10}),
json.dumps({'location': 'i4x://MITx/3.091x/problem/open_ended_demo2', json.dumps({'location': 'i4x://MITx/3.091x/problem/open_ended_demo2',
'problem_name': "Problem 2", 'num_graded': 1, 'num_pending': 5, 'problem_name': "Problem 2", 'num_graded': 1, 'num_pending': 5,
'min_for_ml': 10}) 'min_for_ml': 10})
]}) ]}
def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores, def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores,
submission_flagged): submission_flagged):
...@@ -77,6 +77,8 @@ class StaffGradingService(GradingService): ...@@ -77,6 +77,8 @@ class StaffGradingService(GradingService):
Interface to staff grading backend. Interface to staff grading backend.
""" """
METRIC_NAME = 'edxapp.open_ended_grading.staff_grading_service'
def __init__(self, config): def __init__(self, config):
config['system'] = LmsModuleSystem( config['system'] = LmsModuleSystem(
static_url='/static', static_url='/static',
...@@ -106,7 +108,7 @@ class StaffGradingService(GradingService): ...@@ -106,7 +108,7 @@ class StaffGradingService(GradingService):
grader_id: who is grading this? The anonymous user_id of the grader. grader_id: who is grading this? The anonymous user_id of the grader.
Returns: Returns:
json string with the response from the service. (Deliberately not dict with the response from the service. (Deliberately not
writing out the fields here--see the docs on the staff_grading view writing out the fields here--see the docs on the staff_grading view
in the grading_controller repo) in the grading_controller repo)
...@@ -114,7 +116,14 @@ class StaffGradingService(GradingService): ...@@ -114,7 +116,14 @@ class StaffGradingService(GradingService):
GradingServiceError: something went wrong with the connection. GradingServiceError: something went wrong with the connection.
""" """
params = {'course_id': course_id, 'grader_id': grader_id} params = {'course_id': course_id, 'grader_id': grader_id}
return self.get(self.get_problem_list_url, params) result = self.get(self.get_problem_list_url, params)
tags = [u'course_id:{}'.format(course_id)]
self._record_result('get_problem_list', result, tags)
dog_stats_api.histogram(
self._metric_name('get_problem_list.result.length'),
len(result.get('problem_list', []))
)
return result
def get_next(self, course_id, location, grader_id): def get_next(self, course_id, location, grader_id):
""" """
...@@ -127,17 +136,25 @@ class StaffGradingService(GradingService): ...@@ -127,17 +136,25 @@ class StaffGradingService(GradingService):
grader_id: who is grading this? The anonymous user_id of the grader. grader_id: who is grading this? The anonymous user_id of the grader.
Returns: Returns:
json string with the response from the service. (Deliberately not dict with the response from the service. (Deliberately not
writing out the fields here--see the docs on the staff_grading view writing out the fields here--see the docs on the staff_grading view
in the grading_controller repo) in the grading_controller repo)
Raises: Raises:
GradingServiceError: something went wrong with the connection. GradingServiceError: something went wrong with the connection.
""" """
response = self.get(self.get_next_url, result = self._render_rubric(
params={'location': location, self.get(
'grader_id': grader_id}) self.get_next_url,
return json.dumps(self._render_rubric(response)) params={
'location': location,
'grader_id': grader_id
}
)
)
tags = [u'course_id:{}'.format(course_id)]
self._record_result('get_next', result, tags)
return result
def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores, def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores,
submission_flagged): submission_flagged):
...@@ -145,7 +162,7 @@ class StaffGradingService(GradingService): ...@@ -145,7 +162,7 @@ class StaffGradingService(GradingService):
Save a score and feedback for a submission. Save a score and feedback for a submission.
Returns: Returns:
json dict with keys dict with keys
'success': bool 'success': bool
'error': error msg, if something went wrong. 'error': error msg, if something went wrong.
...@@ -162,12 +179,20 @@ class StaffGradingService(GradingService): ...@@ -162,12 +179,20 @@ class StaffGradingService(GradingService):
'rubric_scores_complete': True, 'rubric_scores_complete': True,
'submission_flagged': submission_flagged} 'submission_flagged': submission_flagged}
return self.post(self.save_grade_url, data=data) result = self._render_rubric(self.post(self.save_grade_url, data=data))
tags = [u'course_id:{}'.format(course_id)]
self._record_result('save_grade', result, tags)
return result
def get_notifications(self, course_id): def get_notifications(self, course_id):
params = {'course_id': course_id} params = {'course_id': course_id}
response = self.get(self.get_notifications_url, params) result = self.get(self.get_notifications_url, params)
return response tags = [
u'course_id:{}'.format(course_id),
u'staff_needs_to_grade:{}'.format(result.get('staff_needs_to_grade'))
]
self._record_result('get_notifications', result, tags)
return result
# don't initialize until staff_grading_service() is called--means that just # don't initialize until staff_grading_service() is called--means that just
...@@ -249,7 +274,7 @@ def get_next(request, course_id): ...@@ -249,7 +274,7 @@ def get_next(request, course_id):
p = request.POST p = request.POST
location = p['location'] location = p['location']
return HttpResponse(_get_next(course_id, grader_id, location), return HttpResponse(json.dumps(_get_next(course_id, grader_id, location)),
mimetype="application/json") mimetype="application/json")
def get_problem_list(request, course_id): def get_problem_list(request, course_id):
...@@ -278,7 +303,6 @@ def get_problem_list(request, course_id): ...@@ -278,7 +303,6 @@ def get_problem_list(request, course_id):
_check_access(request.user, course_id) _check_access(request.user, course_id)
try: try:
response = staff_grading_service().get_problem_list(course_id, unique_id_for_user(request.user)) response = staff_grading_service().get_problem_list(course_id, unique_id_for_user(request.user))
response = json.loads(response)
# If 'problem_list' is in the response, then we got a list of problems from the ORA server. # If 'problem_list' is in the response, then we got a list of problems from the ORA server.
# If it is not, then ORA could not find any problems. # If it is not, then ORA could not find any problems.
...@@ -373,14 +397,14 @@ def save_grade(request, course_id): ...@@ -373,14 +397,14 @@ def save_grade(request, course_id):
location = p['location'] location = p['location']
try: try:
result_json = staff_grading_service().save_grade(course_id, result = staff_grading_service().save_grade(course_id,
grader_id, grader_id,
p['submission_id'], p['submission_id'],
p['score'], p['score'],
p['feedback'], p['feedback'],
skipped, skipped,
p.getlist('rubric_scores[]'), p.getlist('rubric_scores[]'),
p['submission_flagged']) p['submission_flagged'])
except GradingServiceError: except GradingServiceError:
#This is a dev_facing_error #This is a dev_facing_error
log.exception( log.exception(
...@@ -388,9 +412,6 @@ def save_grade(request, course_id): ...@@ -388,9 +412,6 @@ def save_grade(request, course_id):
request, course_id)) request, course_id))
#This is a staff_facing_error #This is a staff_facing_error
return _err_response(STAFF_ERROR_MESSAGE) return _err_response(STAFF_ERROR_MESSAGE)
try:
result = json.loads(result_json)
except ValueError: except ValueError:
#This is a dev_facing_error #This is a dev_facing_error
log.exception( log.exception(
...@@ -406,7 +427,7 @@ def save_grade(request, course_id): ...@@ -406,7 +427,7 @@ def save_grade(request, course_id):
return _err_response(STAFF_ERROR_MESSAGE) return _err_response(STAFF_ERROR_MESSAGE)
# Ok, save_grade seemed to work. Get the next submission to grade. # Ok, save_grade seemed to work. Get the next submission to grade.
return HttpResponse(_get_next(course_id, grader_id, location), return HttpResponse(json.dumps(_get_next(course_id, grader_id, location)),
mimetype="application/json") mimetype="application/json")
......
...@@ -45,7 +45,7 @@ class EmptyStaffGradingService(object): ...@@ -45,7 +45,7 @@ class EmptyStaffGradingService(object):
""" """
Return a staff grading response that is missing a problem list key. Return a staff grading response that is missing a problem list key.
""" """
return json.dumps({'success': True, 'error': 'No problems found.'}) return {'success': True, 'error': 'No problems found.'}
def make_instructor(course, user_email): def make_instructor(course, user_email):
...@@ -62,10 +62,9 @@ class StudentProblemListMockQuery(object): ...@@ -62,10 +62,9 @@ class StudentProblemListMockQuery(object):
def get_grading_status_list(self, *args, **kwargs): def get_grading_status_list(self, *args, **kwargs):
""" """
Get a mock grading status list with locations from the open_ended test course. Get a mock grading status list with locations from the open_ended test course.
@returns: json formatted grading status message. @returns: grading status message dictionary.
""" """
grading_status_list = json.dumps( return {
{
"version": 1, "version": 1,
"problem_list": [ "problem_list": [
{ {
...@@ -95,8 +94,6 @@ class StudentProblemListMockQuery(object): ...@@ -95,8 +94,6 @@ class StudentProblemListMockQuery(object):
], ],
"success": True "success": True
} }
)
return grading_status_list
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
......
...@@ -118,12 +118,10 @@ class StudentProblemList(object): ...@@ -118,12 +118,10 @@ class StudentProblemList(object):
self.success = False self.success = False
try: try:
#Get list of all open ended problems that the grading server knows about #Get list of all open ended problems that the grading server knows about
problem_list_json = self.controller_qs.get_grading_status_list(self.course_id, self.user_id) problem_list_dict = self.controller_qs.get_grading_status_list(self.course_id, self.user_id)
except GradingServiceError: except GradingServiceError:
log.error("Problem contacting open ended grading service " + self.course_error_ending) log.error("Problem contacting open ended grading service " + self.course_error_ending)
return self.success return self.success
try:
problem_list_dict = json.loads(problem_list_json)
except ValueError: except ValueError:
log.error("Problem with results from external grading service for open ended" + self.course_error_ending) log.error("Problem with results from external grading service for open ended" + self.course_error_ending)
return self.success return self.success
......
...@@ -197,8 +197,7 @@ def flagged_problem_list(request, course_id): ...@@ -197,8 +197,7 @@ def flagged_problem_list(request, course_id):
# Make a service that can query edX ORA. # Make a service that can query edX ORA.
controller_qs = create_controller_query_service() controller_qs = create_controller_query_service()
try: try:
problem_list_json = controller_qs.get_flagged_problem_list(course_id) problem_list_dict = controller_qs.get_flagged_problem_list(course_id)
problem_list_dict = json.loads(problem_list_json)
success = problem_list_dict['success'] success = problem_list_dict['success']
if 'error' in problem_list_dict: if 'error' in problem_list_dict:
error_text = problem_list_dict['error'] error_text = problem_list_dict['error']
...@@ -326,7 +325,7 @@ def take_action_on_flags(request, course_id): ...@@ -326,7 +325,7 @@ def take_action_on_flags(request, course_id):
controller_qs = create_controller_query_service() controller_qs = create_controller_query_service()
try: try:
response = controller_qs.take_action_on_flags(course_id, student_id, submission_id, action_type) response = controller_qs.take_action_on_flags(course_id, student_id, submission_id, action_type)
return HttpResponse(response, mimetype="application/json") return HttpResponse(json.dumps(response), mimetype="application/json")
except GradingServiceError: except GradingServiceError:
log.exception( log.exception(
u"Error taking action on flagged peer grading submissions, " u"Error taking action on flagged peer grading submissions, "
......
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