Skip to content

Commit

Permalink
Merge pull request #2044 from openedx/asheehan-edx/ENT-8439
Browse files Browse the repository at this point in the history
feat: admin pages for enterprise groups and enterprise group memberships
  • Loading branch information
alex-sheehan-edx committed Mar 7, 2024
2 parents beb2832 + c1ea930 commit 7ddd2ce
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 27 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ Change Log
Unreleased
----------
[4.13.4]
---------
* feat: admin pages for enterprise groups and enterprise group memberships

[4.13.3]
---------
* feat: adding management command to remove expired pending group memberships
Expand Down
2 changes: 1 addition & 1 deletion enterprise/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Your project description goes here.
"""

__version__ = "4.13.3"
__version__ = "4.13.4"
57 changes: 56 additions & 1 deletion enterprise/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ class Meta:
)

list_display = ('username', 'user_email', 'get_enterprise_customer')
search_fields = ('user_id',)
search_fields = ('user_id', 'user_email',)

@admin.display(
description='Enterprise Customer'
Expand Down Expand Up @@ -583,6 +583,11 @@ class Meta:
'created'
)

search_fields = (
'user_email',
'id'
)

readonly_fields = (
'user_email',
'enterprise_customer',
Expand Down Expand Up @@ -1155,3 +1160,53 @@ def mark_configured(self, request, obj):
obj.save()

mark_configured.label = "Mark as Configured"


@admin.register(models.EnterpriseGroup)
class EnterpriseGroupAdmin(admin.ModelAdmin):
"""
Django admin for EnterpriseGroup model.
"""
model = models.EnterpriseGroup
list_display = ('uuid', 'enterprise_customer', 'applies_to_all_contexts', )
list_filter = ('applies_to_all_contexts',)
search_fields = (
'uuid',
'name',
'enterprise_customer__name',
'enterprise_customer__uuid',
)
readonly_fields = ('count', 'members',)

def members(self, obj):
"""
Return the non-deleted members of a group
"""
return obj.get_all_learners()

@admin.display(description="Number of members in group")
def count(self, obj):
"""
Return the number of members in a group
"""
return len(obj.get_all_learners())


@admin.register(models.EnterpriseGroupMembership)
class EnterpriseGroupMembershipAdmin(admin.ModelAdmin):
"""
Django admin for EnterpriseGroupMembership model.
"""
model = models.EnterpriseGroupMembership
list_display = ('group', 'membership_user',)
search_fields = (
'uuid',
'group__enterprise_customer_user',
'enterprise_customer_user',
'pending_enterprise_customer_user',
)
autocomplete_fields = (
'group',
'enterprise_customer_user',
'pending_enterprise_customer_user',
)
27 changes: 2 additions & 25 deletions enterprise/api/v1/views/enterprise_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,31 +120,8 @@ def get_learners(self, *args, **kwargs):
group_uuid = kwargs.get('group_uuid')
try:
group_object = self.get_queryset().get(uuid=group_uuid)
if group_object.applies_to_all_contexts:
members = []
customer_users = models.EnterpriseCustomerUser.objects.filter(
enterprise_customer=group_object.enterprise_customer,
active=True,
)
pending_customer_users = models.PendingEnterpriseCustomerUser.objects.filter(
enterprise_customer=group_object.enterprise_customer,
)
for ent_user in customer_users:
members.append(models.EnterpriseGroupMembership(
uuid=None,
enterprise_customer_user=ent_user,
group=group_object,
))
for pending_user in pending_customer_users:
members.append(models.EnterpriseGroupMembership(
uuid=None,
pending_enterprise_customer_user=pending_user,
group=group_object,
))
page = self.paginate_queryset(members)
else:
learner_list = group_object.members.all()
page = self.paginate_queryset(learner_list)
members = group_object.get_all_learners()
page = self.paginate_queryset(members)
serializer = serializers.EnterpriseGroupMembershipSerializer(page, many=True)
response = self.get_paginated_response(serializer.data)
return response
Expand Down
58 changes: 58 additions & 0 deletions enterprise/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4249,6 +4249,36 @@ class Meta:
unique_together = (("name", "enterprise_customer"),)
ordering = ['-modified']

def get_all_learners(self):
"""
Returns all users associated with the group, whether the group specifies the entire org else all associated
membership records.
"""
if self.applies_to_all_contexts:
members = []
customer_users = EnterpriseCustomerUser.objects.filter(
enterprise_customer=self.enterprise_customer,
active=True,
)
pending_customer_users = PendingEnterpriseCustomerUser.objects.filter(
enterprise_customer=self.enterprise_customer,
)
for ent_user in customer_users:
members.append(EnterpriseGroupMembership(
uuid=None,
enterprise_customer_user=ent_user,
group=self,
))
for pending_user in pending_customer_users:
members.append(EnterpriseGroupMembership(
uuid=None,
pending_enterprise_customer_user=pending_user,
group=self,
))
return members
else:
return self.members.filter(is_removed=False)


class EnterpriseGroupMembership(TimeStampedModel, SoftDeletableModel):
"""
Expand Down Expand Up @@ -4287,3 +4317,31 @@ class Meta:
# ie no issue if multiple fields have: group = A and pending_enterprise_customer_user = NULL
unique_together = (("group", "enterprise_customer_user"), ("group", "pending_enterprise_customer_user"))
ordering = ['-modified']

@property
def membership_user(self):
"""
Return the user record associated with the membership, defaulting to ``enterprise_customer_user``
and falling back on ``obj.pending_enterprise_customer_user``
"""
return self.enterprise_customer_user or self.pending_enterprise_customer_user

def clean(self, *args, **kwargs):
"""
Ensure that records added via Django Admin have matching customer records between learner and group.
"""
user = self.membership_user
if user:
user_customer = user.enterprise_customer
if user_customer != self.group.enterprise_customer:
raise ValidationError(
'Enterprise Customer associated with membership group must match the Enterprise Customer associated'
' with the memberships user'
)
super().clean(*args, **kwargs)

def __str__(self):
"""
Return human-readable string representation.
"""
return f"member: {self.membership_user} in group: {self.uuid}"

0 comments on commit 7ddd2ce

Please sign in to comment.