Skip to content

Commit

Permalink
Merge branch 'master' into qasim/migrations/postgresql
Browse files Browse the repository at this point in the history
  • Loading branch information
e0d authored Nov 20, 2024
2 parents 273f50c + 546f6fc commit 8c0641b
Show file tree
Hide file tree
Showing 28 changed files with 1,644 additions and 1,169 deletions.
30 changes: 30 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,36 @@ Unreleased
----------
* nothing unreleased

[4.33.0]
--------
* feat: Updated pagination for reporting configurations.

[4.32.3]
--------
* chore: Update python requirements.

[4.32.2]
--------
* fix: ensure admin_users in EnterpriseCustomerUserViewSet is correct.

[4.32.1]
--------
* feat: enable search filter on learner data transmission audit admin views for all integrated channels.

[4.32.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.31.2]
--------
* feat: add test cases for newly added unlink_self endpoint.

[4.31.1]
--------
* fix: fixed query for monthly_impact_report command.

[4.31.0]
--------
* feat: add new endpoint to unlink the logged in user.
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.31.0"
__version__ = "4.33.0"
17 changes: 15 additions & 2 deletions enterprise/api/v1/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ class Meta:
)

user = UserSerializer()
enterprise_customer = EnterpriseCustomerSerializer()
enterprise_customer = serializers.SerializerMethodField()
data_sharing_consent_records = serializers.SerializerMethodField()
groups = serializers.SerializerMethodField()
role_assignments = serializers.SerializerMethodField()
Expand Down Expand Up @@ -756,6 +756,15 @@ def __init__(self, instance=None, data=empty, **kwargs):
)
self.role_assignments_by_ecu_id = role_assignments_by_ecu_id

def get_enterprise_customer(self, obj):
"""
Return serialization of EnterpriseCustomer associated with the EnterpriseCustomerUser.
"""
return EnterpriseCustomerSerializer(
instance=obj.enterprise_customer,
context=self.context
).data

def get_data_sharing_consent_records(self, obj):
"""
Return serialization of EnterpriseCustomerUser.data_sharing_consent_records property.
Expand Down Expand Up @@ -1483,6 +1492,10 @@ class EnrollmentsInfoSerializer(serializers.Serializer):
required=False,
help_text='Enroll even if enrollment deadline is expired.',
)
is_default_auto_enrollment = serializers.BooleanField(
required=False,
help_text='Auto-enrollment for default enterprise enrollment intention.',
)

def create(self, validated_data):
return validated_data
Expand Down Expand Up @@ -2064,7 +2077,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
10 changes: 10 additions & 0 deletions enterprise/api/v1/views/enterprise_customer_reporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
from rest_framework import permissions, status
from rest_framework.authentication import SessionAuthentication
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from rest_framework.status import HTTP_200_OK, HTTP_404_NOT_FOUND
from rest_framework.views import APIView
Expand All @@ -17,6 +18,14 @@
from enterprise.utils import get_enterprise_customer


class ExpandDefaultPageSize(PageNumberPagination):
"""
Expands page size for the API.
Used to populate large reporting configurations.
"""
page_size = 100


class EnterpriseCustomerReportingConfigurationViewSet(EnterpriseReadWriteModelViewSet):
"""
API views for the ``enterprise-customer-reporting`` API endpoint.
Expand All @@ -26,6 +35,7 @@ class EnterpriseCustomerReportingConfigurationViewSet(EnterpriseReadWriteModelVi
serializer_class = serializers.EnterpriseCustomerReportingConfigurationSerializer
lookup_field = 'uuid'
permission_classes = [permissions.IsAuthenticated]
pagination_class = ExpandDefaultPageSize

USER_ID_FILTER = 'enterprise_customer__enterprise_customer_users__user_id'
FIELDS = (
Expand Down
8 changes: 4 additions & 4 deletions enterprise/management/commands/monthly_impact_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -865,17 +865,17 @@
FROM (
SELECT
acc.integration_enterprise_uuid__c as uuid,
MAX(opp.contract_end_date__c) latest_contract_end_date
acc.integration_enterprise_uuid_c as uuid,
MAX(opp.contract_end_date_c) latest_contract_end_date
FROM
salesforce_prod_pii.opportunity as opp
LEFT JOIN
salesforce_prod_pii._account as acc
ON
opp.accountid = acc.id
opp.account_id = acc.id
WHERE
-- only closed won contracts
opp.stagename = 'Closed Won'
opp.stage_name = 'Closed Won'
GROUP BY
1
HAVING
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
8 changes: 8 additions & 0 deletions integrated_channels/blackboard/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ class BlackboardLearnerDataTransmissionAuditAdmin(BaseLearnerDataTransmissionAud
"api_record",
)

search_fields = (
"blackboard_user_email",
"enterprise_course_enrollment_id",
"course_id",
"content_title",
"friendly_status_message"
)

list_per_page = 1000

class Meta:
Expand Down
8 changes: 8 additions & 0 deletions integrated_channels/canvas/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ class CanvasLearnerDataTransmissionAuditAdmin(BaseLearnerDataTransmissionAuditAd
"api_record",
)

search_fields = (
"canvas_user_email",
"enterprise_course_enrollment_id",
"course_id",
"content_title",
"friendly_status_message"
)

list_per_page = 1000

class Meta:
Expand Down
Loading

0 comments on commit 8c0641b

Please sign in to comment.