add analytics proxy endpoint
This commit is contained in:
@@ -5,6 +5,7 @@ Unit tests for instructor.api methods.
|
||||
import unittest
|
||||
import json
|
||||
from urllib import quote
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from nose.tools import raises
|
||||
from mock import Mock
|
||||
@@ -23,6 +24,7 @@ from student.models import CourseEnrollment
|
||||
from courseware.models import StudentModule
|
||||
|
||||
from instructor.access import allow_access
|
||||
import instructor.views.api
|
||||
from instructor.views.api import (
|
||||
_split_input_list, _msk_from_problem_urlname, common_exceptions_400)
|
||||
from instructor_task.api_helper import AlreadyRunningError
|
||||
@@ -118,6 +120,7 @@ class TestInstructorAPIDenyLevels(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
'list_instructor_tasks',
|
||||
'list_forum_members',
|
||||
'update_forum_role_membership',
|
||||
'proxy_legacy_analytics',
|
||||
]
|
||||
for endpoint in staff_level_endpoints:
|
||||
url = reverse(endpoint, kwargs={'course_id': self.course.id})
|
||||
@@ -753,6 +756,95 @@ class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
self.assertEqual(json.loads(response.content), expected_res)
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
|
||||
@override_settings(ANALYTICS_SERVER_URL="http://robotanalyticsserver.netbot:900/")
|
||||
@override_settings(ANALYTICS_API_KEY="robot_api_key")
|
||||
class TestInstructorAPIAnalyticsProxy(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
Test instructor analytics proxy endpoint.
|
||||
"""
|
||||
|
||||
class FakeProxyResponse(object):
|
||||
""" Fake successful requests response object. """
|
||||
def __init__(self):
|
||||
self.status_code = instructor.views.api.codes.OK
|
||||
self.content = '{"test_content": "robot test content"}'
|
||||
|
||||
class FakeBadProxyResponse(object):
|
||||
""" Fake strange-failed requests response object. """
|
||||
def __init__(self):
|
||||
self.status_code = 'notok.'
|
||||
self.content = '{"test_content": "robot test content"}'
|
||||
|
||||
def setUp(self):
|
||||
self.instructor = AdminFactory.create()
|
||||
self.course = CourseFactory.create()
|
||||
self.client.login(username=self.instructor.username, password='test')
|
||||
|
||||
def test_analytics_proxy_url(self):
|
||||
""" Test legacy analytics proxy url generation. """
|
||||
act = Mock(return_value=self.FakeProxyResponse())
|
||||
instructor.views.api.requests.get = act
|
||||
|
||||
url = reverse('proxy_legacy_analytics', kwargs={'course_id': self.course.id})
|
||||
response = self.client.get(url, {
|
||||
'aname': 'ProblemGradeDistribution'
|
||||
})
|
||||
print response.content
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# check request url
|
||||
expected_url = "{url}get?aname={aname}&course_id={course_id}&apikey={api_key}".format(
|
||||
url="http://robotanalyticsserver.netbot:900/",
|
||||
aname="ProblemGradeDistribution",
|
||||
course_id=self.course.id,
|
||||
api_key="robot_api_key",
|
||||
)
|
||||
act.assert_called_once_with(expected_url)
|
||||
|
||||
def test_analytics_proxy(self):
|
||||
"""
|
||||
Test legacy analytics content proxying.
|
||||
"""
|
||||
act = Mock(return_value=self.FakeProxyResponse())
|
||||
instructor.views.api.requests.get = act
|
||||
|
||||
url = reverse('proxy_legacy_analytics', kwargs={'course_id': self.course.id})
|
||||
response = self.client.get(url, {
|
||||
'aname': 'ProblemGradeDistribution'
|
||||
})
|
||||
print response.content
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# check response
|
||||
self.assertTrue(act.called)
|
||||
expected_res = {'test_content': "robot test content"}
|
||||
self.assertEqual(json.loads(response.content), expected_res)
|
||||
|
||||
def test_analytics_proxy_reqfailed(self):
|
||||
""" Test proxy when server reponds with failure. """
|
||||
act = Mock(return_value=self.FakeBadProxyResponse())
|
||||
instructor.views.api.requests.get = act
|
||||
|
||||
url = reverse('proxy_legacy_analytics', kwargs={'course_id': self.course.id})
|
||||
response = self.client.get(url, {
|
||||
'aname': 'ProblemGradeDistribution'
|
||||
})
|
||||
print response.content
|
||||
self.assertEqual(response.status_code, 500)
|
||||
|
||||
def test_analytics_proxy_missing_param(self):
|
||||
""" Test proxy when missing the aname query parameter. """
|
||||
act = Mock(return_value=self.FakeProxyResponse())
|
||||
instructor.views.api.requests.get = act
|
||||
|
||||
url = reverse('proxy_legacy_analytics', kwargs={'course_id': self.course.id})
|
||||
response = self.client.get(url, {})
|
||||
print response.content
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertFalse(act.called)
|
||||
|
||||
|
||||
class TestInstructorAPIHelpers(TestCase):
|
||||
""" Test helpers for instructor.api """
|
||||
def test_split_input_list(self):
|
||||
|
||||
@@ -8,11 +8,15 @@ Many of these GETs may become PUTs in the future.
|
||||
|
||||
import re
|
||||
import logging
|
||||
import requests
|
||||
from requests.status_codes import codes
|
||||
from collections import OrderedDict
|
||||
from django.conf import settings
|
||||
from django_future.csrf import ensure_csrf_cookie
|
||||
from django.views.decorators.cache import cache_control
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.http import HttpResponseBadRequest, HttpResponseForbidden
|
||||
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden
|
||||
from util.json_request import JsonResponse
|
||||
|
||||
from courseware.access import has_access
|
||||
@@ -725,6 +729,57 @@ def update_forum_role_membership(request, course_id):
|
||||
return JsonResponse(response_payload)
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
@require_level('staff')
|
||||
@require_query_params(
|
||||
aname="name of analytic to query",
|
||||
)
|
||||
@common_exceptions_400
|
||||
def proxy_legacy_analytics(request, course_id):
|
||||
"""
|
||||
Proxies to the analytics cron job server.
|
||||
|
||||
`aname` is a query parameter specifying which analytic to query.
|
||||
"""
|
||||
analytics_name = request.GET.get('aname')
|
||||
|
||||
# abort if misconfigured
|
||||
if not (hasattr(settings, 'ANALYTICS_SERVER_URL') and hasattr(settings, 'ANALYTICS_API_KEY')):
|
||||
return HttpResponse("Analytics service not configured.", status=501)
|
||||
|
||||
url = "{}get?aname={}&course_id={}&apikey={}".format(
|
||||
settings.ANALYTICS_SERVER_URL,
|
||||
analytics_name,
|
||||
course_id,
|
||||
settings.ANALYTICS_API_KEY,
|
||||
)
|
||||
|
||||
try:
|
||||
res = requests.get(url)
|
||||
except Exception:
|
||||
log.exception("Error requesting from analytics server at %s", url)
|
||||
return HttpResponse("Error requesting from analytics server.", status=500)
|
||||
|
||||
if res.status_code is 200:
|
||||
# return the successful request content
|
||||
return HttpResponse(res.content, content_type="application/json")
|
||||
elif res.status_code is 404:
|
||||
# forward the 404 and content
|
||||
return HttpResponse(res.content, content_type="application/json", status=404)
|
||||
else:
|
||||
# 500 on all other unexpected status codes.
|
||||
log.error(
|
||||
"Error fetching {}, code: {}, msg: {}".format(
|
||||
url, res.status_code, res.content
|
||||
)
|
||||
)
|
||||
return HttpResponse(
|
||||
"Error from analytics server ({}).".format(res.status_code),
|
||||
status=500
|
||||
)
|
||||
|
||||
|
||||
def _split_input_list(str_list):
|
||||
"""
|
||||
Separate out individual student email from the comma, or space separated string.
|
||||
|
||||
@@ -30,4 +30,6 @@ urlpatterns = patterns('', # nopep8
|
||||
'instructor.views.api.list_forum_members', name="list_forum_members"),
|
||||
url(r'^update_forum_role_membership$',
|
||||
'instructor.views.api.update_forum_role_membership', name="update_forum_role_membership"),
|
||||
url(r'^proxy_legacy_analytics$',
|
||||
'instructor.views.api.proxy_legacy_analytics', name="proxy_legacy_analytics"),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user