Commit 5f21b965 by Jason Bau

Merge pull request #35 from dylanrhodes/dylanrhodes/aggregate_data_apis

Added REST API for Grade Distribution and Sequential Open Distribution
parents 646ad4a1 e533cdf1
......@@ -119,3 +119,29 @@ class CourseEnrollmentByCountry(BaseCourseEnrollment):
db_table = 'course_enrollment_location_current'
ordering = ('date', 'course_id', 'country_code')
unique_together = [('course_id', 'date', 'country_code')]
class GradeDistribution(models.Model):
""" Each row stores the count of a particular grade on a module for a given course. """
class Meta(object):
db_table = 'grade_distribution'
module_id = models.CharField(db_index=True, max_length=255)
course_id = models.CharField(db_index=True, max_length=255)
grade = models.IntegerField()
max_grade = models.IntegerField()
count = models.IntegerField()
created = models.DateTimeField(auto_now_add=True)
class SequentialOpenDistribution(models.Model):
""" Each row stores the count of views a particular module has had in a given course. """
class Meta(object):
db_table = 'sequential_open_distribution'
module_id = models.CharField(db_index=True, max_length=255)
course_id = models.CharField(db_index=True, max_length=255)
count = models.IntegerField()
created = models.DateTimeField(auto_now_add=True)
......@@ -54,6 +54,38 @@ class ProblemResponseAnswerDistributionSerializer(serializers.ModelSerializer):
)
class GradeDistributionSerializer(serializers.ModelSerializer):
"""
Representation of the grade_distribution table without id
"""
class Meta(object):
model = models.GradeDistribution
fields = (
'module_id',
'course_id',
'grade',
'max_grade',
'count',
'created'
)
class SequentialOpenDistributionSerializer(serializers.ModelSerializer):
"""
Representation of the sequential_open_distribution table without id
"""
class Meta(object):
model = models.SequentialOpenDistribution
fields = (
'module_id',
'course_id',
'count',
'created'
)
class BaseCourseEnrollmentModelSerializer(serializers.ModelSerializer):
date = serializers.DateField(format=settings.DATE_FORMAT)
created = serializers.DateTimeField(format=settings.DATETIME_FORMAT)
......
......@@ -18,6 +18,8 @@ from analytics_data_api.v0 import models
from analytics_data_api.v0.constants import UNKNOWN_COUNTRY, UNKNOWN_COUNTRY_CODE
from analytics_data_api.v0.models import CourseActivityWeekly
from analytics_data_api.v0.serializers import ProblemResponseAnswerDistributionSerializer
from analytics_data_api.v0.serializers import GradeDistributionSerializer
from analytics_data_api.v0.serializers import SequentialOpenDistributionSerializer
from analytics_data_api.v0.tests.utils import flatten
from analyticsdataserver.tests import TestCaseWithAuthentication
......@@ -498,3 +500,61 @@ class CourseActivityWeeklyViewTests(CourseViewTestCaseMixin, TestCaseWithAuthent
expected = self.format_as_response(*self.model.objects.all())
self.assertEqual(len(expected), 2)
self.assertIntervalFilteringWorks(expected, self.interval_start, interval_end + datetime.timedelta(days=1))
# pylint: disable=no-member,no-value-for-parameter
class GradeDistributionTests(TestCaseWithAuthentication):
path = '/grade_distribution'
maxDiff = None
@classmethod
def setUpClass(cls):
cls.course_id = "org/class/test"
cls.module_id = "i4x://org/class/test/problem/RANDOM_NUMBER"
cls.ad1 = G(
models.GradeDistribution,
course_id=cls.course_id,
module_id=cls.module_id,
)
def test_get(self):
response = self.authenticated_get('/api/v0/problems/%s%s' % (self.module_id, self.path))
self.assertEquals(response.status_code, 200)
expected_dict = GradeDistributionSerializer(self.ad1).data
actual_list = response.data
self.assertEquals(len(actual_list), 1)
self.assertDictEqual(actual_list[0], expected_dict)
def test_get_404(self):
response = self.authenticated_get('/api/v0/problems/%s%s' % ("DOES-NOT-EXIST", self.path))
self.assertEquals(response.status_code, 404)
# pylint: disable=no-member,no-value-for-parameter
class SequentialOpenDistributionTests(TestCaseWithAuthentication):
path = '/sequential_open_distribution'
maxDiff = None
@classmethod
def setUpClass(cls):
cls.course_id = "org/class/test"
cls.module_id = "i4x://org/class/test/problem/RANDOM_NUMBER"
cls.ad1 = G(
models.SequentialOpenDistribution,
course_id=cls.course_id,
module_id=cls.module_id,
)
def test_get(self):
response = self.authenticated_get('/api/v0/problems/%s%s' % (self.module_id, self.path))
self.assertEquals(response.status_code, 200)
expected_dict = SequentialOpenDistributionSerializer(self.ad1).data
actual_list = response.data
self.assertEquals(len(actual_list), 1)
self.assertDictEqual(actual_list[0], expected_dict)
def test_get_404(self):
response = self.authenticated_get('/api/v0/problems/%s%s' % ("DOES-NOT-EXIST", self.path))
self.assertEquals(response.status_code, 404)
......@@ -3,13 +3,18 @@ import re
from django.conf.urls import patterns, url
from analytics_data_api.v0.views.problems import ProblemResponseAnswerDistributionView
from analytics_data_api.v0.views.problems import GradeDistributionView
from analytics_data_api.v0.views.problems import SequentialOpenDistributionView
PROBLEM_URLS = [
('answer_distribution', ProblemResponseAnswerDistributionView, 'answer_distribution'),
('grade_distribution', GradeDistributionView, 'grade_distribution'),
]
urlpatterns = patterns(
'',
url(r'^(?P<module_id>.+)/sequential_open_distribution$',
SequentialOpenDistributionView.as_view(), name='sequential_open_distribution'),
)
for path, view, name in PROBLEM_URLS:
......
......@@ -3,6 +3,12 @@ from rest_framework import generics
from analytics_data_api.v0.models import ProblemResponseAnswerDistribution
from analytics_data_api.v0.serializers import ProblemResponseAnswerDistributionSerializer
from analytics_data_api.v0.models import GradeDistribution
from analytics_data_api.v0.serializers import GradeDistributionSerializer
from analytics_data_api.v0.models import SequentialOpenDistribution
from analytics_data_api.v0.serializers import SequentialOpenDistributionSerializer
class ProblemResponseAnswerDistributionView(generics.ListAPIView):
"""
......@@ -18,3 +24,31 @@ class ProblemResponseAnswerDistributionView(generics.ListAPIView):
"""Select all the answer distribution response having to do with this usage of the problem."""
problem_id = self.kwargs.get('problem_id')
return ProblemResponseAnswerDistribution.objects.filter(module_id=problem_id)
class GradeDistributionView(generics.ListAPIView):
"""
Distribution of grades for a particular module in a given course
"""
serializer_class = GradeDistributionSerializer
allow_empty = False
def get_queryset(self):
"""Select all grade distributions for a particular module"""
problem_id = self.kwargs.get('problem_id')
return GradeDistribution.objects.filter(module_id=problem_id)
class SequentialOpenDistributionView(generics.ListAPIView):
"""
Distribution of view counts for a particular module in a given course
"""
serializer_class = SequentialOpenDistributionSerializer
allow_empty = False
def get_queryset(self):
"""Select the view count for a specific module"""
module_id = self.kwargs.get('module_id')
return SequentialOpenDistribution.objects.filter(module_id=module_id)
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