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
9b23fed0
Commit
9b23fed0
authored
Nov 04, 2014
by
Clinton Blackburn
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #39 from edx/serializer-defaults
Updated Demographics and Enrollment Mode Serializers with Defaults
parents
1e0124df
376dce83
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
109 additions
and
25 deletions
+109
-25
analytics_data_api/constants/genders.py
+1
-0
analytics_data_api/v0/serializers.py
+25
-5
analytics_data_api/v0/tests/test_views.py
+82
-19
requirements/base.txt
+1
-1
No files found.
analytics_data_api/constants/genders.py
View file @
9b23fed0
...
...
@@ -2,3 +2,4 @@ FEMALE = u'female'
MALE
=
u'male'
OTHER
=
u'other'
UNKNOWN
=
u'unknown'
ALL
=
[
FEMALE
,
MALE
,
OTHER
,
UNKNOWN
]
analytics_data_api/v0/serializers.py
View file @
9b23fed0
from
django.conf
import
settings
from
rest_framework
import
serializers
from
analytics_data_api.constants
import
enrollment_modes
from
analytics_data_api.constants
import
enrollment_modes
,
genders
from
analytics_data_api.v0
import
models
...
...
@@ -90,6 +90,9 @@ class BaseCourseEnrollmentModelSerializer(serializers.ModelSerializer):
date
=
serializers
.
DateField
(
format
=
settings
.
DATE_FORMAT
)
created
=
serializers
.
DateTimeField
(
format
=
settings
.
DATETIME_FORMAT
)
def
default_if_none
(
self
,
value
,
default
=
0
):
return
value
if
value
is
not
None
else
default
class
CourseEnrollmentDailySerializer
(
BaseCourseEnrollmentModelSerializer
):
""" Representation of course enrollment for a single day and course. """
...
...
@@ -110,8 +113,14 @@ class CourseEnrollmentModeDailySerializer(BaseCourseEnrollmentModelSerializer):
for
mode
in
enrollment_modes
.
ALL
:
fields
[
mode
]
=
serializers
.
IntegerField
(
required
=
True
,
default
=
0
)
# Create a transform method for each field
setattr
(
self
,
'transform_
%
s'
%
mode
,
self
.
_transform_mode
)
return
fields
def
_transform_mode
(
self
,
_obj
,
value
):
return
self
.
default_if_none
(
value
,
0
)
class
Meta
(
object
):
model
=
models
.
CourseEnrollmentDaily
...
...
@@ -142,10 +151,21 @@ class CourseEnrollmentByCountrySerializer(BaseCourseEnrollmentModelSerializer):
class
CourseEnrollmentByGenderSerializer
(
BaseCourseEnrollmentModelSerializer
):
female
=
serializers
.
IntegerField
(
required
=
False
)
male
=
serializers
.
IntegerField
(
required
=
False
)
other
=
serializers
.
IntegerField
(
required
=
False
)
unknown
=
serializers
.
IntegerField
(
required
=
False
)
def
get_default_fields
(
self
):
# pylint: disable=super-on-old-class
fields
=
super
(
CourseEnrollmentByGenderSerializer
,
self
)
.
get_default_fields
()
# Create a field for each gender
for
gender
in
genders
.
ALL
:
fields
[
gender
]
=
serializers
.
IntegerField
(
required
=
True
,
default
=
0
)
# Create a transform method for each field
setattr
(
self
,
'transform_
%
s'
%
gender
,
self
.
_transform_gender
)
return
fields
def
_transform_gender
(
self
,
_obj
,
value
):
return
self
.
default_if_none
(
value
,
0
)
class
Meta
(
object
):
model
=
models
.
CourseEnrollmentByGender
...
...
analytics_data_api/v0/tests/test_views.py
View file @
9b23fed0
...
...
@@ -15,7 +15,7 @@ import pytz
from
opaque_keys.edx.keys
import
CourseKey
from
analytics_data_api.v0
import
models
from
analytics_data_api.constants
import
country
,
enrollment_modes
from
analytics_data_api.constants
import
country
,
enrollment_modes
,
genders
from
analytics_data_api.v0.models
import
CourseActivityWeekly
from
analytics_data_api.v0.serializers
import
ProblemResponseAnswerDistributionSerializer
from
analytics_data_api.v0.serializers
import
GradeDistributionSerializer
...
...
@@ -37,6 +37,20 @@ class DemoCourseMixin(object):
super
(
DemoCourseMixin
,
self
)
.
setUp
()
class
DefaultFillTestMixin
(
object
):
"""
Test that the view fills in missing data with a default value.
"""
model
=
None
def
destroy_data
(
self
):
self
.
model
.
objects
.
all
()
.
delete
()
def
test_default_fill
(
self
):
raise
NotImplementedError
# pylint: disable=no-member
class
CourseViewTestCaseMixin
(
DemoCourseMixin
):
model
=
None
...
...
@@ -75,16 +89,19 @@ class CourseViewTestCaseMixin(DemoCourseMixin):
response
=
self
.
authenticated_get
(
u'
%
scourses/
%
s
%
s'
%
(
self
.
api_root_path
,
course_id
,
self
.
path
))
self
.
assertEquals
(
response
.
status_code
,
404
)
def
test_get
(
self
):
""" Verify the endpoint returns an HTTP 200 status and the correct data. """
def
assertViewReturnsExpectedData
(
self
,
expected
):
# Validate the basic response status
response
=
self
.
authenticated_get
(
u'
%
scourses/
%
s
%
s'
%
(
self
.
api_root_path
,
self
.
course_id
,
self
.
path
))
self
.
assertEquals
(
response
.
status_code
,
200
)
# Validate the data is correct and sorted chronologically
expected
=
self
.
format_as_response
(
*
self
.
get_latest_data
())
self
.
assertEquals
(
response
.
data
,
expected
)
def
test_get
(
self
):
""" Verify the endpoint returns an HTTP 200 status and the correct data. """
expected
=
self
.
format_as_response
(
*
self
.
get_latest_data
())
self
.
assertViewReturnsExpectedData
(
expected
)
def
assertCSVIsValid
(
self
,
course_id
,
filename
):
path
=
u'{0}courses/{1}{2}'
.
format
(
self
.
api_root_path
,
course_id
,
self
.
path
)
csv_content_type
=
'text/csv'
...
...
@@ -307,7 +324,8 @@ class CourseEnrollmentByEducationViewTests(CourseEnrollmentViewTestCaseMixin, Te
ce
in
args
]
class
CourseEnrollmentByGenderViewTests
(
CourseEnrollmentViewTestCaseMixin
,
TestCaseWithAuthentication
):
class
CourseEnrollmentByGenderViewTests
(
CourseEnrollmentViewTestCaseMixin
,
DefaultFillTestMixin
,
TestCaseWithAuthentication
):
path
=
'/enrollment/gender/'
model
=
models
.
CourseEnrollmentByGender
order_by
=
[
'gender'
]
...
...
@@ -315,11 +333,11 @@ class CourseEnrollmentByGenderViewTests(CourseEnrollmentViewTestCaseMixin, TestC
def
generate_data
(
self
,
course_id
=
None
):
course_id
=
course_id
or
self
.
course_id
genders
=
[
'f'
,
'm'
,
'o'
,
None
]
_
genders
=
[
'f'
,
'm'
,
'o'
,
None
]
days
=
2
for
day
in
range
(
days
):
for
gender
in
genders
:
for
gender
in
_
genders
:
G
(
self
.
model
,
course_id
=
course_id
,
date
=
self
.
date
-
datetime
.
timedelta
(
days
=
day
),
...
...
@@ -330,6 +348,14 @@ class CourseEnrollmentByGenderViewTests(CourseEnrollmentViewTestCaseMixin, TestC
super
(
CourseEnrollmentByGenderViewTests
,
self
)
.
setUp
()
self
.
generate_data
()
def
serialize_enrollment
(
self
,
enrollment
):
return
{
'created'
:
enrollment
.
created
.
strftime
(
settings
.
DATETIME_FORMAT
),
'course_id'
:
unicode
(
enrollment
.
course_id
),
'date'
:
enrollment
.
date
.
strftime
(
settings
.
DATE_FORMAT
),
enrollment
.
cleaned_gender
:
enrollment
.
count
}
def
format_as_response
(
self
,
*
args
):
response
=
[]
...
...
@@ -339,17 +365,30 @@ class CourseEnrollmentByGenderViewTests(CourseEnrollmentViewTestCaseMixin, TestC
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
.
cleaned_gender
:
enrollment
.
count
})
item
.
update
(
self
.
serialize_enrollment
(
enrollment
))
response
.
append
(
item
)
return
response
def
test_default_fill
(
self
):
self
.
destroy_data
()
# Create a single entry for a single gender
enrollment
=
G
(
self
.
model
,
course_id
=
self
.
course_id
,
date
=
self
.
date
,
gender
=
'f'
,
count
=
1
)
# Create the expected data
_genders
=
list
(
genders
.
ALL
)
_genders
.
remove
(
genders
.
FEMALE
)
expected
=
self
.
serialize_enrollment
(
enrollment
)
for
gender
in
_genders
:
expected
[
gender
]
=
0
expected
=
[
expected
]
self
.
assertViewReturnsExpectedData
(
expected
)
# pylint: disable=no-member,no-value-for-parameter
class
AnswerDistributionTests
(
TestCaseWithAuthentication
):
...
...
@@ -403,7 +442,8 @@ class CourseEnrollmentViewTests(CourseEnrollmentViewTestCaseMixin, TestCaseWithA
for
ce
in
args
]
class
CourseEnrollmentModeViewTests
(
CourseEnrollmentViewTestCaseMixin
,
TestCaseWithAuthentication
):
class
CourseEnrollmentModeViewTests
(
CourseEnrollmentViewTestCaseMixin
,
DefaultFillTestMixin
,
TestCaseWithAuthentication
):
model
=
models
.
CourseEnrollmentModeDaily
path
=
'/enrollment/mode'
csv_filename_slug
=
u'enrollment_mode'
...
...
@@ -418,13 +458,17 @@ class CourseEnrollmentModeViewTests(CourseEnrollmentViewTestCaseMixin, TestCaseW
for
mode
in
enrollment_modes
.
ALL
:
G
(
self
.
model
,
course_id
=
course_id
,
date
=
self
.
date
,
mode
=
mode
)
def
serialize_enrollment
(
self
,
enrollment
):
return
{
u'course_id'
:
enrollment
.
course_id
,
u'date'
:
enrollment
.
date
.
strftime
(
settings
.
DATE_FORMAT
),
u'created'
:
enrollment
.
created
.
strftime
(
settings
.
DATETIME_FORMAT
),
enrollment
.
mode
:
enrollment
.
count
}
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
)
}
response
=
self
.
serialize_enrollment
(
arg
)
total
=
0
for
ce
in
args
:
...
...
@@ -435,6 +479,25 @@ class CourseEnrollmentModeViewTests(CourseEnrollmentViewTestCaseMixin, TestCaseW
return
[
response
]
def
test_default_fill
(
self
):
self
.
destroy_data
()
# Create a single entry for a single enrollment mode
enrollment
=
G
(
self
.
model
,
course_id
=
self
.
course_id
,
date
=
self
.
date
,
mode
=
enrollment_modes
.
AUDIT
,
count
=
1
)
# Create the expected data
modes
=
list
(
enrollment_modes
.
ALL
)
modes
.
remove
(
enrollment_modes
.
AUDIT
)
expected
=
self
.
serialize_enrollment
(
enrollment
)
expected
[
u'count'
]
=
1
for
mode
in
modes
:
expected
[
mode
]
=
0
expected
=
[
expected
]
self
.
assertViewReturnsExpectedData
(
expected
)
class
CourseEnrollmentByLocationViewTests
(
CourseEnrollmentViewTestCaseMixin
,
TestCaseWithAuthentication
):
path
=
'/enrollment/location/'
...
...
requirements/base.txt
View file @
9b23fed0
...
...
@@ -2,7 +2,7 @@ Django==1.6.6 # BSD License
Markdown==2.4.1 # BSD
South==1.0 # Apache License
django-model-utils==1.4.0 # BSD
djangorestframework==2.
3.5
# BSD
djangorestframework==2.
4.4
# BSD
ipython==2.1.0 # BSD
django-rest-swagger==0.1.14 # BSD
djangorestframework-csv==1.3.3 # BSD
...
...
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