[BD-24] [BB-2726] [TNL-7330] Added batch_get_or_create class method for ExternalId - #25844
Conversation
|
Thanks for the pull request, @shimulch! I've created BLENDED-789 to keep track of it in Jira. More details are on the BD-24 project page. When this pull request is ready, tag your edX technical lead. |
giovannicimolin
left a comment
There was a problem hiding this comment.
Left a few comments.
There was a problem hiding this comment.
This is returning ExternalId model, when ideally it should return a dict mapping id to external_user_ids and signaling if they where created.
Can you make this function return something like the reponse shown below?
{
"id_1": ("external_id_1", True), # (external_id, created)
"id_2": ("external_id_2", False),
}
There was a problem hiding this comment.
This way we keep the external user ID API purely using data without passing classes around.
There was a problem hiding this comment.
@giovannicimolin, Now the method returns a dictionary as per the suggested structure.
There was a problem hiding this comment.
@shimulch I think we can get rid of the created argument here. To simplify the implementation, a "user_id": ExternalId map is enough here.
There was a problem hiding this comment.
| def batch_get_or_create(cls, users, type_name): | |
| def batch_get_or_create_user_ids(cls, users, type_name): |
There was a problem hiding this comment.
I think this is not the optimal solution, given that a potentially big user dataset might come in.
Forcing a queryset evaluation and then subtracting two user objects sets might be an expensive operation.
There was a problem hiding this comment.
@giovannicimolin, I've optimized it to use a query instead of set operation.
|
@shimulch Thank you for your contribution. Please ping me once it is ready for edX review. |
giovannicimolin
left a comment
There was a problem hiding this comment.
Aside a minor change in the data returned and a comment about a test, this is good to go.
There was a problem hiding this comment.
Since this is a batch operation, we should ideally add some query count test here, to make sure more users != more queries.
This can be done with a simple assertion in the test_batch_get_or_create_user_ids (see assertNumQueries, might require a change to TransactionTestCase instead of TestCase).
There was a problem hiding this comment.
Tests with the expected number of queries added. Thanks for the tip 👍🏽
There was a problem hiding this comment.
@shimulch I think we can get rid of the created argument here. To simplify the implementation, a "user_id": ExternalId map is enough here.
There was a problem hiding this comment.
Can you please provide an example of when it's useful to create these in a batch, instead of creating them for each user as needed?
There was a problem hiding this comment.
@bradenmacdonald, In the case of LTI Consumer, External ID's are created when a student actually launches LTI from LMS. But when providing course member information to a third party tool, not every member has External ID's available. To create one by one would be a performance issue. Thus this method has been created.
There was a problem hiding this comment.
@shimulch Oops, I meant to specify that I'd like that added to the docstring. Dosctrings should generally say both what the code does and why it exists, because without the why it's hard to know when/how it can be refactored or removed in the future.
489a9fc to
b7951e3
Compare
|
@shimulch Just checking if you are still planning on pursuing this PR. |
|
@natabene CC @bradenmacdonald @shimulch This PR is related to openedx/xblock-lti-consumer#124, which is for a Blended project. Could someone tag it so we know it's not an ordinary OSPR? |
|
@natabene As @pomegranited mentioned, this is related to BD-24. We ended up having discussions related to the implementation on this ADR before continuing the work. We're now unblocked and will start moving this forward in the coming week. |
b7951e3 to
8654d80
Compare
4556a0d to
e0785b0
Compare
There was a problem hiding this comment.
@shimulch Code is working perfectly, but I have a few nits and changes:
- Python APIs should not return internal classes/objects whenever possible. Please change the
ExternalIdobject on the response with the plain text external id. - Improve this PR test instructions and proper testing instructions for verifying this works on the devstack.
- Clean up the unused description fields.
- Rebase this PR on the latest
master.
There was a problem hiding this comment.
ExternalIds have no relation to LTI. This explanation should be in the lti_consumer code that uses this API.
Here you should put a description of what this API does, its limitations and return format.
There was a problem hiding this comment.
Removed, LTI specific doc, and added generic doc which explains what this method does.
Update doc string
6c6b020 to
8db49de
Compare
This method is just a batch version of the existing
Thanks for pointing that out. I've added proper test instruction on how to verify the new method.
If you are talking about the PR description, then yes, I've cleaned up. If that's not the case I am probably not sure what you meant.
Rebased. |
giovannicimolin
left a comment
There was a problem hiding this comment.
👍
- I tested this:
- Followed the testing instructions on the devstack.
- Checked that the query count didn't increase when increasing the number of users created (tested with 2, 20, and 200 users in the test script above).
- I read through the code
-
I checked for accessibility issuesNA - Includes documentation: In docstrings
@shimulch This is good to go. Great work.
@nedbat This is ready for your review.
| type_obj = ExternalIdType.objects.get(name=type_name) | ||
| except ExternalIdType.DoesNotExist: | ||
| LOGGER.info( | ||
| 'Batch ID Creation failed, no external id type of {type}'.format( |
There was a problem hiding this comment.
| 'Batch ID Creation failed, no external id type of {type}'.format( | |
| 'Batch ID Creation failed, no external id type of {type!r}'.format( |
| type=type_name | ||
| ) | ||
| ) | ||
| return None |
There was a problem hiding this comment.
Should this be {} instead to stick to the signature of the function?
There was a problem hiding this comment.
Or would it be better to raise an exception? I don't know about ExternalIdType, and what callers expect.
There was a problem hiding this comment.
(or at least update the docstring)
There was a problem hiding this comment.
The reasoning behind sending None is to keep it similar to add_new_user_id which also returns None if fails. I've updated the docstring.
| with self.assertNumQueries(self.EXPECTED_NUM_OF_QUERIES): | ||
| result = ExternalId.batch_get_or_create_user_ids(users, id_type) | ||
|
|
||
| assert len(result) == len(result) |
There was a problem hiding this comment.
This surely is not what you meant!
| assert len(result) == len(result) | |
| assert len(result) == len(users) |
There was a problem hiding this comment.
Sorry for that and thanks for catching it.
nedbat
left a comment
There was a problem hiding this comment.
GitHub says I have to leave a comment explaining the changes, so I will point you to all of the comments I left explaining changes :)
|
Your PR has finished running tests. There were no failures. |
|
@nedbat Thanks for the review. I've addressed your comments. :-) |
|
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. |
|
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. |
1 similar comment
|
EdX Release Notice: This PR has been deployed to the production environment. |
openedx/xblock-lti-consumer#124 depends on this PR.
This PR adds a new class method
batch_get_or_create_user_idstoExternalId. This enables a faster way to createExternalIdfor a list ofUser.When
batch_get_or_create_user_idsgets called with a list ofUserinstances and atype_name(Name of the type of ExternalId) it will return a dict mapping user id with corresponding external id.{ user_id: ExternalId }JIRA tickets: This PR completes a part of the functionality required in the following tickets -
Dependencies: None
Merge deadline: None
Testing instructions:
make lms-shellpython manage.py lms shellExternalIdmodel{10: <ExternalId: ExternalId object (None)>, 11: <ExternalId: ExternalId object (None)>, 12: <ExternalId: ExternalId object (None)>, 13: <ExternalId: ExternalId object (None)>, 14: <ExternalId: ExternalId object (None)>}Author notes and concerns:
N/A
Reviewers