Skip to content

Commit

Permalink
Updated enrollment/gender endpoint to aggregate gender counts by day.
Browse files Browse the repository at this point in the history
  • Loading branch information
dsjen committed Oct 17, 2014
1 parent 646ad4a commit 4532b11
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 16 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Ben Patterson <bpatterson@edx.org>
Carlos Andrés Rocha <rocha@edx.org>
Clinton Blackburn <cblackburn@edx.org>
Dennis Jen <djen@edx.org>
Ed Zarecor <ed@edx.org>
Gabe Mulley <gabe@edx.org>
Jason Bau <jbau@stanford.edu>
Expand Down
10 changes: 7 additions & 3 deletions analytics_data_api/v0/serializers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from django.conf import settings
from rest_framework import serializers
from analytics_data_api.v0 import models
from analytics_data_api.v0.models import CourseActivityWeekly


class CourseActivityByWeekSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -97,9 +96,14 @@ class Meta(object):


class CourseEnrollmentByGenderSerializer(BaseCourseEnrollmentModelSerializer):
f = serializers.IntegerField(required=False)
m = serializers.IntegerField(required=False)
o = serializers.IntegerField(required=False)
u = serializers.IntegerField(required=False)

class Meta(object):
model = models.CourseEnrollmentByGender
fields = ('course_id', 'date', 'gender', 'count', 'created')
fields = ('course_id', 'date', 'f', 'm', 'o', 'u', 'created')


class CourseEnrollmentByEducationSerializer(BaseCourseEnrollmentModelSerializer):
Expand All @@ -126,6 +130,6 @@ class CourseActivityWeeklySerializer(serializers.ModelSerializer):
created = serializers.DateTimeField(format=settings.DATETIME_FORMAT)

class Meta(object):
model = CourseActivityWeekly
model = models.CourseActivityWeekly
# TODO: Add 'posted_forum' here to restore forum data
fields = ('interval_start', 'interval_end', 'course_id', 'any', 'attempted_problem', 'played_video', 'created')
34 changes: 28 additions & 6 deletions analytics_data_api/v0/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,18 +314,40 @@ class CourseEnrollmentByGenderViewTests(CourseEnrollmentViewTestCaseMixin, TestC

def generate_data(self, course_id=None):
course_id = course_id or self.course_id
G(self.model, course_id=course_id, gender='m', date=self.date, count=34)
G(self.model, course_id=course_id, gender='f', date=self.date, count=45)
G(self.model, course_id=course_id, gender='f', date=self.date - datetime.timedelta(days=2), count=45)
genders = ['f', 'm', 'o', 'u']
days = 2

for day in range(days):
for gender in genders:
G(self.model,
course_id=course_id,
date=self.date - datetime.timedelta(days=day),
gender=gender,
count=100 + day)

def setUp(self):
super(CourseEnrollmentByGenderViewTests, self).setUp()
self.generate_data()

def format_as_response(self, *args):
return [
{'course_id': unicode(ce.course_id), 'count': ce.count, 'date': ce.date.strftime(settings.DATE_FORMAT),
'gender': ce.gender, 'created': ce.created.strftime(settings.DATETIME_FORMAT)} for ce in args]
response = []

# Group by date
for _key, group in groupby(args, lambda x: x.date):
# Iterate over groups and create a single item with genders
item = {}

for enrollment in group:
item.update({
'created': enrollment.created.strftime(settings.DATETIME_FORMAT),
'course_id': unicode(enrollment.course_id),
'date': enrollment.date.strftime(settings.DATE_FORMAT),
enrollment.gender: enrollment.count
})

response.append(item)

return response


# pylint: disable=no-member,no-value-for-parameter
Expand Down
36 changes: 29 additions & 7 deletions analytics_data_api/v0/views/courses.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class CourseActivityWeeklyView(BaseCourseView):
<dd>The number of unique users who created a new post, responded to a post, or submitted a comment on any forum in the course.</dd>
</dl>
If no start or end dates are passed, the data for the latest date is returned. All dates should are in the UTC zone.
If no start or end dates are passed, the data for the latest date is returned. All dates are in the UTC zone.
Data is sorted chronologically (earliest to latest).
Expand Down Expand Up @@ -248,7 +248,7 @@ class CourseEnrollmentByBirthYearView(BaseCourseEnrollmentView):
Returns the enrollment of a course with users binned by their birth years.
If no start or end dates are passed, the data for the latest date is returned. All dates should are in the UTC zone.
If no start or end dates are passed, the data for the latest date is returned. All dates are in the UTC zone.
Data is sorted chronologically (earliest to latest).
Expand All @@ -269,7 +269,7 @@ class CourseEnrollmentByEducationView(BaseCourseEnrollmentView):
Returns the enrollment of a course with users binned by their education levels.
If no start or end dates are passed, the data for the latest date is returned. All dates should are in the UTC zone.
If no start or end dates are passed, the data for the latest date is returned. All dates are in the UTC zone.
Data is sorted chronologically (earliest to latest).
Expand All @@ -287,14 +287,15 @@ class CourseEnrollmentByGenderView(BaseCourseEnrollmentView):
"""
Course enrollment broken down by user gender
Returns the enrollment of a course with users binned by their genders.
Returns the enrollment of a course where each row/item contains user genders for the day.
Genders:
m - male
f - female
o - other
u - unknown
If no start or end dates are passed, the data for the latest date is returned. All dates should are in the UTC zone.
If no start or end dates are passed, the data for the latest date is returned. All dates are in the UTC zone.
Data is sorted chronologically (earliest to latest).
Expand All @@ -307,12 +308,33 @@ class CourseEnrollmentByGenderView(BaseCourseEnrollmentView):
serializer_class = serializers.CourseEnrollmentByGenderSerializer
model = models.CourseEnrollmentByGender

def get_queryset(self):
queryset = super(CourseEnrollmentByGenderView, self).get_queryset()
formatted_data = []

for key, group in groupby(queryset, lambda x: (x.course_id, x.date)):
# Iterate over groups and create a single item with gender data
item = {
u'course_id': key[0],
u'date': key[1],
u'created': None
}

for enrollment in group:
gender = enrollment.gender.lower()
item[gender] = enrollment.count
item[u'created'] = max(enrollment.created, item[u'created']) if item[u'created'] else enrollment.created

formatted_data.append(item)

return formatted_data


class CourseEnrollmentView(BaseCourseEnrollmentView):
"""
Returns the enrollment count for the specified course.
If no start or end dates are passed, the data for the latest date is returned. All dates should are in the UTC zone.
If no start or end dates are passed, the data for the latest date is returned. All dates are in the UTC zone.
Data is sorted chronologically (earliest to latest).
Expand All @@ -337,7 +359,7 @@ class CourseEnrollmentByLocationView(BaseCourseEnrollmentView):
Countries are denoted by their <a href="http://www.iso.org/iso/country_codes/country_codes" target="_blank">ISO 3166 country code</a>.
If no start or end dates are passed, the data for the latest date is returned. All dates should are in the UTC zone.
If no start or end dates are passed, the data for the latest date is returned. All dates are in the UTC zone.
Data is sorted chronologically (earliest to latest).
Expand Down

0 comments on commit 4532b11

Please sign in to comment.