Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-analytics-dashboard
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-dashboard
Commits
33430b73
Commit
33430b73
authored
Jun 27, 2017
by
Kyle McCormick
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Revert "Improve performance of CourseSummariesPresenter"
This reverts commit
da828152
.
parent
43c0d12d
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
83 additions
and
97 deletions
+83
-97
analytics_dashboard/courses/presenters/course_summaries.py
+33
-18
analytics_dashboard/courses/tests/test_presenters/test_course_summaries.py
+50
-79
No files found.
analytics_dashboard/courses/presenters/course_summaries.py
View file @
33430b73
from
django.conf
import
settings
from
django.core.cache
import
cache
from
waffle
import
switch_is_active
from
courses.presenters
import
BasePresenter
...
...
@@ -6,9 +8,34 @@ from courses.presenters import BasePresenter
class
CourseSummariesPresenter
(
BasePresenter
):
""" Presenter for the course enrollment data. """
CACHE_KEY
=
'summaries'
NON_NULL_STRING_FIELDS
=
[
'course_id'
,
'catalog_course'
,
'catalog_course_title'
,
'start_date'
,
'end_date'
,
'pacing_type'
,
'availability'
]
@staticmethod
def
filter_summaries
(
all_summaries
,
course_ids
=
None
):
"""Filter results to just the course IDs specified."""
if
course_ids
is
None
:
return
all_summaries
return
[
summary
for
summary
in
all_summaries
if
summary
[
'course_id'
]
in
course_ids
]
def
_get_all_summaries
(
self
):
"""
Returns all course summaries. If not cached, summaries will be fetched
from the analytics data API.
"""
all_summaries
=
cache
.
get
(
self
.
CACHE_KEY
)
if
all_summaries
is
None
:
exclude
=
[
'programs'
]
# we make a separate call to the programs endpoint
if
not
switch_is_active
(
'enable_course_passing'
):
exclude
.
append
(
'passing_users'
)
all_summaries
=
self
.
client
.
course_summaries
()
.
course_summaries
(
exclude
=
exclude
)
all_summaries
=
[
{
field
:
(
''
if
val
is
None
and
field
in
self
.
NON_NULL_STRING_FIELDS
else
val
)
for
field
,
val
in
summary
.
items
()}
for
summary
in
all_summaries
]
cache
.
set
(
self
.
CACHE_KEY
,
all_summaries
,
settings
.
COURSE_SUMMARIES_CACHE_TIMEOUT
)
return
all_summaries
def
_get_last_updated
(
self
,
summaries
):
# all the create times should be the same, so just use the first one
if
summaries
:
...
...
@@ -21,27 +48,15 @@ class CourseSummariesPresenter(BasePresenter):
Returns course summaries that match those listed in course_ids. If
no course IDs provided, all data will be returned.
"""
exclude
=
[
'programs'
]
# we make a separate call to the programs endpoint
if
not
switch_is_active
(
'enable_course_passing'
):
exclude
.
append
(
'passing_users'
)
summaries
=
self
.
client
.
course_summaries
()
.
course_summaries
(
course_ids
=
course_ids
,
exclude
=
exclude
)
summaries
=
[
{
field
:
(
''
if
val
is
None
and
field
in
self
.
NON_NULL_STRING_FIELDS
else
val
)
for
field
,
val
in
summary
.
items
()
}
for
summary
in
summaries
]
all_summaries
=
self
.
_get_all_summaries
()
filtered_summaries
=
self
.
filter_summaries
(
all_summaries
,
course_ids
)
# sort by title by default with "None" values at the end
summaries
=
sorted
(
summaries
,
key
=
lambda
x
:
(
not
x
[
'catalog_course_title'
],
x
[
'catalog_course_title'
])
)
filtered_summaries
=
sorted
(
filtered_summaries
,
key
=
lambda
x
:
(
not
x
[
'catalog_course_title'
],
x
[
'catalog_course_title'
]))
return
summaries
,
self
.
_get_last_updated
(
summaries
)
return
filtered_summaries
,
self
.
_get_last_updated
(
filtered_
summaries
)
def
get_course_summary_metrics
(
self
,
summaries
):
summary
=
{
...
...
analytics_dashboard/courses/tests/test_presenters/test_course_summaries.py
View file @
33430b73
from
ddt
import
(
data
,
ddt
,
unpack
ddt
)
import
mock
from
django.test
import
TestCase
from
django.test
import
(
override_settings
,
TestCase
)
from
courses.presenters.course_summaries
import
CourseSummariesPresenter
from
courses.tests
import
utils
from
courses.tests.utils
import
CourseSamples
_ANOTHER_DEPRECATED_COURSE_ID
=
'another/course/id'
@ddt
class
CourseSummariesPresenterTests
(
TestCase
):
_API_SUMMARIES
=
{
CourseSamples
.
DEPRECATED_DEMO_COURSE_ID
:
{
@property
def
mock_api_response
(
self
):
'''
Returns a mocked API response for two courses including some null fields.
'''
return
[{
'course_id'
:
CourseSamples
.
DEPRECATED_DEMO_COURSE_ID
,
'catalog_course_title'
:
'Deprecated demo course'
,
'catalog_course'
:
'edX+demo.1x'
,
...
...
@@ -64,8 +67,7 @@ class CourseSummariesPresenterTests(TestCase):
}
},
'created'
:
utils
.
CREATED_DATETIME_STRING
,
},
CourseSamples
.
DEMO_COURSE_ID
:
{
},
{
'course_id'
:
CourseSamples
.
DEMO_COURSE_ID
,
'catalog_course_title'
:
'Demo Course'
,
'catalog_course'
:
None
,
...
...
@@ -110,16 +112,15 @@ class CourseSummariesPresenterTests(TestCase):
}
},
'created'
:
utils
.
CREATED_DATETIME_STRING
,
},
_ANOTHER_DEPRECATED_COURSE_ID
:
{
'course_id'
:
_ANOTHER_DEPRECATED_COURSE_ID
,
},
{
'course_id'
:
'another/course/id'
,
'catalog_course_title'
:
None
,
'catalog_course'
:
None
,
'start_date'
:
None
,
'end_date'
:
None
,
'pacing_type'
:
'instructor_paced'
,
'pacing_type'
:
None
,
'availability'
:
None
,
'count'
:
None
,
'count'
:
1
,
'cumulative_count'
:
1
,
'count_change_7_days'
:
0
,
'enrollment_modes'
:
{
...
...
@@ -155,75 +156,45 @@ class CourseSummariesPresenterTests(TestCase):
}
},
'created'
:
utils
.
CREATED_DATETIME_STRING
,
},
}
}]
_PRESENTER_SUMMARIES
=
{
CourseSamples
.
DEPRECATED_DEMO_COURSE_ID
:
_API_SUMMARIES
[
CourseSamples
.
DEPRECATED_DEMO_COURSE_ID
],
CourseSamples
.
DEMO_COURSE_ID
:
_API_SUMMARIES
[
CourseSamples
.
DEMO_COURSE_ID
],
_ANOTHER_DEPRECATED_COURSE_ID
:
_API_SUMMARIES
[
_ANOTHER_DEPRECATED_COURSE_ID
],
}
_PRESENTER_SUMMARIES
[
CourseSamples
.
DEMO_COURSE_ID
]
.
update
({
'catalog_course'
:
''
,
'start_date'
:
''
,
'end_date'
:
''
,
'pacing_type'
:
''
,
'availability'
:
''
,
})
_PRESENTER_SUMMARIES
[
_ANOTHER_DEPRECATED_COURSE_ID
]
.
update
({
'catalog_course'
:
''
,
'catalog_course_title'
:
''
,
'start_date'
:
''
,
'end_date'
:
''
,
'availability'
:
''
,
})
def
get_expected_summaries
(
self
,
course_ids
=
None
):
''''Expected results with default values, sorted, and filtered to course_ids.'''
if
course_ids
is
None
:
course_ids
=
[
CourseSamples
.
DEMO_COURSE_ID
,
CourseSamples
.
DEPRECATED_DEMO_COURSE_ID
,
'another/course/id'
]
summaries
=
[
summary
for
summary
in
self
.
mock_api_response
if
summary
[
'course_id'
]
in
course_ids
]
# fill in with defaults
for
summary
in
summaries
:
for
field
in
CourseSummariesPresenter
.
NON_NULL_STRING_FIELDS
:
if
summary
[
field
]
is
None
:
summary
[
field
]
=
''
# sort by title
return
sorted
(
summaries
,
key
=
lambda
x
:
(
not
x
[
'catalog_course_title'
],
x
[
'catalog_course_title'
]))
@override_settings
(
CACHES
=
{
'default'
:
{
'BACKEND'
:
'django.core.cache.backends.dummy.DummyCache'
,
}
})
@data
(
(
None
,
[
CourseSamples
.
DEMO_COURSE_ID
,
CourseSamples
.
DEPRECATED_DEMO_COURSE_ID
,
_ANOTHER_DEPRECATED_COURSE_ID
,
],
),
(
[
CourseSamples
.
DEPRECATED_DEMO_COURSE_ID
,
CourseSamples
.
DEMO_COURSE_ID
,
],
[
CourseSamples
.
DEMO_COURSE_ID
,
CourseSamples
.
DEPRECATED_DEMO_COURSE_ID
,
],
),
None
,
[
CourseSamples
.
DEMO_COURSE_ID
],
[
CourseSamples
.
DEPRECATED_DEMO_COURSE_ID
],
[
CourseSamples
.
DEMO_COURSE_ID
,
CourseSamples
.
DEPRECATED_DEMO_COURSE_ID
],
)
@unpack
def
test_get_summaries
(
self
,
input_course_ids
,
ouptut_course_ids
):
def
test_get_summaries
(
self
,
course_ids
):
''''Test courses filtered from API response.'''
presenter
=
CourseSummariesPresenter
()
if
input_course_ids
:
mock_api_response
=
[
self
.
_API_SUMMARIES
[
course_id
]
for
course_id
in
input_course_ids
]
else
:
mock_api_response
=
self
.
_API_SUMMARIES
.
values
()
expected_summaries
=
[
self
.
_PRESENTER_SUMMARIES
[
course_id
]
for
course_id
in
ouptut_course_ids
]
with
mock
.
patch
(
'analyticsclient.course_summaries.CourseSummaries.course_summaries'
,
mock
.
Mock
(
return_value
=
mock_api_response
)):
actual_summaries
,
last_updated
=
presenter
.
get_course_summaries
(
course_ids
=
input_
course_ids
)
self
.
assertListEqual
(
actual_summaries
,
expected_summaries
)
mock
.
Mock
(
return_value
=
self
.
mock_api_response
)):
actual_summaries
,
last_updated
=
presenter
.
get_course_summaries
(
course_ids
=
course_ids
)
self
.
assertListEqual
(
actual_summaries
,
self
.
get_expected_summaries
(
course_ids
)
)
self
.
assertEqual
(
last_updated
,
utils
.
CREATED_DATETIME
)
def
test_no_summaries
(
self
):
presenter
=
CourseSummariesPresenter
()
with
mock
.
patch
(
'analyticsclient.course_summaries.CourseSummaries.course_summaries'
,
mock
.
Mock
(
return_value
=
[])):
summaries
,
last_updated
=
presenter
.
get_course_summaries
()
self
.
assertListEqual
(
summaries
,
[])
self
.
assertIsNone
(
last_updated
)
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