Commit 8ac02398 by Clinton Blackburn

Added Enrollment Mode

parent 5001a93a
......@@ -2,8 +2,3 @@ from iso3166 import Country
UNKNOWN_COUNTRY_CODE = u'UNKNOWN'
UNKNOWN_COUNTRY = Country(UNKNOWN_COUNTRY_CODE, None, None, None)
FEMALE_GENDER = u'female'
MALE_GENDER = u'male'
OTHER_GENDER = u'other'
UNKNOWN_GENDER = u'unknown'
AUDIT = u'audit'
HONOR = u'honor'
PROFESSIONAL = u'professional'
VERIFIED = u'verified'
ALL = [AUDIT, HONOR, PROFESSIONAL, VERIFIED]
FEMALE = u'female'
MALE = u'male'
OTHER = u'other'
UNKNOWN = u'unknown'
......@@ -31,7 +31,8 @@ def get_count(start):
class Command(BaseCommand):
help = 'Generate fake data'
option_list = BaseCommand.option_list + (
make_option('-n', '--num-weeks', action='store', type="int", dest='num_weeks', help='"Number of weeks worth of data to generate.'),
make_option('-n', '--num-weeks', action='store', type="int", dest='num_weeks',
help='"Number of weeks worth of data to generate.'),
)
def generate_daily_data(self, course_id, start_date, end_date):
......@@ -63,6 +64,13 @@ class Command(BaseCommand):
'UNKNOWN': 0.01
}
enrollment_mode_ratios = {
'audit': 0.15,
'honor': 0.35,
'professional': 0.10,
'verified': 0.40
}
# Generate birth year ratios
birth_years = range(1960, 2005)
ratios = [n / 1000.0 for n in constrained_sum_sample_pos(len(birth_years), 1000)]
......@@ -87,6 +95,11 @@ class Command(BaseCommand):
daily_total = get_count(daily_total)
models.CourseEnrollmentDaily.objects.create(course_id=course_id, date=date, count=daily_total)
for mode, ratio in enrollment_mode_ratios.iteritems():
count = int(ratio * daily_total)
models.CourseEnrollmentModeDaily.objects.create(course_id=course_id, date=date, count=count,
mode=mode)
for gender, ratio in gender_ratios.iteritems():
count = int(ratio * daily_total)
models.CourseEnrollmentByGender.objects.create(course_id=course_id, date=date, count=count,
......
from django.db import models
from iso3166 import countries
from analytics_data_api.v0.constants import UNKNOWN_COUNTRY, FEMALE_GENDER, MALE_GENDER, OTHER_GENDER, UNKNOWN_GENDER
from analytics_data_api.constants import UNKNOWN_COUNTRY, genders
class CourseActivityWeekly(models.Model):
......@@ -45,6 +45,15 @@ class CourseEnrollmentDaily(BaseCourseEnrollment):
unique_together = [('course_id', 'date',)]
class CourseEnrollmentModeDaily(BaseCourseEnrollment):
mode = models.CharField(max_length=255)
class Meta(BaseCourseEnrollment.Meta):
db_table = 'course_enrollment_mode_daily'
ordering = ('date', 'course_id', 'mode')
unique_together = [('course_id', 'date', 'mode')]
class CourseEnrollmentByBirthYear(BaseCourseEnrollment):
birth_year = models.IntegerField(null=False)
......@@ -76,9 +85,9 @@ class CourseEnrollmentByEducation(BaseCourseEnrollment):
class CourseEnrollmentByGender(BaseCourseEnrollment):
CLEANED_GENDERS = {
'f': FEMALE_GENDER,
'm': MALE_GENDER,
'o': OTHER_GENDER
u'f': genders.FEMALE,
u'm': genders.MALE,
u'o': genders.OTHER
}
gender = models.CharField(max_length=255, null=True, db_column='gender')
......@@ -88,7 +97,7 @@ class CourseEnrollmentByGender(BaseCourseEnrollment):
"""
Returns the gender with full names and 'unknown' replacing null/None.
"""
return self.CLEANED_GENDERS.get(self.gender, UNKNOWN_GENDER)
return self.CLEANED_GENDERS.get(self.gender, genders.UNKNOWN)
class Meta(BaseCourseEnrollment.Meta):
db_table = 'course_enrollment_gender_daily'
......
from django.conf import settings
from rest_framework import serializers
from analytics_data_api.constants import enrollment_modes
from analytics_data_api.v0 import models
......@@ -98,6 +99,26 @@ class CourseEnrollmentDailySerializer(BaseCourseEnrollmentModelSerializer):
fields = ('course_id', 'date', 'count', 'created')
class CourseEnrollmentModeDailySerializer(BaseCourseEnrollmentModelSerializer):
""" Representation of course enrollment, broken down by mode, for a single day and course. """
def get_default_fields(self):
# pylint: disable=super-on-old-class
fields = super(CourseEnrollmentModeDailySerializer, self).get_default_fields()
# Create a field for each enrollment mode
for mode in enrollment_modes.ALL:
fields[mode] = serializers.IntegerField(required=True)
return fields
class Meta(object):
model = models.CourseEnrollmentDaily
# Declare the dynamically-created fields here as well so that they will be picked up by Swagger.
fields = ['course_id', 'date', 'count', 'created'] + enrollment_modes.ALL
class CountrySerializer(serializers.Serializer):
"""
Serialize country to an object with fields for the complete country name
......
......@@ -3,7 +3,7 @@ from django_dynamic_fixture import G
from iso3166 import countries
from analytics_data_api.v0 import models
from analytics_data_api.v0.constants import UNKNOWN_COUNTRY
from analytics_data_api.constants import UNKNOWN_COUNTRY
class EducationLevelTests(TestCase):
......
......@@ -15,7 +15,7 @@ import pytz
from opaque_keys.edx.keys import CourseKey
from analytics_data_api.v0 import models
from analytics_data_api.v0.constants import UNKNOWN_COUNTRY, UNKNOWN_COUNTRY_CODE
from analytics_data_api.constants import UNKNOWN_COUNTRY, UNKNOWN_COUNTRY_CODE, enrollment_modes
from analytics_data_api.v0.models import CourseActivityWeekly
from analytics_data_api.v0.serializers import ProblemResponseAnswerDistributionSerializer
from analytics_data_api.v0.serializers import GradeDistributionSerializer
......@@ -404,6 +404,39 @@ class CourseEnrollmentViewTests(CourseEnrollmentViewTestCaseMixin, TestCaseWithA
for ce in args]
class CourseEnrollmentModeViewTests(CourseEnrollmentViewTestCaseMixin, TestCaseWithAuthentication):
model = models.CourseEnrollmentModeDaily
path = '/enrollment/mode'
csv_filename_slug = u'enrollment_mode'
def setUp(self):
super(CourseEnrollmentModeViewTests, self).setUp()
self.generate_data()
def generate_data(self, course_id=None):
course_id = course_id or self.course_id
for mode in enrollment_modes.ALL:
G(self.model, course_id=course_id, date=self.date, mode=mode)
def format_as_response(self, *args):
arg = args[0]
response = {
u'course_id': arg.course_id,
u'date': arg.date.strftime(settings.DATE_FORMAT),
u'created': arg.created.strftime(settings.DATETIME_FORMAT)
}
total = 0
for ce in args:
total += ce.count
response[ce.mode] = ce.count
response[u'count'] = total
return [response]
class CourseEnrollmentByLocationViewTests(CourseEnrollmentViewTestCaseMixin, TestCaseWithAuthentication):
path = '/enrollment/location/'
model = models.CourseEnrollmentByCountry
......
......@@ -7,6 +7,7 @@ COURSE_URLS = [
('activity', views.CourseActivityWeeklyView, 'activity'),
('recent_activity', views.CourseActivityMostRecentWeekView, 'recent_activity'),
('enrollment', views.CourseEnrollmentView, 'enrollment_latest'),
('enrollment/mode', views.CourseEnrollmentModeView, 'enrollment_by_mode'),
('enrollment/birth_year', views.CourseEnrollmentByBirthYearView, 'enrollment_by_birth_year'),
('enrollment/education', views.CourseEnrollmentByEducationView, 'enrollment_by_education'),
('enrollment/gender', views.CourseEnrollmentByGenderView, 'enrollment_by_gender'),
......
......@@ -289,12 +289,6 @@ class CourseEnrollmentByGenderView(BaseCourseEnrollmentView):
Returns the enrollment of a course where each row/item contains user genders for the day.
Genders:
male
female
other
unknown
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).
......@@ -350,6 +344,50 @@ class CourseEnrollmentView(BaseCourseEnrollmentView):
model = models.CourseEnrollmentDaily
class CourseEnrollmentModeView(BaseCourseEnrollmentView):
"""
Course enrollment broken down by enrollment mode.
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).
Date format: YYYY-mm-dd (e.g. 2014-01-31)
start_date -- Date after which all data should be returned (inclusive)
end_date -- Date before which all data should be returned (exclusive)
"""
slug = u'enrollment_mode'
serializer_class = serializers.CourseEnrollmentModeDailySerializer
model = models.CourseEnrollmentModeDaily
def get_queryset(self):
queryset = super(CourseEnrollmentModeView, self).get_queryset()
formatted_data = []
for key, group in groupby(queryset, lambda x: (x.course_id, x.date)):
item = {
u'course_id': key[0],
u'date': key[1],
u'created': None
}
total = 0
for enrollment in group:
mode = enrollment.mode
item[mode] = enrollment.count
item[u'created'] = max(enrollment.created, item[u'created']) if item[u'created'] else enrollment.created
total += enrollment.count
item[u'count'] = total
formatted_data.append(item)
return formatted_data
# pylint: disable=line-too-long
class CourseEnrollmentByLocationView(BaseCourseEnrollmentView):
"""
......
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