Skip to content

Commit

Permalink
feat: create DefaultEnterpriseEnrollmentRealization during bulk enrol…
Browse files Browse the repository at this point in the history
…lment
  • Loading branch information
adamstankiewicz committed Oct 30, 2024
1 parent f3092a7 commit dbdcb76
Show file tree
Hide file tree
Showing 8 changed files with 346 additions and 75 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ Unreleased
----------
* nothing unreleased

[4.31.0]
--------
* feat: create DefaultEnterpriseEnrollmentRealization objects in bulk enrollment API, when applicable.
* fix: Alter the realized_enrollment field in DefaultEnterpriseEnrollmentRealization to be a OneToOneField.
* fix: rename metadata field in DefaultEnterpriseEnrollmentIntentionLearnerStatusSerializer.

[4.30.1]
--------
* fix: serialize best_mode_for_course_run field in DefaultEnterpriseEnrollmentIntentionSerializer.
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.30.1"
__version__ = "4.31.0"
2 changes: 1 addition & 1 deletion enterprise/api/v1/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2064,7 +2064,7 @@ def get_metadata(self, obj): # pylint: disable=unused-argument
enrolled by the learner.
"""
return {
'total_default_enterprise_course_enrollments': self.total_default_enrollment_intention_count(),
'total_default_enterprise_enrollment_intentions': self.total_default_enrollment_intention_count(),
'total_needs_enrollment': self.needs_enrollment_counts(),
'total_already_enrolled': self.already_enrolled_count(),
}
8 changes: 5 additions & 3 deletions enterprise/api/v1/views/default_enterprise_enrollments.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def base_queryset(self):
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()`.
"""
return models.DefaultEnterpriseEnrollmentIntention.objects.filter(
return models.DefaultEnterpriseEnrollmentIntention.available_objects.filter(
enterprise_customer=self.requested_enterprise_customer_uuid,
)

Expand Down Expand Up @@ -129,8 +129,10 @@ def learner_status(self, request):
)

# Retrieve configured default enrollment intentions for the enterprise customer
default_enrollment_intentions_for_customer = models.DefaultEnterpriseEnrollmentIntention.objects.filter(
enterprise_customer=enterprise_customer_uuid,
default_enrollment_intentions_for_customer = (
models.DefaultEnterpriseEnrollmentIntention.available_objects.filter(
enterprise_customer=enterprise_customer_uuid,
)
)

# Retrieve the course enrollments for the learner
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.16 on 2024-10-29 21:30

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('enterprise', '0227_alter_defaultenterpriseenrollmentintention_content_type_and_more'),
]

operations = [
migrations.AlterField(
model_name='defaultenterpriseenrollmentrealization',
name='realized_enrollment',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='enterprise.enterprisecourseenrollment'),
),
]
13 changes: 12 additions & 1 deletion enterprise/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2124,6 +2124,17 @@ def learner_credit_fulfillment(self):
associated_fulfillment = None
return associated_fulfillment

@property
def default_enterprise_enrollment_realization(self):
"""
Returns the default realization for the enterprise enrollment.
"""
try:
associated_realization = self.defaultenterpriseenrollmentrealization_realized_enrollment # pylint: disable=no-member
except DefaultEnterpriseEnrollmentRealization.DoesNotExist:
associated_realization = None
return associated_realization

@property
def fulfillments(self):
"""
Expand Down Expand Up @@ -2708,7 +2719,7 @@ class DefaultEnterpriseEnrollmentRealization(TimeStampedModel):
DefaultEnterpriseEnrollmentIntention,
on_delete=models.CASCADE,
)
realized_enrollment = models.ForeignKey(
realized_enrollment = models.OneToOneField(
EnterpriseCourseEnrollment,
on_delete=models.CASCADE,
)
Expand Down
88 changes: 79 additions & 9 deletions enterprise/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,20 @@ def pending_enterprise_customer_admin_user_model():
return apps.get_model('enterprise', 'PendingEnterpriseCustomerAdminUser')


def default_enterprise_enrollment_intention_model():
"""
Returns the ``DefaultEnterpriseEnrollmentIntention`` class.
"""
return apps.get_model('enterprise', 'DefaultEnterpriseEnrollmentIntention')


def default_enterprise_enrollment_realization_model():
"""
Returns the ``DefaultEnterpriseEnrollmentRealization`` class.
"""
return apps.get_model('enterprise', 'DefaultEnterpriseEnrollmentRealization')


def get_enterprise_customer(uuid):
"""
Get the ``EnterpriseCustomer`` instance associated with ``uuid``.
Expand Down Expand Up @@ -1816,14 +1830,15 @@ def enroll_user(enterprise_customer, user, course_mode, *course_ids, **kwargs):


