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
f86ecabb
Commit
f86ecabb
authored
Sep 16, 2014
by
Clinton Blackburn
Committed by
Clinton Blackburn
Sep 16, 2014
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #26 from edx/parts-unknown
Grouping and Returning Unknown Locations
parents
a817d2c8
47c74d7f
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
54 additions
and
8 deletions
+54
-8
analytics_data_api/management/commands/generate_fake_course_data.py
+3
-2
analytics_data_api/v0/models.py
+5
-1
analytics_data_api/v0/tests/test_views.py
+15
-1
analytics_data_api/v0/views/courses.py
+31
-4
No files found.
analytics_data_api/management/commands/generate_fake_course_data.py
View file @
f86ecabb
...
@@ -48,12 +48,13 @@ class Command(BaseCommand):
...
@@ -48,12 +48,13 @@ class Command(BaseCommand):
'doctorate'
:
0.0470
'doctorate'
:
0.0470
}
}
country_ratios
=
{
country_ratios
=
{
'US'
:
0.3
4
,
'US'
:
0.3
3
,
'GH'
:
0.12
,
'GH'
:
0.12
,
'IN'
:
0.10
,
'IN'
:
0.10
,
'CA'
:
0.14
,
'CA'
:
0.14
,
'CN'
:
0.22
,
'CN'
:
0.22
,
'DE'
:
0.08
'DE'
:
0.08
,
'UNKNOWN'
:
0.01
}
}
# Generate birth year ratios
# Generate birth year ratios
...
...
analytics_data_api/v0/models.py
View file @
f86ecabb
from
django.db
import
models
from
django.db
import
models
from
iso3166
import
countries
from
iso3166
import
countries
,
Country
class
CourseActivityWeekly
(
models
.
Model
):
class
CourseActivityWeekly
(
models
.
Model
):
...
@@ -99,6 +99,7 @@ class ProblemResponseAnswerDistribution(models.Model):
...
@@ -99,6 +99,7 @@ class ProblemResponseAnswerDistribution(models.Model):
class
CourseEnrollmentByCountry
(
BaseCourseEnrollment
):
class
CourseEnrollmentByCountry
(
BaseCourseEnrollment
):
UNKNOWN_COUNTRY_CODE
=
'UNKNOWN'
country_code
=
models
.
CharField
(
max_length
=
255
,
null
=
False
,
db_column
=
'country_code'
)
country_code
=
models
.
CharField
(
max_length
=
255
,
null
=
False
,
db_column
=
'country_code'
)
@property
@property
...
@@ -107,6 +108,9 @@ class CourseEnrollmentByCountry(BaseCourseEnrollment):
...
@@ -107,6 +108,9 @@ class CourseEnrollmentByCountry(BaseCourseEnrollment):
Returns a Country object representing the country in this model's country_code.
Returns a Country object representing the country in this model's country_code.
"""
"""
try
:
try
:
if
self
.
country_code
==
self
.
UNKNOWN_COUNTRY_CODE
:
return
Country
(
self
.
UNKNOWN_COUNTRY_CODE
,
None
,
None
,
None
)
return
countries
.
get
(
self
.
country_code
)
return
countries
.
get
(
self
.
country_code
)
except
(
KeyError
,
ValueError
):
except
(
KeyError
,
ValueError
):
# Country code is not valid ISO-3166
# Country code is not valid ISO-3166
...
...
analytics_data_api/v0/tests/test_views.py
View file @
f86ecabb
...
@@ -323,13 +323,27 @@ class CourseEnrollmentByLocationViewTests(CourseEnrollmentViewTestCaseMixin, Tes
...
@@ -323,13 +323,27 @@ class CourseEnrollmentByLocationViewTests(CourseEnrollmentViewTestCaseMixin, Tes
model
=
models
.
CourseEnrollmentByCountry
model
=
models
.
CourseEnrollmentByCountry
def
format_as_response
(
self
,
*
args
):
def
format_as_response
(
self
,
*
args
):
unknown
=
{
'course_id'
:
None
,
'count'
:
0
,
'date'
:
None
,
'country'
:
{
'alpha2'
:
None
,
'alpha3'
:
None
,
'name'
:
u'UNKNOWN'
}}
for
arg
in
args
:
if
not
arg
.
country
:
unknown
[
'course_id'
]
=
arg
.
course_id
unknown
[
'date'
]
=
arg
.
date
.
strftime
(
settings
.
DATE_FORMAT
)
unknown
[
'count'
]
+=
arg
.
count
args
=
[
arg
for
arg
in
args
if
arg
.
country_code
not
in
[
''
,
'A1'
,
'A2'
,
'AP'
,
'EU'
,
'O1'
,
'UNKNOWN'
]]
args
=
[
arg
for
arg
in
args
if
arg
.
country_code
not
in
[
''
,
'A1'
,
'A2'
,
'AP'
,
'EU'
,
'O1'
,
'UNKNOWN'
]]
args
=
sorted
(
args
,
key
=
lambda
item
:
(
item
.
date
,
item
.
course_id
,
item
.
country
.
alpha3
))
args
=
sorted
(
args
,
key
=
lambda
item
:
(
item
.
date
,
item
.
course_id
,
item
.
country
.
alpha3
))
re
turn
[
re
sponse
=
[
{
'course_id'
:
str
(
ce
.
course_id
),
'count'
:
ce
.
count
,
'date'
:
ce
.
date
.
strftime
(
settings
.
DATE_FORMAT
),
{
'course_id'
:
str
(
ce
.
course_id
),
'count'
:
ce
.
count
,
'date'
:
ce
.
date
.
strftime
(
settings
.
DATE_FORMAT
),
'country'
:
{
'alpha2'
:
ce
.
country
.
alpha2
,
'alpha3'
:
ce
.
country
.
alpha3
,
'name'
:
ce
.
country
.
name
}}
for
ce
in
'country'
:
{
'alpha2'
:
ce
.
country
.
alpha2
,
'alpha3'
:
ce
.
country
.
alpha3
,
'name'
:
ce
.
country
.
name
}}
for
ce
in
args
]
args
]
# Unknown comes last
response
.
append
(
unknown
)
return
response
def
setUp
(
self
):
def
setUp
(
self
):
super
(
CourseEnrollmentByLocationViewTests
,
self
)
.
setUp
()
super
(
CourseEnrollmentByLocationViewTests
,
self
)
.
setUp
()
self
.
country
=
countries
.
get
(
'US'
)
self
.
country
=
countries
.
get
(
'US'
)
...
...
analytics_data_api/v0/views/courses.py
View file @
f86ecabb
...
@@ -313,7 +313,8 @@ class CourseEnrollmentByLocationView(BaseCourseEnrollmentView):
...
@@ -313,7 +313,8 @@ class CourseEnrollmentByLocationView(BaseCourseEnrollmentView):
Course enrollment broken down by user location
Course enrollment broken down by user location
Returns the enrollment of a course with users binned by their location. Location is calculated based on the user's
Returns the enrollment of a course with users binned by their location. Location is calculated based on the user's
IP address.
IP address. Enrollment counts for users whose location cannot be determined will be included in an entry with
country.name set to UNKNOWN.
Countries are denoted by their <a href="http://www.iso.org/iso/country_codes/country_codes" target="_blank">ISO 3166 country code</a>.
Countries are denoted by their <a href="http://www.iso.org/iso/country_codes/country_codes" target="_blank">ISO 3166 country code</a>.
...
@@ -333,10 +334,36 @@ class CourseEnrollmentByLocationView(BaseCourseEnrollmentView):
...
@@ -333,10 +334,36 @@ class CourseEnrollmentByLocationView(BaseCourseEnrollmentView):
def
get_queryset
(
self
):
def
get_queryset
(
self
):
queryset
=
super
(
CourseEnrollmentByLocationView
,
self
)
.
get_queryset
()
queryset
=
super
(
CourseEnrollmentByLocationView
,
self
)
.
get_queryset
()
# Remove all items where country is None
# Get all of the data from the database
items
=
[
item
for
item
in
queryset
.
all
()
if
item
.
country
is
not
None
]
items
=
queryset
.
all
()
# Split into known and unknown
knowns
=
[]
unknowns
=
[]
for
item
in
items
:
if
item
.
country
:
knowns
.
append
(
item
)
else
:
unknowns
.
append
(
item
)
# Group the unknowns by date and combine the counts
for
key
,
group
in
groupby
(
unknowns
,
lambda
x
:
(
x
.
date
,
x
.
course_id
)):
date
=
key
[
0
]
course_id
=
key
[
1
]
count
=
0
for
item
in
group
:
count
+=
item
.
count
# pylint: disable=unexpected-keyword-arg,no-value-for-parameter
knowns
.
append
(
models
.
CourseEnrollmentByCountry
(
course_id
=
course_id
,
date
=
date
,
country_code
=
models
.
CourseEnrollmentByCountry
.
UNKNOWN_COUNTRY_CODE
,
count
=
count
))
# Note: We are returning a list, instead of a queryset. This is
# Note: We are returning a list, instead of a queryset. This is
# acceptable since the consuming code simply expects the returned
# acceptable since the consuming code simply expects the returned
# value to be iterable, not necessarily a queryset.
# value to be iterable, not necessarily a queryset.
return
item
s
return
known
s
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