Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-analytics-data-api
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-analytics-data-api
Commits
8ac02398
Commit
8ac02398
authored
Oct 23, 2014
by
Clinton Blackburn
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added Enrollment Mode
parent
5001a93a
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
139 additions
and
19 deletions
+139
-19
analytics_data_api/constants/__init__.py
+0
-5
analytics_data_api/constants/enrollment_modes.py
+6
-0
analytics_data_api/constants/genders.py
+4
-0
analytics_data_api/management/commands/generate_fake_course_data.py
+14
-1
analytics_data_api/v0/models.py
+14
-5
analytics_data_api/v0/serializers.py
+21
-0
analytics_data_api/v0/tests/test_models.py
+1
-1
analytics_data_api/v0/tests/test_views.py
+34
-1
analytics_data_api/v0/urls/courses.py
+1
-0
analytics_data_api/v0/views/courses.py
+44
-6
No files found.
analytics_data_api/
v0/constants
.py
→
analytics_data_api/
constants/__init__
.py
View file @
8ac02398
...
...
@@ -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'
analytics_data_api/constants/enrollment_modes.py
0 → 100644
View file @
8ac02398
AUDIT
=
u'audit'
HONOR
=
u'honor'
PROFESSIONAL
=
u'professional'
VERIFIED
=
u'verified'
ALL
=
[
AUDIT
,
HONOR
,
PROFESSIONAL
,
VERIFIED
]
analytics_data_api/constants/genders.py
0 → 100644
View file @
8ac02398
FEMALE
=
u'female'
MALE
=
u'male'
OTHER
=
u'other'
UNKNOWN
=
u'unknown'
analytics_data_api/management/commands/generate_fake_course_data.py
View file @
8ac02398
...
...
@@ -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
,
...
...
analytics_data_api/v0/models.py
View file @
8ac02398
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_GEND
ER
u'f'
:
genders
.
FEMALE
,
u'm'
:
genders
.
MALE
,
u'o'
:
genders
.
OTH
ER
}
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'
...
...
analytics_data_api/v0/serializers.py
View file @
8ac02398
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
...
...
analytics_data_api/v0/tests/test_models.py
View file @
8ac02398
...
...
@@ -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
):
...
...
analytics_data_api/v0/tests/test_views.py
View file @
8ac02398
...
...
@@ -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
...
...
analytics_data_api/v0/urls/courses.py
View file @
8ac02398
...
...
@@ -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'
),
...
...
analytics_data_api/v0/views/courses.py
View file @
8ac02398
...
...
@@ -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
):
"""
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment