diff --git a/kolibri/core/auth/api.py b/kolibri/core/auth/api.py index 3d49cfc8fbc..afa30e0adfe 100644 --- a/kolibri/core/auth/api.py +++ b/kolibri/core/auth/api.py @@ -214,6 +214,9 @@ class FacilityUserFilter(FilterSet): exclude_member_of = ModelChoiceFilter( method="filter_exclude_member_of", queryset=Collection.objects.all() ) + exclude_coach_for = ModelChoiceFilter( + method="filter_exclude_coach_for", queryset=Collection.objects.all() + ) exclude_user_type = ChoiceFilter( choices=USER_TYPE_CHOICES, method="filter_exclude_user_type", @@ -232,6 +235,16 @@ def filter_user_type(self, queryset, name, value): def filter_exclude_member_of(self, queryset, name, value): return queryset.exclude(Q(memberships__collection=value) | Q(facility=value)) + def filter_exclude_coach_for(self, queryset, name, value): + return queryset.exclude( + Q(roles__in=Role.objects.filter(kind=role_kinds.COACH, collection=value)) + | Q( + roles__in=Role.objects.filter( + kind=role_kinds.COACH, collection_id=value.parent_id + ) + ) + ) + def filter_exclude_user_type(self, queryset, name, value): if value == "learner": return queryset.exclude(roles__isnull=True) diff --git a/kolibri/plugins/facility/assets/src/modules/classAssignMembers/handlers.js b/kolibri/plugins/facility/assets/src/modules/classAssignMembers/handlers.js index fb47a9219c5..fdca46cb1b5 100644 --- a/kolibri/plugins/facility/assets/src/modules/classAssignMembers/handlers.js +++ b/kolibri/plugins/facility/assets/src/modules/classAssignMembers/handlers.js @@ -1,7 +1,7 @@ import ConditionalPromise from 'kolibri.lib.conditionalPromise'; +import pickBy from 'lodash/pickBy'; import samePageCheckGenerator from 'kolibri.utils.samePageCheckGenerator'; import { ClassroomResource, FacilityUserResource } from 'kolibri.resources'; -import { UserKinds } from 'kolibri.coreVue.vuex.constants'; import { _userState } from '../mappers'; export function showLearnerClassEnrollmentPage(store, toRoute) { @@ -9,32 +9,23 @@ export function showLearnerClassEnrollmentPage(store, toRoute) { store.dispatch('preparePage'); // facility users that are not enrolled in this class const userPromise = FacilityUserResource.fetchCollection({ - getParams: { + getParams: pickBy({ member_of: facility_id || store.getters.activeFacilityId, - page_size: 30, - page: 1, + page: toRoute.query.page || 1, + page_size: toRoute.query.page_size || 30, exclude_member_of: id, - }, + exclude_coach_for: id, + }), force: true, }); // current class const classPromise = ClassroomResource.fetchModel({ id }); - // users in current class - const classUsersPromise = FacilityUserResource.fetchCollection({ - getParams: { - member_of: id, - page_size: 30, - page: 1, - }, - force: true, - }); - return ConditionalPromise.all([userPromise, classPromise, classUsersPromise]).only( + return ConditionalPromise.all([userPromise, classPromise]).only( samePageCheckGenerator(store), - ([facilityUsers, classroom, classUsers]) => { + ([facilityUsers, classroom]) => { store.commit('classAssignMembers/SET_STATE', { facilityUsers: facilityUsers.results.map(_userState), - classUsers: classUsers.results.map(_userState), totalPageNumber: facilityUsers.total_pages, totalLearners: facilityUsers.count, class: classroom, @@ -48,23 +39,20 @@ export function showLearnerClassEnrollmentPage(store, toRoute) { ); } -const eligibleRoles = [ - UserKinds.ASSIGNABLE_COACH, - UserKinds.COACH, - UserKinds.ADMIN, - UserKinds.SUPERUSER, -]; - export function showCoachClassAssignmentPage(store, toRoute) { const { id, facility_id } = toRoute.params; store.commit('CORE_SET_PAGE_LOADING', true); const facilityId = facility_id || store.getters.activeFacilityId; - // all users in facility - // NOTE: - // don't use backend pagination here, since we are filtering users with multiple eligible roles - // just exclude the learners to reduce the queryset size + // all users in facility eligible to be a coach that is not already a coach const userPromise = FacilityUserResource.fetchCollection({ - getParams: { member_of: facilityId, exclude_member_of: id, exclude_user_type: 'learner' }, + getParams: { + member_of: facilityId, + exclude_member_of: id, + exclude_user_type: 'learner', + exclude_coach_for: id, + page: toRoute.query.page || 1, + page_size: toRoute.query.page_size || 30, + }, force: true, }); // current class @@ -73,17 +61,11 @@ export function showCoachClassAssignmentPage(store, toRoute) { return ConditionalPromise.all([userPromise, classPromise]).only( samePageCheckGenerator(store), ([facilityUsers, classroom]) => { - let filteredFacilityUsers = facilityUsers - .filter(user => { - // filter out users who are not eligible to be coaches - return user.roles.some(({ kind }) => eligibleRoles.includes(kind)); - }) - .map(_userState); store.commit('classAssignMembers/SET_STATE', { // facilityUsers now only contains users that are eligible for coachdom - // TODO rename - facilityUsers: filteredFacilityUsers, - classUsers: classroom.coaches.map(_userState), + facilityUsers: facilityUsers.results.map(_userState), + totalPageNumber: facilityUsers.total_pages, + totalLearners: facilityUsers.count, class: classroom, modalShown: false, }); diff --git a/kolibri/plugins/facility/assets/src/modules/classAssignMembers/index.js b/kolibri/plugins/facility/assets/src/modules/classAssignMembers/index.js index 110fe57bd90..a2e734f1976 100644 --- a/kolibri/plugins/facility/assets/src/modules/classAssignMembers/index.js +++ b/kolibri/plugins/facility/assets/src/modules/classAssignMembers/index.js @@ -4,7 +4,6 @@ import { assignCoachesToClass, enrollLearnersInClass } from './actions'; function defaultState() { return { class: {}, - classUsers: [], facilityUsers: [], modalShown: false, }; diff --git a/kolibri/plugins/facility/assets/src/views/ClassEnrollForm.vue b/kolibri/plugins/facility/assets/src/views/ClassEnrollForm.vue index 9298901d454..3767524171d 100644 --- a/kolibri/plugins/facility/assets/src/views/ClassEnrollForm.vue +++ b/kolibri/plugins/facility/assets/src/views/ClassEnrollForm.vue @@ -3,39 +3,22 @@