def customer_admin_enroll_user_with_status(
enterprise_customer,
user,
course_mode,
course_id,
enrollment_source=None,
license_uuid=None,
transaction_id=None,
force_enrollment=False,
enterprise_customer,
user,
course_mode,
course_id,
enrollment_source=None,
license_uuid=None,
transaction_id=None,
force_enrollment=False,
is_default_auto_enrollment=False,
):
"""
For use with bulk enrollment, or any use case of admin enrolling a user
Expand Down Expand Up @@ -1910,6 +1925,12 @@ def customer_admin_enroll_user_with_status(
licensed_enrollment_obj.uuid, license_uuid,
)
enterprise_fulfillment_source_uuid = licensed_enrollment_obj.uuid

if is_default_auto_enrollment:
# Check for default enterprise enrollment intentions for enterprise customer associated
# with the enrollment, and create a default enterprise enrollment realization if necessary.
check_default_enterprise_enrollment_intentions_and_create_realization(enterprise_course_enrollment=obj)

if created:
# Note: this tracking event only caters to bulk enrollment right now.
track_enrollment(PATHWAY_CUSTOMER_ADMIN_ENROLLMENT, user.id, course_id)
Expand All @@ -1920,6 +1941,50 @@ def customer_admin_enroll_user_with_status(
return succeeded, created, enterprise_fulfillment_source_uuid


def check_default_enterprise_enrollment_intentions_and_create_realization(enterprise_course_enrollment):
"""
Check if there are any default enrollment intentions for the given enterprise customer,
and create corresponding realizations if they do not exist.
"""
enterprise_customer_uuid = enterprise_course_enrollment.enterprise_customer_user.enterprise_customer.uuid
default_enrollment_intentions_for_customer = (
default_enterprise_enrollment_intention_model().available_objects.filter(
enterprise_customer=enterprise_customer_uuid,
)
)
default_enterprise_enrollment_intention = next(
(
intention for intention in default_enrollment_intentions_for_customer
if intention.course_run_key == enterprise_course_enrollment.course_id
),
None
)

if not default_enterprise_enrollment_intention:
LOGGER.info(
"No default enrollment intention found for enterprise course enrollment %s",
enterprise_course_enrollment.id
)
return None

default_enterprise_enrollment_realization, created = (
default_enterprise_enrollment_realization_model().objects.get_or_create(
intended_enrollment=default_enterprise_enrollment_intention,
realized_enrollment=enterprise_course_enrollment,
)
)

if created:
LOGGER.info(
"Created default enterprise enrollment realization for default enrollment "
"intention %s and enterprise course enrollment %s",
default_enterprise_enrollment_intention.uuid,
enterprise_course_enrollment.id
)

return default_enterprise_enrollment_intention, default_enterprise_enrollment_realization


def customer_admin_enroll_user(enterprise_customer, user, course_mode, course_id, enrollment_source=None):
"""
For use with bulk enrollment, or any use case of admin enrolling a user
Expand Down Expand Up @@ -2006,10 +2071,12 @@ def enroll_subsidy_users_in_courses(enterprise_customer, subsidy_users_info, dis
* '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).
* 'is_default_auto_enrollment' (bool, optional): If True, a related default enterprise enrollment
realization will be created (default False).
Example::
licensed_users_info: [
subsidy_users_info: [
{
'email': '[email protected]',
'course_run_key': 'course-v1:edX+DemoX+Demo_Course',
Expand All @@ -2029,6 +2096,7 @@ def enroll_subsidy_users_in_courses(enterprise_customer, subsidy_users_info, dis
'transaction_id': '3a5312d722564db0a16e3d81f53a3718',
},
]
discount: (int) the discount offered to the learner for their enrollment. Subscription based enrollments
default to 100
Expand Down Expand Up @@ -2057,6 +2125,7 @@ def enroll_subsidy_users_in_courses(enterprise_customer, subsidy_users_info, dis
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)
is_default_auto_enrollment = subsidy_user_info.get('is_default_auto_enrollment', False)

if user_id and user_email:
user = User.objects.filter(id=subsidy_user_info['user_id']).first()
Expand Down Expand Up @@ -2088,6 +2157,7 @@ def enroll_subsidy_users_in_courses(enterprise_customer, subsidy_users_info, dis
license_uuid,
transaction_id,
force_enrollment=force_enrollment,
is_default_auto_enrollment=is_default_auto_enrollment,
)
if succeeded:
success_dict = {
Expand Down
Loading

0 comments on commit dbdcb76

Please sign in to comment.