-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: REST APIs for default-enterprise-enrollment-intentions
- Loading branch information
1 parent
3bd4bd6
commit f25d939
Showing
12 changed files
with
425 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,4 @@ | |
Your project description goes here. | ||
""" | ||
|
||
__version__ = "4.29.0" | ||
__version__ = "4.30.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
174 changes: 174 additions & 0 deletions
174
enterprise/api/v1/views/default_enterprise_enrollments.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
""" | ||
Views for default enterprise enrollments. | ||
""" | ||
|
||
from uuid import UUID | ||
|
||
from rest_framework.decorators import action | ||
from rest_framework.exceptions import ValidationError | ||
from rest_framework.response import Response | ||
from rest_framework import status, viewsets | ||
from edx_rbac.mixins import PermissionRequiredForListingMixin | ||
|
||
from django.contrib.auth import get_user_model | ||
|
||
from enterprise import models | ||
from enterprise.api.v1 import serializers | ||
from enterprise.api.v1.views.base_views import EnterpriseViewSet | ||
from enterprise.constants import DEFAULT_ENTERPRISE_ENROLLMENT_INTENTIONS_PERMISSION | ||
|
||
|
||
class DefaultEnterpriseEnrollmentIntentionViewSet( | ||
PermissionRequiredForListingMixin, | ||
EnterpriseViewSet, | ||
viewsets.ModelViewSet, | ||
): | ||
""" | ||
API views for default enterprise enrollment intentions | ||
""" | ||
|
||
permission_required = DEFAULT_ENTERPRISE_ENROLLMENT_INTENTIONS_PERMISSION | ||
list_lookup_field = 'enterprise_customer__uuid' | ||
serializer_class = serializers.DefaultEnterpriseEnrollmentIntentionSerializer | ||
http_method_names = ['get', 'post', 'delete'] | ||
|
||
@property | ||
def requested_enterprise_customer_uuid(self): | ||
""" | ||
Get and validate the enterprise customer UUID from the query parameters. | ||
""" | ||
if not (enterprise_customer_uuid := self.request.query_params.get('enterprise_customer_uuid')): | ||
raise ValidationError({"enterprise_customer_uuid": "This query parameter is required."}) | ||
|
||
try: | ||
UUID(enterprise_customer_uuid) | ||
except ValueError as exc: | ||
raise ValidationError({"enterprise_customer_uuid": "Invalid UUID format."}) from exc | ||
return enterprise_customer_uuid | ||
|
||
@property | ||
def requested_lms_user_id(self): | ||
""" | ||
Get the (optional) LMS user ID from the request. | ||
""" | ||
return self.request.query_params.get('lms_user_id') | ||
|
||
@property | ||
def base_queryset(self): | ||
""" | ||
Required by the `PermissionRequiredForListingMixin`. | ||
For non-list actions, this is what's returned by `get_queryset()`. | ||
For list actions, some non-strict subset of this is what's returned by `get_queryset()`. | ||
""" | ||
kwargs = {} | ||
if self.requested_enterprise_customer_uuid: | ||
kwargs['enterprise_customer'] = self.requested_enterprise_customer_uuid | ||
return models.DefaultEnterpriseEnrollmentIntention.objects.filter(**kwargs) | ||
|
||
@property | ||
def user_for_learner_status(self): | ||
""" | ||
Get the user for learner status based on the request. | ||
""" | ||
if self.request.user.is_staff and self.requested_lms_user_id is not None: | ||
User = get_user_model() | ||
try: | ||
return User.objects.get(id=self.requested_lms_user_id) | ||
except User.DoesNotExist: | ||
return None | ||
|
||
return self.request.user | ||
|
||
def get_permission_object(self): | ||
""" | ||
Used for "retrieve" actions. Determines the context (enterprise UUID) to check | ||
against for role-based permissions. | ||
""" | ||
return self.requested_enterprise_customer_uuid | ||
|
||
@action(detail=False, methods=['get'], url_path='learner-status') | ||
def learner_status(self, request): # pylint: disable=unused-argument | ||
""" | ||
Get the status of the learner's enrollment in the default enterprise course. | ||
""" | ||
# Validate the enterprise customer uuid. | ||
try: | ||
enterprise_customer_uuid = self.requested_enterprise_customer_uuid | ||
except ValidationError as exc: | ||
return Response(exc, status=status.HTTP_400_BAD_REQUEST) | ||
|
||
# Validate the user for learner status exists and is associated | ||
# with the enterprise customer. | ||
if not (user_for_learner_status := self.user_for_learner_status): | ||
return Response( | ||
{'detail': f'User with lms_user_id {self.requested_lms_user_id} not found.'}, | ||
status=status.HTTP_400_BAD_REQUEST, | ||
) | ||
|
||
try: | ||
enterprise_customer_user = models.EnterpriseCustomerUser.objects.get( | ||
user_id=user_for_learner_status.id, | ||
enterprise_customer=enterprise_customer_uuid, | ||
) | ||
except models.EnterpriseCustomerUser.DoesNotExist: | ||
return Response( | ||
{ | ||
'detail': ( | ||
f'User with lms_user_id {user_for_learner_status.id} is not associated with ' | ||
f'the enterprise customer {enterprise_customer_uuid}.' | ||
), | ||
}, | ||
status=status.HTTP_400_BAD_REQUEST, | ||
) | ||
|
||
# Retrieve configured default enrollment intentions for the enterprise customer | ||
default_enrollment_intentions_for_customer = models.DefaultEnterpriseEnrollmentIntention.objects.filter( | ||
enterprise_customer=enterprise_customer_uuid, | ||
) | ||
|
||
# Retrieve the course enrollments for the learner | ||
enterprise_course_enrollments_for_learner = models.EnterpriseCourseEnrollment.objects.filter( | ||
enterprise_customer_user=enterprise_customer_user, | ||
) | ||
enrolled_course_ids_for_learner = enterprise_course_enrollments_for_learner.values_list('course_id', flat=True) | ||
|
||
already_enrolled = [] | ||
needs_enrollment_enrollable = [] | ||
needs_enrollment_not_enrollable = [] | ||
|
||
# Iterate through the default enrollment intentions and categorize them based | ||
# on the learner's enrollment status (already enrolled, needs enrollment, etc.) | ||
# and whether the course run is enrollable. | ||
for default_enrollment_intention in default_enrollment_intentions_for_customer: | ||
course_run_key = default_enrollment_intention.course_run_key | ||
is_course_run_enrollable = default_enrollment_intention.is_course_run_enrollable | ||
applicable_enterprise_catalog_uuids = default_enrollment_intention.applicable_enterprise_catalog_uuids | ||
|
||
if course_run_key in enrolled_course_ids_for_learner: | ||
# Learner is already enrolled in this course run | ||
already_enrolled.append(default_enrollment_intention) | ||
elif is_course_run_enrollable and applicable_enterprise_catalog_uuids: | ||
# Learner needs enrollment, the course run is enrollable, and there are applicable catalogs | ||
needs_enrollment_enrollable.append(default_enrollment_intention) | ||
else: | ||
# Learner needs enrollment, but the course run is not enrollable and/or there are no applicable catalogs | ||
needs_enrollment_not_enrollable.append(default_enrollment_intention) | ||
|
||
serializer_data = { | ||
'lms_user_id': user_for_learner_status.id, | ||
'user_email': user_for_learner_status.email, | ||
'enterprise_customer_uuid': enterprise_customer_uuid, | ||
} | ||
serializer_context = { | ||
'needs_enrollment': { | ||
'enrollable': needs_enrollment_enrollable, | ||
'not_enrollable': needs_enrollment_not_enrollable, | ||
}, | ||
'already_enrolled': already_enrolled, | ||
} | ||
serializer = serializers.DefaultEnterpriseEnrollmentIntentionLearnerStatusSerializer( | ||
data=serializer_data, | ||
context=serializer_context, | ||
) | ||
serializer.is_valid(raise_exception=True) | ||
return Response(serializer.data, status=status.HTTP_200_OK) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.