Skip to content

Commit

Permalink
Merge pull request #2048 from openedx/pwnage101/ENT-8525
Browse files Browse the repository at this point in the history
feat: pass force_enrollment when bulk enrolling learners
  • Loading branch information
pwnage101 authored Mar 18, 2024
2 parents 02c8d10 + 3f5667a commit 41c7614
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 5 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.11]
---------
* feat: pass force_enrollment when bulk enrolling learners

[4.13.10]
---------
* fix: remove filter to debug failing transmissions
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.10"
__version__ = "4.13.11"
12 changes: 9 additions & 3 deletions enterprise/api/v1/views/enterprise_customer.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,15 @@ def enroll_learners_in_courses(self, request, pk):
Parameters:
enrollments_info (list of dicts): an array of dictionaries, each containing the necessary information to
create an enrollment based on a subsidy for a user in a specified course. Each dictionary must contain
a user email (or user_id), a course run key, and either a UUID of the license that the learner is using
to enroll with or a transaction ID related to Executive Education the enrollment. `licenses_info` is
also accepted as a body param name.
the following keys:
* 'user_id' OR 'email': Either unique identifier describing the user to enroll.
* 'course_run_key': The course to enroll into.
* 'license_uuid' OR 'transaction_id': ID of either accepted form of subsidy. `license_uuid` refers to
subscription licenses, and `transaction_id` refers to Learner Credit transactions.
* 'force_enrollment' (bool, optional): Enroll even if enrollment deadline is expired (default False).
`licenses_info` is also accepted as a body param name.
Example::
Expand Down
7 changes: 6 additions & 1 deletion enterprise/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1807,6 +1807,7 @@ def customer_admin_enroll_user_with_status(
enrollment_source=None,
license_uuid=None,
transaction_id=None,
force_enrollment=False,
):
"""
For use with bulk enrollment, or any use case of admin enrolling a user
Expand Down Expand Up @@ -1848,6 +1849,7 @@ def customer_admin_enroll_user_with_status(
course_mode,
is_active=True,
enterprise_uuid=enterprise_customer.uuid,
force_enrollment=force_enrollment,
)
succeeded = True
LOGGER.info("Successfully enrolled user %s in course %s", user.id, course_id)
Expand Down Expand Up @@ -1987,6 +1989,7 @@ def enroll_subsidy_users_in_courses(enterprise_customer, subsidy_users_info, dis
* 'course_run_key': The course to enroll into.
* 'course_mode': The course mode.
* 'license_uuid' OR 'transaction_id': ID of either accepted form of subsidy.
* 'force_enrollment' (bool, optional): Enroll user even enrollment deadline is expired (default False).
Example::
Expand Down Expand Up @@ -2037,6 +2040,7 @@ def enroll_subsidy_users_in_courses(enterprise_customer, subsidy_users_info, dis
license_uuid = subsidy_user_info.get('license_uuid')
transaction_id = subsidy_user_info.get('transaction_id')
activation_link = subsidy_user_info.get('activation_link')
force_enrollment = subsidy_user_info.get('force_enrollment', False)

if user_id and user_email:
user = User.objects.filter(id=subsidy_user_info['user_id']).first()
Expand Down Expand Up @@ -2066,7 +2070,8 @@ def enroll_subsidy_users_in_courses(enterprise_customer, subsidy_users_info, dis
course_run_key,
enrollment_source,
license_uuid,
transaction_id
transaction_id,
force_enrollment=force_enrollment,
)
if succeeded:
success_dict = {
Expand Down
114 changes: 114 additions & 0 deletions tests/test_enterprise/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import unittest
from datetime import timedelta
from unittest import mock
from unittest.mock import call
from urllib.parse import quote, urlencode

import ddt
Expand Down Expand Up @@ -325,6 +326,119 @@ def test_enroll_subsidy_users_in_courses_with_user_id_succeeds(
)
self.assertEqual(len(EnterpriseCourseEnrollment.objects.all()), 2)

@mock.patch('enterprise.utils.lms_update_or_create_enrollment')
def test_enroll_subsidy_users_in_courses_with_force_enrollment(
self,
mock_update_or_create_enrollment,
):
"""
"""
self.create_user()
another_user_1 = factories.UserFactory(is_active=True)
another_user_2 = factories.UserFactory(is_active=True)
ent_customer = factories.EnterpriseCustomerFactory(
uuid=FAKE_UUIDS[0],
name="test_enterprise"
)
licensed_users_info = [
{
# Should succeed with force_enrollment passed as False under the hood.
'user_id': self.user.id,
'course_run_key': 'course-key-1',
'course_mode': 'verified',
'license_uuid': '5b77bdbade7b4fcb838f8111b68e18ae',
},
{
# Should also succeed with force_enrollment passed as False.
'user_id': another_user_1.id,
'course_run_key': 'course-key-2',
'course_mode': 'verified',
'license_uuid': '5b77bdbade7b4fcb838f8111b68e18ae',
'force_enrollment': False,
},
{
# Should succeed with force_enrollment passed as True.
'user_id': another_user_2.id,
'course_run_key': 'course-key-3',
'course_mode': 'verified',
'license_uuid': '5b77bdbade7b4fcb838f8111b68e18ae',
'force_enrollment': True,
},
]

mock_update_or_create_enrollment.return_value = True

result = enroll_subsidy_users_in_courses(ent_customer, licensed_users_info)
self.assertEqual(
{
'pending': [],
'successes': [
{
'user_id': self.user.id,
'email': self.user.email,
'course_run_key': 'course-key-1',
'user': self.user,
'created': True,
'activation_link': None,
'enterprise_fulfillment_source_uuid': EnterpriseCourseEnrollment.objects.filter(
enterprise_customer_user__user_id=self.user.id
).first().licensedenterprisecourseenrollment_enrollment_fulfillment.uuid,
},
{
'user_id': another_user_1.id,
'email': another_user_1.email,
'course_run_key': 'course-key-2',
'user': another_user_1,
'created': True,
'activation_link': None,
'enterprise_fulfillment_source_uuid': EnterpriseCourseEnrollment.objects.filter(
enterprise_customer_user__user_id=another_user_1.id
).first().licensedenterprisecourseenrollment_enrollment_fulfillment.uuid,
},
{
'user_id': another_user_2.id,
'email': another_user_2.email,
'course_run_key': 'course-key-3',
'user': another_user_2,
'created': True,
'activation_link': None,
'enterprise_fulfillment_source_uuid': EnterpriseCourseEnrollment.objects.filter(
enterprise_customer_user__user_id=another_user_2.id
).first().licensedenterprisecourseenrollment_enrollment_fulfillment.uuid,
},
],
'failures': [],
},
result
)
self.assertEqual(len(EnterpriseCourseEnrollment.objects.all()), 3)
assert mock_update_or_create_enrollment.mock_calls == [
call(
self.user.username,
'course-key-1',
'verified',
is_active=True,
enterprise_uuid=ent_customer.uuid,
force_enrollment=False,
),
call(
another_user_1.username,
'course-key-2',
'verified',
is_active=True,
enterprise_uuid=ent_customer.uuid,
force_enrollment=False,
),
call(
another_user_2.username,
'course-key-3',
'verified',
is_active=True,
enterprise_uuid=ent_customer.uuid,
force_enrollment=True,
),
]

@mock.patch('enterprise.utils.lms_update_or_create_enrollment')
def test_enroll_subsidy_users_in_courses_user_identifier_failures(
self,
Expand Down

0 comments on commit 41c7614

Please sign in to comment.