-
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: new mgmt command to validate default enrollment intentions
Introduced validate_default_enrollment_intentions.py to check that all DefaultEnterpriseEnrollmentIntention objects have a valid content_key which actually belongs to at least one of the related customer's catalogs. ENT-9941
- Loading branch information
Showing
2 changed files
with
150 additions
and
0 deletions.
There are no files selected for viewing
141 changes: 141 additions & 0 deletions
141
enterprise/management/commands/validate_default_enrollment_intentions.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,141 @@ | ||
""" | ||
Django management command to validate that DefaultEnterpriseEnrollmentIntention | ||
objects have enrollable content. | ||
""" | ||
import logging | ||
from datetime import datetime, timedelta | ||
|
||
from django.core.management import BaseCommand, CommandError | ||
from django.db.models import Max | ||
from django.db.models.functions import Greatest | ||
|
||
from enterprise.api_client.enterprise_catalog import EnterpriseCatalogApiClient | ||
from enterprise.models import EnterpriseCustomer | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class Command(BaseCommand): | ||
""" | ||
Enumerate the catalog filters and log information about how we might migrate them. | ||
""" | ||
|
||
def __init__(self, *args, **kwargs): | ||
self.enterprise_catalog_client = None | ||
self.delay_minutes = None | ||
super().__init__(*args, **kwargs) | ||
|
||
def add_arguments(self, parser): | ||
parser.add_argument( | ||
'--delay-minutes', | ||
dest='delay_minutes', | ||
required=False, | ||
type=int, | ||
default=30, | ||
help="How long after a customer's catalog has been updated are we allowed to evaluate the customer." | ||
) | ||
|
||
@property | ||
def latest_change_allowed(self): | ||
return datetime.now() - timedelta(minutes=self.delay_minutes) | ||
|
||
def handle_default_enrollment_intention(self, customer, intention): | ||
""" | ||
Check that the default enrollment intention's content_key is contained in any of the customer's catalogs. | ||
Returns: | ||
bool: True if the default enrollment intention is valid. | ||
""" | ||
content_metadata = self.enterprise_catalog_client.get_customer_content_metadata_content_identifier( | ||
customer.uuid, | ||
intention.content_key, | ||
) | ||
contained_in_customer_catalogs = bool(content_metadata) | ||
if contained_in_customer_catalogs: | ||
logger.info( | ||
f"handle_default_enrollment_intention(): Default enrollment intention {intention} " | ||
"is compatible with the customer's catalogs." | ||
) | ||
else: | ||
logger.error( | ||
f"handle_default_enrollment_intention(): Default enrollment intention {intention} " | ||
"is NOT compatible with the customer's catalogs." | ||
) | ||
return contained_in_customer_catalogs | ||
|
||
def handle_customer(self, customer): | ||
""" | ||
Try to evaluate an EnterpriseCustomer for any invalid DefaultEnterpriseEnrollmentIntention records. | ||
Returns: | ||
dict: A structured result object that indicates whether the | ||
customer was skipped, and which intentions are invalid. | ||
""" | ||
result = { | ||
'skipped': None, | ||
'invalid_intentions': None, | ||
} | ||
if customer.catalogs_modified_latest > self.latest_change_allowed: | ||
result['skipped'] = True | ||
logger.info( | ||
f"handle_customer(): SKIPPING Evaluating default enrollment intentions for customer {customer}." | ||
) | ||
return result | ||
result['skipped'] = False | ||
logger.info(f"handle_customer(): Evaluating default enrollment intentions for customer {customer}.") | ||
results = { | ||
intention: self.handle_default_enrollment_intention(customer, intention) | ||
for intention in customer.default_enrollment_intentions | ||
} | ||
result['invalid_intentions'] = [intention for intention, valid in results.items() if not valid] | ||
return result | ||
|
||
def handle(self, *args, **options): | ||
self.enterprise_catalog_client = EnterpriseCatalogApiClient() | ||
self.delay_minutes = options.get("delay_minutes") | ||
|
||
customers = EnterpriseCustomer.objects.annotate( | ||
catalogs_modified_latest=Greatest( | ||
Max("enterprise_customer_catalogs__modified"), | ||
Max("enterprise_customer_catalogs__enterprise_catalog_query__modified"), | ||
), | ||
).prefetch_related( | ||
"default_enrollment_intentions", | ||
) | ||
|
||
results = {customer: self.handle_customer(customer) for customer in customers} | ||
results_evaluated = {customer: result for customer, result in results.items() if not result['skipped']} | ||
results_failed = { | ||
customer: result | ||
for customer, result in results_evaluated.items() | ||
if result['invalid_intentions']} | ||
invalid_intentions = [ | ||
intention | ||
for intention in failed_result['invalid_intentions'] | ||
for failed_result in results_failed.values() | ||
] | ||
|
||
count_customers_total = len(results) | ||
count_customers_evaluated = len(results_evaluated) | ||
count_customers_skipped = count_customers_total - count_customers_evaluated | ||
count_customers_failed = len(results_failed) | ||
count_customers_passed = count_customers_evaluated - count_customers_failed | ||
|
||
logger.info( | ||
f"{count_customers_total} total customers found, " | ||
f"and {count_customers_evaluated}/{count_customers_total} customers were evaluated " | ||
f"({count_customers_skipped}/{count_customers_total} skipped)." | ||
) | ||
logger.info( | ||
f"Out of {count_customers_evaluated} total evaluated customers, " | ||
f"{count_customers_passed}/{count_customers_evaluated} customers passed validation " | ||
f"({count_customers_failed}/{count_customers_evaluated} failed)." | ||
) | ||
if count_customers_failed: | ||
logger.error("Summary of all {len(invalid_intentions)} invalid intentions: {invalid_intentions}") | ||
logger.error("FAILURE: Some default enrollment intentions were invalid.") | ||
raise CommandError( | ||
f"{len(invalid_intentions)} invalid default enrollment intentions found " | ||
f"across {count_customers_failed} customers." | ||
) | ||
logger.info("SUCCESS: All default enrollment intentions are valid!") | ||
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