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
f868623a
Commit
f868623a
authored
Jul 17, 2014
by
Clinton Blackburn
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added CSV Support
Change-Id: I364eadff8cf5ce4598895923c3770defcef2fc5d
parent
acefe9fc
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
103 additions
and
48 deletions
+103
-48
Makefile
+1
-1
analytics_data_api/v0/models.py
+1
-1
analytics_data_api/v0/tests/test_views.py
+69
-38
analytics_data_api/v0/tests/utils.py
+17
-0
analyticsdataserver/settings/base.py
+5
-1
analyticsdataserver/settings/local.py
+1
-1
analyticsdataserver/tests.py
+8
-6
requirements/base.txt
+1
-0
No files found.
Makefile
View file @
f868623a
...
@@ -53,4 +53,4 @@ loaddata: syncdb
...
@@ -53,4 +53,4 @@ loaddata: syncdb
python manage.py loaddata courses education_levels single_course_activity course_enrollment_birth_year course_enrollment_education course_enrollment_gender problem_response_answer_distribution course_enrollment_daily countries course_enrollment_country
--database
=
analytics
python manage.py loaddata courses education_levels single_course_activity course_enrollment_birth_year course_enrollment_education course_enrollment_gender problem_response_answer_distribution course_enrollment_daily countries course_enrollment_country
--database
=
analytics
demo
:
clean requirements loaddata
demo
:
clean requirements loaddata
python manage.py set_api_key
analytics analytics
python manage.py set_api_key
edx edx
analytics_data_api/v0/models.py
View file @
f868623a
...
@@ -31,7 +31,7 @@ class CourseActivityByWeek(models.Model):
...
@@ -31,7 +31,7 @@ class CourseActivityByWeek(models.Model):
class
BaseCourseEnrollment
(
models
.
Model
):
class
BaseCourseEnrollment
(
models
.
Model
):
course
=
models
.
ForeignKey
(
Course
,
null
=
False
)
course
=
models
.
ForeignKey
(
Course
,
null
=
False
)
date
=
models
.
DateField
(
null
=
False
)
date
=
models
.
DateField
(
null
=
False
,
db_index
=
True
)
count
=
models
.
IntegerField
(
null
=
False
)
count
=
models
.
IntegerField
(
null
=
False
)
class
Meta
(
object
):
class
Meta
(
object
):
...
...
analytics_data_api/v0/tests/test_views.py
View file @
f868623a
# NOTE: Full URLs are used throughout these tests to ensure that the API contract is fulfilled. The URLs should *not*
# NOTE: Full URLs are used throughout these tests to ensure that the API contract is fulfilled. The URLs should *not*
# change for versions greater than 1.0.0. Tests target a specific version of the API, additional tests should be added
# change for versions greater than 1.0.0. Tests target a specific version of the API, additional tests should be added
# for subsequent versions if there are breaking changes introduced in those versions.
# for subsequent versions if there are breaking changes introduced in those versions.
import
StringIO
import
csv
import
datetime
import
datetime
import
random
import
random
...
@@ -9,11 +10,9 @@ from django.conf import settings
...
@@ -9,11 +10,9 @@ from django.conf import settings
from
django_dynamic_fixture
import
G
from
django_dynamic_fixture
import
G
import
pytz
import
pytz
from
analytics_data_api.v0.models
import
CourseEnrollmentByBirthYear
,
CourseEnrollmentByEducation
,
EducationLevel
,
\
from
analytics_data_api.v0
import
models
CourseEnrollmentByGender
,
CourseActivityByWeek
,
Course
,
ProblemResponseAnswerDistribution
,
CourseEnrollmentDaily
,
\
Country
,
\
CourseEnrollmentByCountry
from
analytics_data_api.v0.serializers
import
ProblemResponseAnswerDistributionSerializer
from
analytics_data_api.v0.serializers
import
ProblemResponseAnswerDistributionSerializer
from
analytics_data_api.v0.tests.utils
import
flatten
from
analyticsdataserver.tests
import
TestCaseWithAuthentication
from
analyticsdataserver.tests
import
TestCaseWithAuthentication
...
@@ -21,16 +20,16 @@ class CourseActivityLastWeekTest(TestCaseWithAuthentication):
...
@@ -21,16 +20,16 @@ class CourseActivityLastWeekTest(TestCaseWithAuthentication):
def
setUp
(
self
):
def
setUp
(
self
):
super
(
CourseActivityLastWeekTest
,
self
)
.
setUp
()
super
(
CourseActivityLastWeekTest
,
self
)
.
setUp
()
self
.
course_id
=
'edX/DemoX/Demo_Course'
self
.
course_id
=
'edX/DemoX/Demo_Course'
self
.
course
=
G
(
Course
,
course_id
=
self
.
course_id
)
self
.
course
=
G
(
models
.
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
(
models
.
CourseActivityByWeek
,
course
=
self
.
course
,
interval_start
=
interval_start
,
interval_end
=
interval_end
,
activity_type
=
'posted_forum'
,
count
=
100
)
activity_type
=
'posted_forum'
,
count
=
100
)
G
(
CourseActivityByWeek
,
course
=
self
.
course
,
interval_start
=
interval_start
,
interval_end
=
interval_end
,
G
(
models
.
CourseActivityByWeek
,
course
=
self
.
course
,
interval_start
=
interval_start
,
interval_end
=
interval_end
,
activity_type
=
'attempted_problem'
,
count
=
200
)
activity_type
=
'attempted_problem'
,
count
=
200
)
G
(
CourseActivityByWeek
,
course
=
self
.
course
,
interval_start
=
interval_start
,
interval_end
=
interval_end
,
G
(
models
.
CourseActivityByWeek
,
course
=
self
.
course
,
interval_start
=
interval_start
,
interval_end
=
interval_end
,
activity_type
=
'any'
,
count
=
300
)
activity_type
=
'any'
,
count
=
300
)
G
(
CourseActivityByWeek
,
course
=
self
.
course
,
interval_start
=
interval_start
,
interval_end
=
interval_end
,
G
(
models
.
CourseActivityByWeek
,
course
=
self
.
course
,
interval_start
=
interval_start
,
interval_end
=
interval_end
,
activity_type
=
'played_video'
,
count
=
400
)
activity_type
=
'played_video'
,
count
=
400
)
def
test_activity
(
self
):
def
test_activity
(
self
):
...
@@ -89,7 +88,7 @@ class CourseEnrollmentViewTestCase(object):
...
@@ -89,7 +88,7 @@ class CourseEnrollmentViewTestCase(object):
def
_get_non_existent_course_id
(
self
):
def
_get_non_existent_course_id
(
self
):
course_id
=
random
.
randint
(
100
,
9999
)
course_id
=
random
.
randint
(
100
,
9999
)
if
not
Course
.
objects
.
filter
(
course_id
=
course_id
)
.
exists
():
if
not
models
.
Course
.
objects
.
filter
(
course_id
=
course_id
)
.
exists
():
return
course_id
return
course_id
return
self
.
_get_non_existent_course_id
()
return
self
.
_get_non_existent_course_id
()
...
@@ -105,12 +104,38 @@ class CourseEnrollmentViewTestCase(object):
...
@@ -105,12 +104,38 @@ class CourseEnrollmentViewTestCase(object):
self
.
assertEquals
(
response
.
status_code
,
404
)
self
.
assertEquals
(
response
.
status_code
,
404
)
def
test_get
(
self
):
def
test_get
(
self
):
# Validate the basic response status
response
=
self
.
authenticated_get
(
'/api/v0/courses/
%
s
%
s'
%
(
self
.
course
.
course_id
,
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
)
# Validate the actual data
expected
=
self
.
get_expected_response
(
*
self
.
model
.
objects
.
filter
(
date
=
self
.
date
))
expected
=
self
.
get_expected_response
(
*
self
.
model
.
objects
.
filter
(
date
=
self
.
date
))
self
.
assertEquals
(
response
.
data
,
expected
)
self
.
assertEquals
(
response
.
data
,
expected
)
def
test_get_csv
(
self
):
path
=
'/api/v0/courses/
%
s
%
s'
%
(
self
.
course
.
course_id
,
self
.
path
,)
csv_content_type
=
'text/csv'
response
=
self
.
authenticated_get
(
path
,
HTTP_ACCEPT
=
csv_content_type
)
# Validate the basic response status and content code
self
.
assertEquals
(
response
.
status_code
,
200
)
self
.
assertEquals
(
response
[
'Content-Type'
]
.
split
(
';'
)[
0
],
csv_content_type
)
# Validate the actual data
data
=
self
.
get_expected_response
(
*
self
.
model
.
objects
.
filter
(
date
=
self
.
date
))
data
=
map
(
flatten
,
data
)
# The CSV renderer sorts the headers alphabetically
fieldnames
=
sorted
(
data
[
0
]
.
keys
())
# Generate the expected CSV output
expected
=
StringIO
.
StringIO
()
writer
=
csv
.
DictWriter
(
expected
,
fieldnames
)
writer
.
writeheader
()
writer
.
writerows
(
data
)
self
.
assertEqual
(
response
.
content
,
expected
.
getvalue
())
def
test_get_with_intervals
(
self
):
def
test_get_with_intervals
(
self
):
expected
=
self
.
get_expected_response
(
*
self
.
model
.
objects
.
filter
(
date
=
self
.
date
))
expected
=
self
.
get_expected_response
(
*
self
.
model
.
objects
.
filter
(
date
=
self
.
date
))
self
.
assertIntervalFilteringWorks
(
expected
,
self
.
date
,
self
.
date
+
datetime
.
timedelta
(
days
=
1
))
self
.
assertIntervalFilteringWorks
(
expected
,
self
.
date
,
self
.
date
+
datetime
.
timedelta
(
days
=
1
))
...
@@ -141,11 +166,11 @@ class CourseEnrollmentViewTestCase(object):
...
@@ -141,11 +166,11 @@ class CourseEnrollmentViewTestCase(object):
class
CourseEnrollmentByBirthYearViewTests
(
TestCaseWithAuthentication
,
CourseEnrollmentViewTestCase
):
class
CourseEnrollmentByBirthYearViewTests
(
TestCaseWithAuthentication
,
CourseEnrollmentViewTestCase
):
path
=
'/enrollment/birth_year'
path
=
'/enrollment/birth_year'
model
=
CourseEnrollmentByBirthYear
model
=
models
.
CourseEnrollmentByBirthYear
@classmethod
@classmethod
def
setUpClass
(
cls
):
def
setUpClass
(
cls
):
cls
.
course
=
G
(
Course
)
cls
.
course
=
G
(
models
.
Course
)
cls
.
date
=
datetime
.
date
(
2014
,
1
,
1
)
cls
.
date
=
datetime
.
date
(
2014
,
1
,
1
)
G
(
cls
.
model
,
course
=
cls
.
course
,
date
=
cls
.
date
,
birth_year
=
1956
)
G
(
cls
.
model
,
course
=
cls
.
course
,
date
=
cls
.
date
,
birth_year
=
1956
)
G
(
cls
.
model
,
course
=
cls
.
course
,
date
=
cls
.
date
,
birth_year
=
1986
)
G
(
cls
.
model
,
course
=
cls
.
course
,
date
=
cls
.
date
,
birth_year
=
1986
)
...
@@ -153,8 +178,9 @@ class CourseEnrollmentByBirthYearViewTests(TestCaseWithAuthentication, CourseEnr
...
@@ -153,8 +178,9 @@ class CourseEnrollmentByBirthYearViewTests(TestCaseWithAuthentication, CourseEnr
G
(
cls
.
model
,
course
=
cls
.
course
,
date
=
cls
.
date
-
datetime
.
timedelta
(
days
=
10
),
birth_year
=
1986
)
G
(
cls
.
model
,
course
=
cls
.
course
,
date
=
cls
.
date
-
datetime
.
timedelta
(
days
=
10
),
birth_year
=
1986
)
def
get_expected_response
(
self
,
*
args
):
def
get_expected_response
(
self
,
*
args
):
return
[{
'course_id'
:
ce
.
course
.
course_id
,
'count'
:
ce
.
count
,
'date'
:
ce
.
date
.
strftime
(
settings
.
DATE_FORMAT
),
return
[
'birth_year'
:
ce
.
birth_year
}
for
ce
in
args
]
{
'course_id'
:
str
(
ce
.
course
.
course_id
),
'count'
:
ce
.
count
,
'date'
:
ce
.
date
.
strftime
(
settings
.
DATE_FORMAT
),
'birth_year'
:
ce
.
birth_year
}
for
ce
in
args
]
def
test_get
(
self
):
def
test_get
(
self
):
response
=
self
.
authenticated_get
(
'/api/v0/courses/
%
s
%
s'
%
(
self
.
course
.
course_id
,
self
.
path
,))
response
=
self
.
authenticated_get
(
'/api/v0/courses/
%
s
%
s'
%
(
self
.
course
.
course_id
,
self
.
path
,))
...
@@ -170,13 +196,13 @@ class CourseEnrollmentByBirthYearViewTests(TestCaseWithAuthentication, CourseEnr
...
@@ -170,13 +196,13 @@ class CourseEnrollmentByBirthYearViewTests(TestCaseWithAuthentication, CourseEnr
class
CourseEnrollmentByEducationViewTests
(
TestCaseWithAuthentication
,
CourseEnrollmentViewTestCase
):
class
CourseEnrollmentByEducationViewTests
(
TestCaseWithAuthentication
,
CourseEnrollmentViewTestCase
):
path
=
'/enrollment/education/'
path
=
'/enrollment/education/'
model
=
CourseEnrollmentByEducation
model
=
models
.
CourseEnrollmentByEducation
@classmethod
@classmethod
def
setUpClass
(
cls
):
def
setUpClass
(
cls
):
cls
.
el1
=
G
(
EducationLevel
,
name
=
'Doctorate'
,
short_name
=
'doctorate'
)
cls
.
el1
=
G
(
models
.
EducationLevel
,
name
=
'Doctorate'
,
short_name
=
'doctorate'
)
cls
.
el2
=
G
(
EducationLevel
,
name
=
'Top Secret'
,
short_name
=
'top_secret'
)
cls
.
el2
=
G
(
models
.
EducationLevel
,
name
=
'Top Secret'
,
short_name
=
'top_secret'
)
cls
.
course
=
G
(
Course
)
cls
.
course
=
G
(
models
.
Course
)
cls
.
date
=
datetime
.
date
(
2014
,
1
,
1
)
cls
.
date
=
datetime
.
date
(
2014
,
1
,
1
)
G
(
cls
.
model
,
course
=
cls
.
course
,
date
=
cls
.
date
,
education_level
=
cls
.
el1
)
G
(
cls
.
model
,
course
=
cls
.
course
,
date
=
cls
.
date
,
education_level
=
cls
.
el1
)
G
(
cls
.
model
,
course
=
cls
.
course
,
date
=
cls
.
date
,
education_level
=
cls
.
el2
)
G
(
cls
.
model
,
course
=
cls
.
course
,
date
=
cls
.
date
,
education_level
=
cls
.
el2
)
...
@@ -184,26 +210,28 @@ class CourseEnrollmentByEducationViewTests(TestCaseWithAuthentication, CourseEnr
...
@@ -184,26 +210,28 @@ class CourseEnrollmentByEducationViewTests(TestCaseWithAuthentication, CourseEnr
education_level
=
cls
.
el2
)
education_level
=
cls
.
el2
)
def
get_expected_response
(
self
,
*
args
):
def
get_expected_response
(
self
,
*
args
):
return
[{
'course_id'
:
ce
.
course
.
course_id
,
'count'
:
ce
.
count
,
'date'
:
ce
.
date
.
strftime
(
settings
.
DATE_FORMAT
),
return
[
'education_level'
:
{
'name'
:
ce
.
education_level
.
name
,
'short_name'
:
ce
.
education_level
.
short_name
}}
for
{
'course_id'
:
str
(
ce
.
course
.
course_id
),
'count'
:
ce
.
count
,
'date'
:
ce
.
date
.
strftime
(
settings
.
DATE_FORMAT
),
ce
in
args
]
'education_level'
:
{
'name'
:
ce
.
education_level
.
name
,
'short_name'
:
ce
.
education_level
.
short_name
}}
for
ce
in
args
]
class
CourseEnrollmentByGenderViewTests
(
TestCaseWithAuthentication
,
CourseEnrollmentViewTestCase
):
class
CourseEnrollmentByGenderViewTests
(
TestCaseWithAuthentication
,
CourseEnrollmentViewTestCase
):
path
=
'/enrollment/gender/'
path
=
'/enrollment/gender/'
model
=
CourseEnrollmentByGender
model
=
models
.
CourseEnrollmentByGender
@classmethod
@classmethod
def
setUpClass
(
cls
):
def
setUpClass
(
cls
):
cls
.
course
=
G
(
Course
)
cls
.
course
=
G
(
models
.
Course
)
cls
.
date
=
datetime
.
date
(
2014
,
1
,
1
)
cls
.
date
=
datetime
.
date
(
2014
,
1
,
1
)
G
(
cls
.
model
,
course
=
cls
.
course
,
gender
=
'm'
,
date
=
cls
.
date
,
count
=
34
)
G
(
cls
.
model
,
course
=
cls
.
course
,
gender
=
'm'
,
date
=
cls
.
date
,
count
=
34
)
G
(
cls
.
model
,
course
=
cls
.
course
,
gender
=
'f'
,
date
=
cls
.
date
,
count
=
45
)
G
(
cls
.
model
,
course
=
cls
.
course
,
gender
=
'f'
,
date
=
cls
.
date
,
count
=
45
)
G
(
cls
.
model
,
course
=
cls
.
course
,
gender
=
'f'
,
date
=
cls
.
date
-
datetime
.
timedelta
(
days
=
2
),
count
=
45
)
G
(
cls
.
model
,
course
=
cls
.
course
,
gender
=
'f'
,
date
=
cls
.
date
-
datetime
.
timedelta
(
days
=
2
),
count
=
45
)
def
get_expected_response
(
self
,
*
args
):
def
get_expected_response
(
self
,
*
args
):
return
[{
'course_id'
:
ce
.
course
.
course_id
,
'count'
:
ce
.
count
,
'date'
:
ce
.
date
.
strftime
(
settings
.
DATE_FORMAT
),
return
[
'gender'
:
ce
.
gender
}
for
ce
in
args
]
{
'course_id'
:
str
(
ce
.
course
.
course_id
),
'count'
:
ce
.
count
,
'date'
:
ce
.
date
.
strftime
(
settings
.
DATE_FORMAT
),
'gender'
:
ce
.
gender
}
for
ce
in
args
]
# pylint: disable=no-member,no-value-for-parameter
# pylint: disable=no-member,no-value-for-parameter
...
@@ -217,7 +245,7 @@ class AnswerDistributionTests(TestCaseWithAuthentication):
...
@@ -217,7 +245,7 @@ class AnswerDistributionTests(TestCaseWithAuthentication):
cls
.
module_id
=
"i4x://org/num/run/problem/RANDOMNUMBER"
cls
.
module_id
=
"i4x://org/num/run/problem/RANDOMNUMBER"
cls
.
part_id1
=
"i4x-org-num-run-problem-RANDOMNUMBER_2_1"
cls
.
part_id1
=
"i4x-org-num-run-problem-RANDOMNUMBER_2_1"
cls
.
ad1
=
G
(
cls
.
ad1
=
G
(
ProblemResponseAnswerDistribution
,
models
.
ProblemResponseAnswerDistribution
,
course_id
=
cls
.
course_id
,
course_id
=
cls
.
course_id
,
module_id
=
cls
.
module_id
,
module_id
=
cls
.
module_id
,
part_id
=
cls
.
part_id1
part_id
=
cls
.
part_id1
...
@@ -238,33 +266,36 @@ class AnswerDistributionTests(TestCaseWithAuthentication):
...
@@ -238,33 +266,36 @@ class AnswerDistributionTests(TestCaseWithAuthentication):
class
CourseEnrollmentViewTests
(
TestCaseWithAuthentication
,
CourseEnrollmentViewTestCase
):
class
CourseEnrollmentViewTests
(
TestCaseWithAuthentication
,
CourseEnrollmentViewTestCase
):
model
=
CourseEnrollmentDaily
model
=
models
.
CourseEnrollmentDaily
path
=
'/enrollment'
path
=
'/enrollment'
@classmethod
@classmethod
def
setUpClass
(
cls
):
def
setUpClass
(
cls
):
cls
.
course
=
G
(
Course
)
cls
.
course
=
G
(
models
.
Course
)
cls
.
date
=
datetime
.
date
(
2014
,
1
,
1
)
cls
.
date
=
datetime
.
date
(
2014
,
1
,
1
)
G
(
cls
.
model
,
course
=
cls
.
course
,
date
=
cls
.
date
,
count
=
203
)
G
(
cls
.
model
,
course
=
cls
.
course
,
date
=
cls
.
date
,
count
=
203
)
G
(
cls
.
model
,
course
=
cls
.
course
,
date
=
cls
.
date
-
datetime
.
timedelta
(
days
=
5
),
count
=
203
)
G
(
cls
.
model
,
course
=
cls
.
course
,
date
=
cls
.
date
-
datetime
.
timedelta
(
days
=
5
),
count
=
203
)
def
get_expected_response
(
self
,
*
args
):
def
get_expected_response
(
self
,
*
args
):
return
[{
'course_id'
:
ce
.
course
.
course_id
,
'count'
:
ce
.
count
,
'date'
:
ce
.
date
.
strftime
(
settings
.
DATE_FORMAT
)}
return
[
for
ce
in
args
]
{
'course_id'
:
str
(
ce
.
course
.
course_id
),
'count'
:
ce
.
count
,
'date'
:
ce
.
date
.
strftime
(
settings
.
DATE_FORMAT
)}
for
ce
in
args
]
class
CourseEnrollmentByLocationViewTests
(
TestCaseWithAuthentication
,
CourseEnrollmentViewTestCase
):
class
CourseEnrollmentByLocationViewTests
(
TestCaseWithAuthentication
,
CourseEnrollmentViewTestCase
):
path
=
'/enrollment/location/'
path
=
'/enrollment/location/'
model
=
CourseEnrollmentByCountry
model
=
models
.
CourseEnrollmentByCountry
def
get_expected_response
(
self
,
*
args
):
def
get_expected_response
(
self
,
*
args
):
return
[{
'course_id'
:
ce
.
course
.
course_id
,
'count'
:
ce
.
count
,
'date'
:
ce
.
date
.
strftime
(
settings
.
DATE_FORMAT
),
return
[
'country'
:
{
'code'
:
ce
.
country
.
code
,
'name'
:
ce
.
country
.
name
}}
for
ce
in
args
]
{
'course_id'
:
str
(
ce
.
course
.
course_id
),
'count'
:
ce
.
count
,
'date'
:
ce
.
date
.
strftime
(
settings
.
DATE_FORMAT
),
'country'
:
{
'code'
:
ce
.
country
.
code
,
'name'
:
ce
.
country
.
name
}}
for
ce
in
args
]
@classmethod
@classmethod
def
setUpClass
(
cls
):
def
setUpClass
(
cls
):
cls
.
course
=
G
(
Course
)
cls
.
course
=
G
(
models
.
Course
)
cls
.
date
=
datetime
.
date
(
2014
,
1
,
1
)
cls
.
date
=
datetime
.
date
(
2014
,
1
,
1
)
G
(
cls
.
model
,
course
=
cls
.
course
,
country
=
G
(
Country
),
count
=
455
,
date
=
cls
.
date
)
G
(
cls
.
model
,
course
=
cls
.
course
,
country
=
G
(
models
.
Country
),
count
=
455
,
date
=
cls
.
date
)
G
(
cls
.
model
,
course
=
cls
.
course
,
country
=
G
(
Country
),
count
=
356
,
date
=
cls
.
date
)
G
(
cls
.
model
,
course
=
cls
.
course
,
country
=
G
(
models
.
Country
),
count
=
356
,
date
=
cls
.
date
)
G
(
cls
.
model
,
course
=
cls
.
course
,
country
=
G
(
Country
),
count
=
12
,
date
=
cls
.
date
-
datetime
.
timedelta
(
days
=
29
))
G
(
cls
.
model
,
course
=
cls
.
course
,
country
=
G
(
models
.
Country
),
count
=
12
,
date
=
cls
.
date
-
datetime
.
timedelta
(
days
=
29
))
analytics_data_api/v0/tests/utils.py
0 → 100644
View file @
f868623a
import
collections
def
flatten
(
dictionary
,
parent_key
=
''
,
sep
=
'.'
):
"""
Flatten dictionary
http://stackoverflow.com/a/6027615
"""
items
=
[]
for
key
,
value
in
dictionary
.
items
():
new_key
=
parent_key
+
sep
+
key
if
parent_key
else
key
if
isinstance
(
value
,
collections
.
MutableMapping
):
items
.
extend
(
flatten
(
value
,
new_key
)
.
items
())
else
:
items
.
append
((
new_key
,
value
))
return
dict
(
items
)
analyticsdataserver/settings/base.py
View file @
f868623a
"""Common settings and globals."""
"""Common settings and globals."""
from
os.path
import
abspath
,
basename
,
dirname
,
join
,
normpath
from
os.path
import
abspath
,
basename
,
dirname
,
join
,
normpath
from
sys
import
stderr
from
sys
import
stderr
...
@@ -250,6 +249,11 @@ REST_FRAMEWORK = {
...
@@ -250,6 +249,11 @@ REST_FRAMEWORK = {
# For the browseable API
# For the browseable API
'rest_framework.authentication.SessionAuthentication'
,
'rest_framework.authentication.SessionAuthentication'
,
),
),
'DEFAULT_RENDERER_CLASSES'
:
(
'rest_framework.renderers.JSONRenderer'
,
'rest_framework.renderers.BrowsableAPIRenderer'
,
'rest_framework_csv.renderers.CSVRenderer'
,
)
}
}
########## END REST FRAMEWORK CONFIGURATION
########## END REST FRAMEWORK CONFIGURATION
...
...
analyticsdataserver/settings/local.py
View file @
f868623a
...
@@ -85,5 +85,5 @@ ENABLE_ADMIN_SITE = True
...
@@ -85,5 +85,5 @@ ENABLE_ADMIN_SITE = True
TEST_RUNNER
=
'django_nose.NoseTestSuiteRunner'
TEST_RUNNER
=
'django_nose.NoseTestSuiteRunner'
SWAGGER_SETTINGS
=
{
SWAGGER_SETTINGS
=
{
'api_key'
:
'
analytics
'
'api_key'
:
'
edx
'
}
}
analyticsdataserver/tests.py
View file @
f868623a
from
contextlib
import
contextmanager
from
contextlib
import
contextmanager
from
functools
import
partial
from
django.conf
import
settings
from
django.conf
import
settings
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
django.db.utils
import
ConnectionHandler
,
DatabaseError
from
django.db.utils
import
ConnectionHandler
,
DatabaseError
from
django.test
import
TestCase
from
django.test
import
TestCase
from
django.test.utils
import
override_settings
from
django.test.utils
import
override_settings
from
mock
import
patch
,
Mock
import
mock
import
mock
from
rest_framework.authtoken.models
import
Token
from
rest_framework.authtoken.models
import
Token
...
@@ -15,13 +14,16 @@ class TestCaseWithAuthentication(TestCase):
...
@@ -15,13 +14,16 @@ class TestCaseWithAuthentication(TestCase):
def
setUp
(
self
):
def
setUp
(
self
):
super
(
TestCaseWithAuthentication
,
self
)
.
setUp
()
super
(
TestCaseWithAuthentication
,
self
)
.
setUp
()
test_user
=
User
.
objects
.
create_user
(
'tester'
,
'test@example.com'
,
'testpassword'
)
test_user
=
User
.
objects
.
create_user
(
'tester'
,
'test@example.com'
,
'testpassword'
)
token
=
Token
.
objects
.
create
(
user
=
test_user
)
self
.
token
=
Token
.
objects
.
create
(
user
=
test_user
)
self
.
authenticated_get
=
partial
(
self
.
client
.
get
,
HTTP_AUTHORIZATION
=
'Token '
+
token
.
key
,
follow
=
True
)
def
authenticated_get
(
self
,
path
,
data
=
None
,
follow
=
True
,
**
extra
):
data
=
data
or
{}
return
self
.
client
.
get
(
path
,
data
,
follow
,
HTTP_AUTHORIZATION
=
'Token '
+
self
.
token
.
key
,
**
extra
)
@contextmanager
@contextmanager
def
no_database
():
def
no_database
():
cursor_mock
=
Mock
(
side_effect
=
DatabaseError
)
cursor_mock
=
mock
.
Mock
(
side_effect
=
DatabaseError
)
with
mock
.
patch
(
'django.db.backends.util.CursorWrapper'
,
cursor_mock
):
with
mock
.
patch
(
'django.db.backends.util.CursorWrapper'
,
cursor_mock
):
yield
yield
...
@@ -58,7 +60,7 @@ class OperationalEndpointsTest(TestCaseWithAuthentication):
...
@@ -58,7 +60,7 @@ class OperationalEndpointsTest(TestCaseWithAuthentication):
@staticmethod
@staticmethod
@contextmanager
@contextmanager
def
override_database_connections
(
databases
):
def
override_database_connections
(
databases
):
with
patch
(
'analyticsdataserver.views.connections'
,
ConnectionHandler
(
databases
)):
with
mock
.
patch
(
'analyticsdataserver.views.connections'
,
ConnectionHandler
(
databases
)):
yield
yield
@override_settings
(
ANALYTICS_DATABASE
=
'reporting'
)
@override_settings
(
ANALYTICS_DATABASE
=
'reporting'
)
...
...
requirements/base.txt
View file @
f868623a
...
@@ -5,3 +5,4 @@ django-model-utils==1.4.0
...
@@ -5,3 +5,4 @@ django-model-utils==1.4.0
djangorestframework==2.3.5
djangorestframework==2.3.5
ipython==2.1.0
ipython==2.1.0
django-rest-swagger==0.1.14
django-rest-swagger==0.1.14
djangorestframework-csv==1.3.3
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