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
53f48f60
Commit
53f48f60
authored
Jul 10, 2014
by
Clinton Blackburn
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Replaced course_key with course_id
Change-Id: I33860a35165f42f67be8b5e0738d47540250f55b
parent
b4034977
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
42 additions
and
41 deletions
+42
-41
analytics_data_api/fixtures/courses.json
+1
-1
analytics_data_api/v0/managers.py
+2
-2
analytics_data_api/v0/models.py
+4
-3
analytics_data_api/v0/serializers.py
+4
-4
analytics_data_api/v0/tests.py
+25
-25
analytics_data_api/v0/urls/courses.py
+1
-1
analytics_data_api/v0/views/courses.py
+5
-5
No files found.
analytics_data_api/fixtures/courses.json
View file @
53f48f60
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
"pk"
:
1
,
"pk"
:
1
,
"model"
:
"v0.course"
,
"model"
:
"v0.course"
,
"fields"
:
{
"fields"
:
{
"course_
key
"
:
"edX/DemoX/Demo_Course"
"course_
id
"
:
"edX/DemoX/Demo_Course"
}
}
}
}
]
]
analytics_data_api/v0/managers.py
View file @
53f48f60
...
@@ -2,5 +2,5 @@ from django.db import models
...
@@ -2,5 +2,5 @@ from django.db import models
class
CourseManager
(
models
.
Manager
):
class
CourseManager
(
models
.
Manager
):
def
get_by_natural_key
(
self
,
course_
key
):
def
get_by_natural_key
(
self
,
course_
id
):
return
self
.
get
(
course_
key
=
course_key
)
return
self
.
get
(
course_
id
=
course_id
)
analytics_data_api/v0/models.py
View file @
53f48f60
...
@@ -3,8 +3,9 @@ from analytics_data_api.v0.managers import CourseManager
...
@@ -3,8 +3,9 @@ from analytics_data_api.v0.managers import CourseManager
class
Course
(
models
.
Model
):
class
Course
(
models
.
Model
):
course_id
=
models
.
CharField
(
unique
=
True
,
max_length
=
255
)
objects
=
CourseManager
()
# pylint: disable=no-value-for-parameter
objects
=
CourseManager
()
# pylint: disable=no-value-for-parameter
course_key
=
models
.
CharField
(
unique
=
True
,
max_length
=
255
)
class
Meta
(
object
):
class
Meta
(
object
):
db_table
=
'courses'
db_table
=
'courses'
...
@@ -23,9 +24,9 @@ class CourseActivityByWeek(models.Model):
...
@@ -23,9 +24,9 @@ class CourseActivityByWeek(models.Model):
count
=
models
.
IntegerField
()
count
=
models
.
IntegerField
()
@classmethod
@classmethod
def
get_most_recent
(
cls
,
course_
key
,
activity_type
):
def
get_most_recent
(
cls
,
course_
id
,
activity_type
):
"""Activity for the week that was mostly recently computed."""
"""Activity for the week that was mostly recently computed."""
return
cls
.
objects
.
filter
(
course__course_
key
=
course_key
,
activity_type
=
activity_type
)
.
latest
(
'interval_end'
)
return
cls
.
objects
.
filter
(
course__course_
id
=
course_id
,
activity_type
=
activity_type
)
.
latest
(
'interval_end'
)
class
BaseCourseEnrollment
(
models
.
Model
):
class
BaseCourseEnrollment
(
models
.
Model
):
...
...
analytics_data_api/v0/serializers.py
View file @
53f48f60
...
@@ -9,11 +9,11 @@ class CourseActivityByWeekSerializer(serializers.ModelSerializer):
...
@@ -9,11 +9,11 @@ class CourseActivityByWeekSerializer(serializers.ModelSerializer):
This table is managed by the data pipeline, and records can be removed and added at any time. The id for a
This table is managed by the data pipeline, and records can be removed and added at any time. The id for a
particular record is likely to change unexpectedly so we avoid exposing it.
particular record is likely to change unexpectedly so we avoid exposing it.
"""
"""
course_
key
=
serializers
.
SerializerMethodField
(
'get_course_key
'
)
course_
id
=
serializers
.
SerializerMethodField
(
'get_course_id
'
)
def
get_course_
key
(
self
,
obj
):
def
get_course_
id
(
self
,
obj
):
return
obj
.
course
.
course_
key
return
obj
.
course
.
course_
id
class
Meta
(
object
):
class
Meta
(
object
):
model
=
CourseActivityByWeek
model
=
CourseActivityByWeek
fields
=
(
'interval_start'
,
'interval_end'
,
'activity_type'
,
'count'
,
'course_
key
'
)
fields
=
(
'interval_start'
,
'interval_end'
,
'activity_type'
,
'count'
,
'course_
id
'
)
analytics_data_api/v0/tests.py
View file @
53f48f60
...
@@ -99,8 +99,8 @@ class OperationalEndpointsTest(TestCaseWithAuthentication):
...
@@ -99,8 +99,8 @@ class OperationalEndpointsTest(TestCaseWithAuthentication):
class
CourseActivityLastWeekTest
(
TestCaseWithAuthentication
):
class
CourseActivityLastWeekTest
(
TestCaseWithAuthentication
):
def
setUp
(
self
):
def
setUp
(
self
):
super
(
CourseActivityLastWeekTest
,
self
)
.
setUp
()
super
(
CourseActivityLastWeekTest
,
self
)
.
setUp
()
self
.
course_
key
=
'edX/DemoX/Demo_Course'
self
.
course_
id
=
'edX/DemoX/Demo_Course'
self
.
course
=
G
(
Course
,
course_
key
=
self
.
course_key
)
self
.
course
=
G
(
Course
,
course_
id
=
self
.
course_id
)
interval_start
=
'2014-05-24T00:00:00Z'
interval_start
=
'2014-05-24T00:00:00Z'
interval_end
=
'2014-06-01T00:00:00Z'
interval_end
=
'2014-06-01T00:00:00Z'
G
(
CourseActivityByWeek
,
course
=
self
.
course
,
interval_start
=
interval_start
,
interval_end
=
interval_end
,
G
(
CourseActivityByWeek
,
course
=
self
.
course
,
interval_start
=
interval_start
,
interval_end
=
interval_end
,
...
@@ -113,14 +113,14 @@ class CourseActivityLastWeekTest(TestCaseWithAuthentication):
...
@@ -113,14 +113,14 @@ class CourseActivityLastWeekTest(TestCaseWithAuthentication):
activity_type
=
'played_video'
,
count
=
400
)
activity_type
=
'played_video'
,
count
=
400
)
def
test_activity
(
self
):
def
test_activity
(
self
):
response
=
self
.
authenticated_get
(
'/api/v0/courses/{0}/recent_activity'
.
format
(
self
.
course_
key
))
response
=
self
.
authenticated_get
(
'/api/v0/courses/{0}/recent_activity'
.
format
(
self
.
course_
id
))
self
.
assertEquals
(
response
.
status_code
,
200
)
self
.
assertEquals
(
response
.
status_code
,
200
)
self
.
assertEquals
(
response
.
data
,
self
.
get_activity_record
())
self
.
assertEquals
(
response
.
data
,
self
.
get_activity_record
())
@staticmethod
@staticmethod
def
get_activity_record
(
**
kwargs
):
def
get_activity_record
(
**
kwargs
):
default
=
{
default
=
{
'course_
key
'
:
'edX/DemoX/Demo_Course'
,
'course_
id
'
:
'edX/DemoX/Demo_Course'
,
'interval_start'
:
datetime
(
2014
,
5
,
24
,
0
,
0
,
tzinfo
=
pytz
.
utc
),
'interval_start'
:
datetime
(
2014
,
5
,
24
,
0
,
0
,
tzinfo
=
pytz
.
utc
),
'interval_end'
:
datetime
(
2014
,
6
,
1
,
0
,
0
,
tzinfo
=
pytz
.
utc
),
'interval_end'
:
datetime
(
2014
,
6
,
1
,
0
,
0
,
tzinfo
=
pytz
.
utc
),
'activity_type'
:
'any'
,
'activity_type'
:
'any'
,
...
@@ -130,10 +130,10 @@ class CourseActivityLastWeekTest(TestCaseWithAuthentication):
...
@@ -130,10 +130,10 @@ class CourseActivityLastWeekTest(TestCaseWithAuthentication):
return
default
return
default
def
test_activity_auth
(
self
):
def
test_activity_auth
(
self
):
response
=
self
.
client
.
get
(
'/api/v0/courses/{0}/recent_activity'
.
format
(
self
.
course_
key
))
response
=
self
.
client
.
get
(
'/api/v0/courses/{0}/recent_activity'
.
format
(
self
.
course_
id
))
self
.
assertEquals
(
response
.
status_code
,
401
)
self
.
assertEquals
(
response
.
status_code
,
401
)
def
test_url_encoded_course_
key
(
self
):
def
test_url_encoded_course_
id
(
self
):
response
=
self
.
authenticated_get
(
'/api/v0/courses/edX
%2
FDemoX
%2
FDemo_Course/recent_activity'
)
response
=
self
.
authenticated_get
(
'/api/v0/courses/edX
%2
FDemoX
%2
FDemo_Course/recent_activity'
)
self
.
assertEquals
(
response
.
status_code
,
200
)
self
.
assertEquals
(
response
.
status_code
,
200
)
self
.
assertEquals
(
response
.
data
,
self
.
get_activity_record
())
self
.
assertEquals
(
response
.
data
,
self
.
get_activity_record
())
...
@@ -141,21 +141,21 @@ class CourseActivityLastWeekTest(TestCaseWithAuthentication):
...
@@ -141,21 +141,21 @@ class CourseActivityLastWeekTest(TestCaseWithAuthentication):
def
test_video_activity
(
self
):
def
test_video_activity
(
self
):
activity_type
=
'played_video'
activity_type
=
'played_video'
response
=
self
.
authenticated_get
(
'/api/v0/courses/{0}/recent_activity?activity_type={1}'
.
format
(
response
=
self
.
authenticated_get
(
'/api/v0/courses/{0}/recent_activity?activity_type={1}'
.
format
(
self
.
course_
key
,
activity_type
))
self
.
course_
id
,
activity_type
))
self
.
assertEquals
(
response
.
status_code
,
200
)
self
.
assertEquals
(
response
.
status_code
,
200
)
self
.
assertEquals
(
response
.
data
,
self
.
get_activity_record
(
activity_type
=
activity_type
,
count
=
400
))
self
.
assertEquals
(
response
.
data
,
self
.
get_activity_record
(
activity_type
=
activity_type
,
count
=
400
))
def
test_unknown_activity
(
self
):
def
test_unknown_activity
(
self
):
activity_type
=
'missing_activity_type'
activity_type
=
'missing_activity_type'
response
=
self
.
authenticated_get
(
'/api/v0/courses/{0}/recent_activity?activity_type={1}'
.
format
(
response
=
self
.
authenticated_get
(
'/api/v0/courses/{0}/recent_activity?activity_type={1}'
.
format
(
self
.
course_
key
,
activity_type
))
self
.
course_
id
,
activity_type
))
self
.
assertEquals
(
response
.
status_code
,
404
)
self
.
assertEquals
(
response
.
status_code
,
404
)
def
test_unknown_course_
key
(
self
):
def
test_unknown_course_
id
(
self
):
response
=
self
.
authenticated_get
(
'/api/v0/courses/{0}/recent_activity'
.
format
(
'foo'
))
response
=
self
.
authenticated_get
(
'/api/v0/courses/{0}/recent_activity'
.
format
(
'foo'
))
self
.
assertEquals
(
response
.
status_code
,
404
)
self
.
assertEquals
(
response
.
status_code
,
404
)
def
test_missing_course_
key
(
self
):
def
test_missing_course_
id
(
self
):
response
=
self
.
authenticated_get
(
'/api/v0/courses/recent_activity'
)
response
=
self
.
authenticated_get
(
'/api/v0/courses/recent_activity'
)
self
.
assertEquals
(
response
.
status_code
,
404
)
self
.
assertEquals
(
response
.
status_code
,
404
)
...
@@ -164,21 +164,21 @@ class CourseEnrollmentViewTestCase(object):
...
@@ -164,21 +164,21 @@ class CourseEnrollmentViewTestCase(object):
model
=
None
model
=
None
path
=
None
path
=
None
def
_get_non_existent_course_
key
(
self
):
def
_get_non_existent_course_
id
(
self
):
course_
key
=
random
.
randint
(
100
,
9999
)
course_
id
=
random
.
randint
(
100
,
9999
)
if
not
Course
.
objects
.
filter
(
course_
key
=
course_key
)
.
exists
():
if
not
Course
.
objects
.
filter
(
course_
id
=
course_id
)
.
exists
():
return
course_
key
return
course_
id
return
self
.
_get_non_existent_course_
key
()
return
self
.
_get_non_existent_course_
id
()
def
test_get_not_found
(
self
):
def
test_get_not_found
(
self
):
'''
'''
Requests made against non-existent courses should return a 404
Requests made against non-existent courses should return a 404
'''
'''
course_
key
=
self
.
_get_non_existent_course_key
()
course_
id
=
self
.
_get_non_existent_course_id
()
self
.
assertFalse
(
self
.
model
.
objects
.
filter
(
course__course_
key
=
course_key
)
.
exists
())
# pylint: disable=no-member
self
.
assertFalse
(
self
.
model
.
objects
.
filter
(
course__course_
id
=
course_id
)
.
exists
())
# pylint: disable=no-member
response
=
self
.
authenticated_get
(
'/api/v0/courses/
%
s
%
s'
%
(
course_
key
,
self
.
path
))
# pylint: disable=no-member
response
=
self
.
authenticated_get
(
'/api/v0/courses/
%
s
%
s'
%
(
course_
id
,
self
.
path
))
# pylint: disable=no-member
self
.
assertEquals
(
response
.
status_code
,
404
)
# pylint: disable=no-member
self
.
assertEquals
(
response
.
status_code
,
404
)
# pylint: disable=no-member
def
test_get
(
self
):
def
test_get
(
self
):
...
@@ -196,7 +196,7 @@ class CourseEnrollmentByBirthYearViewTests(TestCaseWithAuthentication, CourseEnr
...
@@ -196,7 +196,7 @@ class CourseEnrollmentByBirthYearViewTests(TestCaseWithAuthentication, CourseEnr
cls
.
ce2
=
G
(
CourseEnrollmentByBirthYear
,
course
=
cls
.
course
,
birth_year
=
1986
)
cls
.
ce2
=
G
(
CourseEnrollmentByBirthYear
,
course
=
cls
.
course
,
birth_year
=
1986
)
def
test_get
(
self
):
def
test_get
(
self
):
response
=
self
.
authenticated_get
(
'/api/v0/courses/
%
s
%
s'
%
(
self
.
course
.
course_
key
,
self
.
path
,))
response
=
self
.
authenticated_get
(
'/api/v0/courses/
%
s
%
s'
%
(
self
.
course
.
course_
id
,
self
.
path
,))
self
.
assertEquals
(
response
.
status_code
,
200
)
self
.
assertEquals
(
response
.
status_code
,
200
)
expected
=
{
expected
=
{
...
@@ -220,7 +220,7 @@ class CourseEnrollmentByEducationViewTests(TestCaseWithAuthentication, CourseEnr
...
@@ -220,7 +220,7 @@ class CourseEnrollmentByEducationViewTests(TestCaseWithAuthentication, CourseEnr
cls
.
ce2
=
G
(
CourseEnrollmentByEducation
,
course
=
cls
.
course
,
education_level
=
cls
.
el2
)
cls
.
ce2
=
G
(
CourseEnrollmentByEducation
,
course
=
cls
.
course
,
education_level
=
cls
.
el2
)
def
test_get
(
self
):
def
test_get
(
self
):
response
=
self
.
authenticated_get
(
'/api/v0/courses/
%
s
%
s'
%
(
self
.
course
.
course_
key
,
self
.
path
,))
response
=
self
.
authenticated_get
(
'/api/v0/courses/
%
s
%
s'
%
(
self
.
course
.
course_
id
,
self
.
path
,))
self
.
assertEquals
(
response
.
status_code
,
200
)
self
.
assertEquals
(
response
.
status_code
,
200
)
expected
=
{
expected
=
{
...
@@ -242,7 +242,7 @@ class CourseEnrollmentByGenderViewTests(TestCaseWithAuthentication, CourseEnroll
...
@@ -242,7 +242,7 @@ class CourseEnrollmentByGenderViewTests(TestCaseWithAuthentication, CourseEnroll
cls
.
ce2
=
G
(
CourseEnrollmentByGender
,
course
=
cls
.
course
,
gender
=
'f'
)
cls
.
ce2
=
G
(
CourseEnrollmentByGender
,
course
=
cls
.
course
,
gender
=
'f'
)
def
test_get
(
self
):
def
test_get
(
self
):
response
=
self
.
authenticated_get
(
'/api/v0/courses/
%
s
%
s'
%
(
self
.
course
.
course_
key
,
self
.
path
,))
response
=
self
.
authenticated_get
(
'/api/v0/courses/
%
s
%
s'
%
(
self
.
course
.
course_
id
,
self
.
path
,))
self
.
assertEquals
(
response
.
status_code
,
200
)
self
.
assertEquals
(
response
.
status_code
,
200
)
expected
=
{
expected
=
{
...
@@ -255,8 +255,8 @@ class CourseEnrollmentByGenderViewTests(TestCaseWithAuthentication, CourseEnroll
...
@@ -255,8 +255,8 @@ class CourseEnrollmentByGenderViewTests(TestCaseWithAuthentication, CourseEnroll
class
CourseManagerTests
(
TestCase
):
class
CourseManagerTests
(
TestCase
):
def
test_get_by_natural_key
(
self
):
def
test_get_by_natural_key
(
self
):
course_
key
=
'edX/DemoX/Demo_Course'
course_
id
=
'edX/DemoX/Demo_Course'
self
.
assertRaises
(
ObjectDoesNotExist
,
Course
.
objects
.
get_by_natural_key
,
course_
key
)
self
.
assertRaises
(
ObjectDoesNotExist
,
Course
.
objects
.
get_by_natural_key
,
course_
id
)
course
=
G
(
Course
,
course_
key
=
course_key
)
course
=
G
(
Course
,
course_
id
=
course_id
)
self
.
assertEqual
(
course
,
Course
.
objects
.
get_by_natural_key
(
course_
key
))
self
.
assertEqual
(
course
,
Course
.
objects
.
get_by_natural_key
(
course_
id
))
analytics_data_api/v0/urls/courses.py
View file @
53f48f60
...
@@ -19,4 +19,4 @@ urlpatterns = patterns(
...
@@ -19,4 +19,4 @@ urlpatterns = patterns(
)
)
for
path
,
view
,
name
in
COURSE_URLS
:
for
path
,
view
,
name
in
COURSE_URLS
:
urlpatterns
+=
patterns
(
''
,
url
(
r'^(?P<course_
key
>.+)/'
+
re
.
escape
(
path
)
+
r'$'
,
view
.
as_view
(),
name
=
name
))
urlpatterns
+=
patterns
(
''
,
url
(
r'^(?P<course_
id
>.+)/'
+
re
.
escape
(
path
)
+
r'$'
,
view
.
as_view
(),
name
=
name
))
analytics_data_api/v0/views/courses.py
View file @
53f48f60
...
@@ -18,7 +18,7 @@ class CourseActivityMostRecentWeekView(generics.RetrieveAPIView):
...
@@ -18,7 +18,7 @@ class CourseActivityMostRecentWeekView(generics.RetrieveAPIView):
The representation has the following fields:
The representation has the following fields:
<ul>
<ul>
<li>course_
key: The ID of
the course whose activity is described (e.g. edX/DemoX/Demo_Course).</li>
<li>course_
id: The string identifying
the course whose activity is described (e.g. edX/DemoX/Demo_Course).</li>
- interval_start: All data from this timestamp up to the `interval_end` was considered when computing this data
- interval_start: All data from this timestamp up to the `interval_end` was considered when computing this data
point.
point.
- interval_end: All data from `interval_start` up to this timestamp was considered when computing this data point.
- interval_end: All data from `interval_start` up to this timestamp was considered when computing this data point.
...
@@ -41,13 +41,13 @@ class CourseActivityMostRecentWeekView(generics.RetrieveAPIView):
...
@@ -41,13 +41,13 @@ class CourseActivityMostRecentWeekView(generics.RetrieveAPIView):
def
get_object
(
self
,
queryset
=
None
):
def
get_object
(
self
,
queryset
=
None
):
"""Select the activity report for the given course and activity type."""
"""Select the activity report for the given course and activity type."""
course_
key
=
self
.
kwargs
.
get
(
'course_key
'
)
course_
id
=
self
.
kwargs
.
get
(
'course_id
'
)
activity_type
=
self
.
request
.
QUERY_PARAMS
.
get
(
'activity_type'
,
'any'
)
activity_type
=
self
.
request
.
QUERY_PARAMS
.
get
(
'activity_type'
,
'any'
)
activity_type
=
activity_type
.
lower
()
activity_type
=
activity_type
.
lower
()
try
:
try
:
print
CourseActivityByWeek
.
objects
.
all
()
print
CourseActivityByWeek
.
objects
.
all
()
return
CourseActivityByWeek
.
get_most_recent
(
course_
key
,
activity_type
)
return
CourseActivityByWeek
.
get_most_recent
(
course_
id
,
activity_type
)
except
ObjectDoesNotExist
:
except
ObjectDoesNotExist
:
raise
Http404
raise
Http404
...
@@ -65,8 +65,8 @@ class AbstractCourseEnrollmentView(APIView):
...
@@ -65,8 +65,8 @@ class AbstractCourseEnrollmentView(APIView):
if
not
self
.
model
:
if
not
self
.
model
:
raise
NotImplementedError
(
'Subclasses must specify a model!'
)
raise
NotImplementedError
(
'Subclasses must specify a model!'
)
course_
key
=
self
.
kwargs
[
'course_key
'
]
course_
id
=
self
.
kwargs
[
'course_id
'
]
data
=
self
.
model
.
objects
.
filter
(
course__course_
key
=
course_key
)
data
=
self
.
model
.
objects
.
filter
(
course__course_
id
=
course_id
)
if
not
data
:
if
not
data
:
raise
Http404
raise
Http404
...
...
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