-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
[BB-2853] Add OAuth2-enabled endpoints for problem response report generation #27313
Conversation
Thanks for the pull request, @xitij2000! I've created OSPR-5734 to keep track of it in JIRA, where we prioritize reviews. Please note that it may take us up to several weeks or months to complete a review and merge your PR. Feel free to add as much of the following information to the ticket:
All technical communication about the code itself will be done via the GitHub pull request interface. As a reminder, our process documentation is here. Please let us know once your PR is ready for our review and all tests are green. |
673c6cd
to
d366719
Compare
'instructor_api_v0:list_instructor_tasks', | ||
'instructor_api_v0:list_report_downloads', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Originally, these were POST endpoints, however they are GET endpoints under the new API since they simply list items, and potentially a future API could add support for other operations.
@@ -179,6 +181,7 @@ | |||
'students_update_enrollment', | |||
'update_forum_role_membership', | |||
'override_problem_score', | |||
'instructor_api_v0:generate_problem_responses' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This remains a POST operation since it's not idempotent.
mock_problem_key.course_key = self.course.id | ||
with patch.object(UsageKey, 'from_string') as patched_method: | ||
patched_method.return_value = mock_problem_key | ||
self._access_endpoint('get_problem_responses', {}, 200, msg) | ||
self._access_endpoint(endpoint, {}, 200, msg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've reused the existing tests here, and just pointed them to run against the new API as well.
lms/djangoapps/instructor/urls.py
Outdated
urlpatterns = [ | ||
url(rf'^courses/{COURSE_ID_PATTERN}/instructor/api/', include(api_urls.urlpatterns)), | ||
url(rf'^instructor/api/v0/courses/{COURSE_ID_PATTERN}/', | ||
include((api_urls.v0_api_urls, 'lms.djangoapps.instructor'), namespace='instructor_api_v0')), | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The base path is now set here instead of in the plugin config.
""" | ||
Initiates asynchronous problem response report generation. | ||
""" | ||
return get_problem_responses(request=request, course_id=course_id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm just directly using the existing view method since it already does what we want with the exception of the auth methods supported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bit weird to use comma-separated strings in a JSON body, when we could even more easily use a proper list. But if that's what the existing API does, we better leave it that way.
@@ -1024,9 +1095,9 @@ def get_problem_responses(request, course_id): | |||
# The name of the POST parameter is `problem_location` (not pluralised) in | |||
# order to preserve backwards compatibility with existing third-party | |||
# scripts. | |||
problem_locations = request.POST.get('problem_location', '') | |||
problem_locations = getattr(request, 'data', request.POST).get('problem_location', '') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're coming to this method from the DRF view above, this data will be available in request.data
, while for the Django view it will be in request.POST
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^ This helpful comment should be added into the code itself, please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've split the parsing of this part out separately so that the new API can use a better format.
@require_POST | ||
@ensure_csrf_cookie | ||
@cache_control(no_cache=True, no_store=True, must_revalidate=True) | ||
@require_course_permission(permissions.SHOW_TASKS) | ||
def list_instructor_tasks(request, course_id): | ||
""" | ||
List instructor tasks. | ||
|
||
Takes optional query paremeters. | ||
Takes optional query parameters. | ||
- With no arguments, lists running tasks. | ||
- `problem_location_str` lists task history for problem | ||
- `problem_location_str` and `unique_student_identifier` lists task | ||
history for problem AND student (intersection) | ||
""" | ||
return _list_instructor_tasks(request=request, course_id=course_id) | ||
|
||
|
||
@cache_control(no_cache=True, no_store=True, must_revalidate=True) | ||
@require_course_permission(permissions.SHOW_TASKS) | ||
def _list_instructor_tasks(request, course_id): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original API uses POST, while the new endpoint is GET, so I've created a very small wrapper to just add the post requirement and CSRF requirement.
v0_api_urls = [ | ||
url(r'^tasks$', api.InstructorTasks.as_view(), name='list_instructor_tasks', ), | ||
url(r'^reports$', api.ReportDownloads.as_view(), name='list_report_downloads', ), | ||
url(r'^generate_report/problem_responses$', api.ProblemResponseReportInitiate.as_view(), | ||
name='generate_problem_responses', ), | ||
] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are v0
URLs only because they are just porting existing code. They've been around a long time and have stayed stable to might be good candidates for v1
.
However, when the UI that uses these is ported to an MFE, these APIs can be reevaluated, and a decision can be made about whether these are good enough, or if we need new v1
APIs the are better driven by MFEs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^ This comment may be worth including inline in the code itself too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, while writing that comment I convinced myself to just call these v1
. With v0
the implication is that the API is still in development and may change in backwards-incompatible ways without bumping up the version number. That doesn't seem to be the case here. These APIs have been in use for a while without major changes.
@xitij2000 Thank you for your contribution. Please let me know once it is ready for our review. |
@natabene Sure, will do. |
d366719
to
dffa06c
Compare
@xitij2000 Good idea to make these new endpoints. And thanks for adding these nice comments explaining your changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
- I tested this: Old endpoints work, task_id is returned on new endpoint and a new report is generated and list api returns the list of all tasks
- I read through the code
- I checked for accessibility issues
- Includes documentation
@xitij2000 Thanks for the helpful comments.
@natabene This is good to review now. CC: @bradenmacdonald Is this a candidate for core committer review? If so I can mark you as reviewer and you could take it up next sprint? |
@natabene @xitij2000 I would be happy to review this, but I'll give @ormsbee the option first, since he reviewed the earlier version of it and also had to revert it. @ormsbee do you want to review? |
5dd4a86
to
c994306
Compare
@bradenmacdonald Sounds like this is yours to review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xitij2000 Nice work, thanks 👍🏻
Please squash and fix the minor docs issues I just noticed:
…can be driven by MFEs.
@bradenmacdonald Done! |
Your PR has finished running tests. There were no failures. |
@xitij2000 🎉 Your pull request was merged! Please take a moment to answer a two question survey so we can improve your experience in the future. |
EdX Release Notice: This PR has been deployed to the staging environment in preparation for a release to production. |
EdX Release Notice: This PR has been deployed to the production environment. |
Thanks! |
…can be driven by MFEs. (openedx#27313) (cherry picked from commit 161e356)
…can be driven by MFEs. (openedx#27313) (#381) (cherry picked from commit 161e356)
@xitij2000 could you port this changes to lilac.master? as part of BB-4877 |
…can be driven by MFEs. (openedx#27313) (cherry picked from commit 161e356)
Currently, the endpoints related to generation of problem response reports, only support session-based authentication, and cannot be easily used by MFE or other API clients.
A previous PR for this was reverted since it causes some issues. While we were not able to reproduce these issues, in the interest of disturbing as little of the existing code as possible, this PR retains the existing endpoints, but adds alternative endpoints that expose the same views but through a DRF setup that supports OAuth2-based authentication.
Discussions:
Sandbox URL: TBD - sandbox is being provisioned.
Merge deadline: "None"
Testing instructions:
paer test_system -t lms/djangoapps/instructor/tests/test_api.py
Make sure to use a user that has permissions to generate a report, such as a staff user/
generate_problem_responses
endpoint for a course with a correspondingproblem_location
. This will return a task id.list_instructor_task
endpoint for the same course. Usually, it is hard to observe the task actually in progress in the devstack, without too much data. The endpoint should work with OAuth though, which is the main thing to test.list_report_downloads
endpoint. It should include the newly-generated report.Reviewers
Settings