-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
03ad938
commit d15a9c8
Showing
4 changed files
with
167 additions
and
0 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
156 changes: 156 additions & 0 deletions
156
license_manager/apps/subscriptions/management/commands/process_expired_licenses.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,156 @@ | ||
|
||
import logging | ||
|
||
from django.conf import settings | ||
from django.core.management.base import BaseCommand | ||
from django.core.paginator import Paginator | ||
from django.db.models import Exists, OuterRef | ||
|
||
from license_manager.apps.api_client.enterprise import EnterpriseApiClient | ||
from license_manager.apps.subscriptions.constants import ( | ||
ACTIVATED, | ||
ASSIGNED, | ||
EXPIRED_LICENSE_PROCESSED, | ||
) | ||
from license_manager.apps.subscriptions.models import ( | ||
CustomerAgreement, | ||
License, | ||
LicenseEvent, | ||
SubscriptionPlan, | ||
) | ||
from license_manager.apps.subscriptions.utils import localized_utcnow | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class Command(BaseCommand): | ||
help = ( | ||
'Process expired licenses.' | ||
) | ||
|
||
def add_arguments(self, parser): | ||
""" | ||
Entry point to add arguments. | ||
""" | ||
parser.add_argument( | ||
'--dry-run', | ||
action='store_true', | ||
dest='dry_run', | ||
default=False, | ||
help='Dry Run, print log messages without firing the segment event.', | ||
) | ||
|
||
def expired_licenses(self, enterprise_customer_uuid): | ||
""" | ||
Get expired licenses. | ||
""" | ||
now = localized_utcnow() | ||
expired_subscription_plan_uuids = [] | ||
|
||
customer_agreement = CustomerAgreement.objects.get(enterprise_customer_uuid=enterprise_customer_uuid) | ||
|
||
expired_subscription_plans = list( | ||
SubscriptionPlan.objects.filter( | ||
customer_agreement=customer_agreement, | ||
expiration_date__lt=now, | ||
).select_related( | ||
'customer_agreement' | ||
).prefetch_related( | ||
'licenses' | ||
) | ||
) | ||
|
||
for expired_subscription_plan in expired_subscription_plans: | ||
# exclude subscription plan if there is a renewal | ||
if expired_subscription_plan.get_renewal(): | ||
continue | ||
|
||
expired_subscription_plan_uuids.append(expired_subscription_plan.uuid) | ||
|
||
# include prior renewals | ||
for prior_renewal in expired_subscription_plan.prior_renewals: | ||
expired_subscription_plan_uuids.append(prior_renewal.prior_subscription_plan.uuid) | ||
|
||
queryset = License.objects.filter( | ||
status__in=[ASSIGNED, ACTIVATED], | ||
renewed_to=None, | ||
subscription_plan__uuid__in=expired_subscription_plan_uuids, | ||
).select_related( | ||
'subscription_plan', | ||
).values('uuid', 'lms_user_id', 'user_email') | ||
|
||
# Subquery to check for the existence of `EXPIRED_LICENSE_PROCESSED` | ||
event_exists_subquery = LicenseEvent.objects.filter( | ||
license=OuterRef('pk'), | ||
event_name=EXPIRED_LICENSE_PROCESSED | ||
).values('pk') | ||
|
||
# Exclude previously processed licenses. | ||
queryset = queryset.exclude(Exists(event_exists_subquery)) | ||
|
||
return queryset | ||
|
||
def handle(self, *args, **options): | ||
""" | ||
Process expired licenses and un-link learners. | ||
""" | ||
unlink = not options['dry_run'] | ||
|
||
log_prefix = '[PROCESS_EXPIRED_LICENSES]' | ||
if not unlink: | ||
log_prefix = '[DRY RUN]' | ||
|
||
logger.info('%s Command started.', log_prefix) | ||
|
||
enterprise_customer_uuids = settings.CUSTOMERS_WITH_EXPIRED_LICENSES_UNLINKING_ENABLED | ||
for enterprise_customer_uuid in enterprise_customer_uuids: | ||
logger.info('%s Processing started for licenses. Enterprise: [%s]', log_prefix, enterprise_customer_uuid) | ||
self.process_expired_licenses(enterprise_customer_uuid, log_prefix, unlink) | ||
logger.info('%s Processing completed for licenses. Enterprise: [%s]', log_prefix, enterprise_customer_uuid) | ||
|
||
logger.info('%s Command completed.', log_prefix) | ||
|
||
def process_expired_licenses(self, enterprise_customer_uuid, log_prefix, unlink): | ||
""" | ||
Process expired licenses and un-link learners. | ||
""" | ||
expired_licenses = self.expired_licenses(enterprise_customer_uuid) | ||
|
||
if not expired_licenses: | ||
logger.info( | ||
'%s No expired licenses were found for enterprise: [%s].', | ||
log_prefix, enterprise_customer_uuid | ||
) | ||
return | ||
|
||
paginator = Paginator(expired_licenses, 100) | ||
for page_number in paginator.page_range: | ||
licenses = paginator.page(page_number) | ||
|
||
license_uuids = [license.get('uuid') for license in licenses] | ||
user_emails = [license.get('user_email') for license in licenses] | ||
|
||
logger.info( | ||
"%s learners unlinked for licenses. Enterprise: [%s], LicenseUUIDs: [%s].", | ||
log_prefix, | ||
enterprise_customer_uuid, | ||
license_uuids | ||
) | ||
|
||
if unlink: | ||
# EnterpriseApiClient.bulk_unlink_enterprise_users( | ||
# enterprise_customer_uuid, | ||
# { | ||
# 'user_emails': user_emails, | ||
# 'is_relinkable': False | ||
# }, | ||
|
||
# ) | ||
|
||
# Create license events for unlinked licenses to avoid processing them again. | ||
unlinked_license_events = [ | ||
LicenseEvent(license_id=license_uuid, event_name=EXPIRED_LICENSE_PROCESSED) | ||
for license_uuid in license_uuids | ||
] | ||
LicenseEvent.objects.bulk_create(unlinked_license_events, batch_size=100) |
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