BUM SidePanel: Implement Assign (coaches) to classes functionality#13614
Conversation
Build Artifacts
|
0afc02c to
99e764a
Compare
LianaHarris360
left a comment
There was a problem hiding this comment.
I noted a few changes that need to be made on the frontend. In the newest Figma designs, the top the warning messages have been updated:
- Only includes the infoOutline
KIcon - Should contain the two messages:
- '[N] users are learners and won’t be assigned' (only if learners have been selected)
- 'Users already in selected classes stay unchanged' - Should have the gray (
palette.grey.v_100) background
kolibri/plugins/facility/assets/src/views/users/sidePanels/AssignCoachesSidePanel.vue
Outdated
Show resolved
Hide resolved
kolibri/plugins/facility/assets/src/views/users/sidePanels/AssignCoachesSidePanel.vue
Outdated
Show resolved
Hide resolved
kolibri/plugins/facility/assets/src/views/users/sidePanels/AssignCoachesSidePanel.vue
Outdated
Show resolved
Hide resolved
kolibri/plugins/facility/assets/src/views/users/sidePanels/AssignCoachesSidePanel.vue
Outdated
Show resolved
Hide resolved
nucleogenesis
left a comment
There was a problem hiding this comment.
I added some notes about a wonky "Cancel" button and some suggestions for the RoleListSerializer. I tested manually and things worked well though.
| <!-- Footer Buttons --> | ||
| <div class="footer-buttons"> | ||
| <KButton | ||
| appearance="secondary-button" |
There was a problem hiding this comment.
This should be flat-button and to determine primariness-vs-secondariness you can use the primary prop set as false.
kolibri/core/auth/serializers.py
Outdated
| try: | ||
| obj, created = ModelClass.objects.get_or_create(**model_data) | ||
| if created: | ||
| created_objects.append(obj) | ||
| except Exception as e: |
There was a problem hiding this comment.
A few things to note here.
- It is a Python anti-pattern to except the
Exceptionclass - in part because it will often except on errors you never expected it to. Some well-aged Stack Overflow discussion on the topic.
I don't think we really need to do a try/catch here because the only data you're getting here is already validated. If there was something wrong w/ the data it ought to have failed in the RoleSerializer#validate method below.
-
It was a bit confusing to me seeing the generic class variable
ModelClassbecause I couldn't figure out where/when/whyModelClasswould be anything other than theRoleclass here. -
You could also avoid looping over each item by using the Django
Queryset#bulk_createmethod w/ theignore_conflicts=Trueparameter - this should basically be a bulk version ofget_or_createbecause it'll ignoreIntegrityErrorswhen trying to create something that exists already.
There was a problem hiding this comment.
For 3: I initially started with implementing it using bulk_create. I was not successful in getting the desired results, it bypasses model/serializer validation, which leads to unique constraints still surfacing awkwardly. Even with ignore_conflicts, we hit unique key constraint edge cases and couldn’t distinguish conflicts from real errors cleanly..
There was a problem hiding this comment.
Thanks - if we can get a sense of what the errors are we can investigate further. Optimizing this won't be a blocker for merge.
The requested changes have been addressed.
31ae124 to
7094a1f
Compare
| created_objects = [] | ||
|
|
||
| for model_data in validated_data: | ||
| obj, created = Role.objects.get_or_create(**model_data) |
There was a problem hiding this comment.
Noting for posterity here that the bulk_create method did not work out here because among the caveats for that method includes that it doesn't work with many-to-many relationships, so the IntegrityErrors persisted despite using the ignore_conflicts kwarg. (cc @rtibbles)
There was a problem hiding this comment.
But, the Role object only has direct ForeignKey relationships, not ManyToMany fields? So I'm not sure why that caveat would apply here: https://github.com/learningequality/kolibri/blob/develop/kolibri/core/auth/models.py#L1343
There was a problem hiding this comment.
I trusted my intuition and didn't consult the model 🤦♂️
I wonder if it's because save() isn't called then (or one of the other caveats). This part of the save() method in particular seems like it'd avoid the error @ozer550 was getting:
[python-devserver] return Database.Cursor.execute(self, query, params)
[python-devserver] django.db.utils.IntegrityError: UNIQUE constraint failed: kolibriauth_role.user_id, kolibriauth_role.collection_id, kolibriauth_role.kindCurious what you think @rtibbles - if the save() method not being called is the culprit, seems we're stuck iterating rather than batching the creates here?
There was a problem hiding this comment.
I agree that this may be something specific for morango models. My guess is that the id is not being set properly during bulk create. I have no objections with us not doing the bulk create at this juncture.
It's an optimization, and we can dig in more if it seems necessary during testing - as long as we encourage Peter to try with large numbers of users!
|
Hi @ozer550, as always I'm listing here all of my testing observations for consideration, anything out of scope for this PR can be filed as a separate issue:
assign.to.deleted.class.mp4assign.0.users.mp4assign.deleted.coach.mp4
users.remain.selected.mp4
|
30f49c0 to
9ff8e08
Compare
AlexVelezLl
left a comment
There was a problem hiding this comment.
Just one important note: We usually should try not to make api requests inside fors as much as possible. The rest of the comments are non blocking :), in general there are some things we can standarize across these side panels, but it's probably a better idea to handle it separately.
kolibri/plugins/facility/assets/src/views/users/sidePanels/AssignCoachesSidePanel.vue
Outdated
Show resolved
Hide resolved
kolibri/plugins/facility/assets/src/views/users/sidePanels/AssignCoachesSidePanel.vue
Outdated
Show resolved
Hide resolved
kolibri/plugins/facility/assets/src/views/users/sidePanels/AssignCoachesSidePanel.vue
Outdated
Show resolved
Hide resolved
kolibri/plugins/facility/assets/src/views/users/sidePanels/AssignCoachesSidePanel.vue
Outdated
Show resolved
Hide resolved
kolibri/plugins/facility/assets/src/views/users/sidePanels/AssignCoachesSidePanel.vue
Outdated
Show resolved
Hide resolved
kolibri/plugins/facility/assets/src/views/users/sidePanels/AssignCoachesSidePanel.vue
Outdated
Show resolved
Hide resolved
| </template> | ||
|
|
||
| <KModal | ||
| v-if="showCloseConfirmationModal" |
There was a problem hiding this comment.
We have this CloseConfirmationGuard component to abstract the confirmation modal logic.
Here is an example of how we can use it. (Just noticed the beforeRouteLeave link that needs to be done is not well docummented in the component 😅).
0e6c879 to
3024c79
Compare
3024c79 to
ff119a4
Compare
AlexVelezLl
left a comment
There was a problem hiding this comment.
Code changes look good to me!
|
Hi @pcenov! Due to some latest changes structurally and logically this would require re-QA. |
|
Thanks @ozer550 - no new issues observed while testing again, LGTM! |
The requested changes have been addressed.






Summary
This pull request updates the AssignCoachesSidePanel component to carry out the bulk action to assign coaches to one or more classes, with intelligent filtering to handle mixed user selections.
AssignCoaches in Bulk: Uses RoleResource to bulk assign or un-assign (undo) coaches to classes with proper role management and duplicate prevention.
User Filtering: The component now automatically filters out learners from the assignment process, only assigning coaches, admins, and superusers to classes. Users can now select a mix of coaches/admins and learners, with the system automatically handling the filtering and showing appropriate warnings.
Fixed Button Assign Logic: The "Assign coach" button on the main users page is now enabled when at least one eligible user (coach/admin/superuser) is selected, rather than requiring all selected users to be eligible.
Discard functionality: Added a confirmation modal that appears when users try to close the side panel with unsaved class selections, preventing accidental loss of work.
Undo System: After successful assignment, a confirmation modal allows users to undo the coach assignments, with proper API calls to remove the created roles. changes also properly handle reassigning coaches who are already assigned to classes, preventing duplicate assignments and ensuring clean role management.
Assigning coaches using SidePanel:
assigncoach.mp4
Assigning coaches and undoing:
undo.mp4
References
closes #13407
Reviewer guidance