Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

John/learner metrics multi course filtering #248

Merged
merged 4 commits into from
Aug 24, 2020

Conversation

johnbaldwin
Copy link
Contributor

This PR adds multiple course filtering for the learner-metrics endpoint.

New functions to support filtering

Adds two functions to figures.sites. This is to support multi-course
filtering for the learner-metrics endpoint

  1. enrollments_for_course_ids

This retrieves a CourseEnrollment queryset for enrollments that belong
to the list of course ids argument

  1. users_enrolled_in_courses

This retrieves a User queryset for users that are enrolled in the
courses identified by the course ids argument

Added tests for the new functionality

New learner-metrics query parameter, "course="

The learner-metrics endpoint now filters on one or more courses using
'course=some-course-id` query parameters. To filter on multiple courses,
simply do multiple parameters of the same "course" key. Example

?course-my-first-course-id&course=my-second-course-id
  • Added query param tests to the learner metrics viewset test class

Adds two functions to figures.sites. This is to support multi-course
filtering for the learner-metrics endpoint

1. enrollments_for_course_ids

This retrieves a CourseEnrollment queryset for enrollments that belong
to the list of course ids argument

2. users_enrolled_in_courses

This retrieves a User queryset for users that are enrolled in the
courses identified by the course ids argument

Added tests for the new functionality
The `learner-metrics` endpoint now filters on one or more courses using
'course=some-course-id` query parameters. To filter on multiple courses,
simply do multiple parameters of the same "course" key. Example

```
?course-my-first-course-id&course=my-second-course-id
```

* Added query param tests to the learner metrics viewset test class
@codecov-commenter
Copy link

codecov-commenter commented Aug 24, 2020

Codecov Report

Merging #248 into master will increase coverage by 0.06%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #248      +/-   ##
==========================================
+ Coverage   91.79%   91.86%   +0.06%     
==========================================
  Files          41       41              
  Lines        2133     2150      +17     
==========================================
+ Hits         1958     1975      +17     
  Misses        175      175              
Impacted Files Coverage Δ
figures/serializers.py 94.96% <100.00%> (+0.05%) ⬆️
figures/sites.py 68.81% <100.00%> (+2.53%) ⬆️
figures/views.py 92.85% <100.00%> (+0.13%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5664a24...f1f82d1. Read the comment docs.

Copy link
Contributor

@grozdanowski grozdanowski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, John! I have tested this out locally and it works like a charm. I also made changes required to the frontend (handling of multi-course filtering in API URL generation) and tested them out. This looks ready for staging to me!

Comment on lines +423 to +428
def query_param_course_keys(self):
"""
TODO: Mixin this
"""
cid_list = self.request.GET.getlist('course')
return [CourseKey.from_string(elem.replace(' ', '+')) for elem in cid_list]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .replace(' ', '+') is an anti pattern in the backend and is error-prune. The frontend should send properly escaped course keys with encodeURIComponent or use fetch() to take care of that.

Suggested change
def query_param_course_keys(self):
"""
TODO: Mixin this
"""
cid_list = self.request.GET.getlist('course')
return [CourseKey.from_string(elem.replace(' ', '+')) for elem in cid_list]
def query_param_course_keys(self):
"""
TODO: Mixin this
"""
cid_list = self.request.GET.getlist('course')
return [CourseKey.from_string(elem) for elem in cid_list]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I"ll make this change right after I have some coffee :)

Copy link
Contributor Author

@johnbaldwin johnbaldwin Aug 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OmarIthawi Well, I would love to make this change, but when I try it, I get this. So the anti-pattern appears to be required still.

>>> cid = 'course-v1:StarFleetAcademy SFA01 2161'
>>> from opaque_keys.edx.keys import CourseKey
>>> CourseKey.from_string(cid)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/jbaldwin/.virtualenvs/figtahoe2/lib/python2.7/site-packages/opaque_keys/__init__.py", line 197, in from_string
    return cls.deprecated_fallback._from_deprecated_string(serialized)
  File "/Users/jbaldwin/.virtualenvs/figtahoe2/lib/python2.7/site-packages/opaque_keys/edx/locator.py", line 379, in _from_deprecated_string
    raise InvalidKeyError(cls, serialized)
InvalidKeyError: <class 'opaque_keys.edx.locator.CourseLocator'>: course-v1:StarFleetAcademy SFA01 2161

and

>>> cid = 'course-v1:StarFleetAcademy+SFA01+2161'
>>> CourseKey.from_string(cid)
CourseLocator(u'StarFleetAcademy', u'SFA01', u'2161', None, None)

To show that the key is otherwise valid.

I know you are saying the front end should handle it, but this was done because we've needed to be defensive in the past on this. Otherwise, this little hack would not exist. And we don't have the bandwidth to make sure 100% this won't happen at the absolute worst possible time. So hack stays for now.

However, this is something that warrants a revisit to do a decorator or mixin for the course key handling

Copy link
Contributor

@OmarIthawi OmarIthawi Aug 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@johnbaldwin that's right, course-v1:StarFleetAcademy SFA01 2161 is not a valid key. Therefore the frontend should not send unescaped keys which what would make those keys invalid.

It's as if the frontend keeps adding a 999 to numbers and asking the API to subtract 999 before using the page argument. This will be more of an issue in the cases like:

  • One of our customers tries to use the figures API through a library that escapes the parameters properly like Python requests or pretty much every HTTP client out there.
  • Or if it's us working with Figures APIs through such libraries.

It's bothering that we're doing an extra step every time we make a Figures API to fix something in the wrong place.

@johnbaldwin johnbaldwin merged commit 5198774 into master Aug 24, 2020
@johnbaldwin johnbaldwin deleted the john/learner-metrics-multi-course-filtering branch August 24, 2020 14:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants