feat: notify_credentials of changed overrides

We use the `notify_credentials` management command to keep certificate-
related data in the LMS and Credentials service in sync. We can run it
with specific arguments (user_ids, course_keys, etc.) when we notice a
data discrepancy; and it is run regularly by a Jenkins job with the
`--auto` flag every ~4 hours to keep things up-to-date.

Because we probably never want to notify credentials of of ALL the
GeneratedCertificates, the celery task must be given some arguments
to filter down to the relevant certificates. Running the management
command with the `--auto` flag (as the Jenkins job does) adds
`start_date` and `end_date` arguments of 4 hours ago and now,
respectively.

The handle_notify_credentials celery task then takes those arguments and
looks for any GeneratedCertificates that have been modified within the
given time range by checking the GeneratedCertificate modified_date.
It will send the current data for those certificates to credentials.

However, we also want to notify credentials about certificates that have
an associated CertificateDateOverride that has changed within that time
range: added, updated, or deleted. But changes to a
CertificateDateOverride won’t affect the GeneratedCertificate’s modified
date, and therefore wouldn’t be included in the list of certs cent to
credentials.

This commit adds a check for changed CertificateDateOverrides and
includes their associated GeneratedCertificates in the list of certs. We
use the CertificateDateOverride’s history model for this check so that
we can include certificates whose override was deleted.

MICROBA-1489
This commit is contained in:
oliviaruizknott
2021-09-08 15:56:32 -06:00
parent 48ad7effb1
commit dbfe2a3f2b
3 changed files with 121 additions and 2 deletions

View File

@@ -32,6 +32,7 @@ from lms.djangoapps.certificates.config import AUTO_CERTIFICATE_GENERATION as _A
from lms.djangoapps.certificates.data import CertificateStatuses
from lms.djangoapps.certificates.models import (
CertificateAllowlist,
CertificateDateOverride,
CertificateGenerationConfiguration,
CertificateGenerationCourseSetting,
CertificateInvalidation,
@@ -203,9 +204,53 @@ def get_recently_modified_certificates(course_keys=None, start_date=None, end_da
if user_ids:
cert_filter_args['user__id__in'] = user_ids
# Include certificates with a CertificateDateOverride modified within the
# given time range.
if start_date or end_date:
certs_with_modified_overrides = get_certs_with_modified_overrides(course_keys, start_date, end_date, user_ids)
return GeneratedCertificate.objects.filter(
**cert_filter_args
).union(
certs_with_modified_overrides
).order_by(
'modified_date'
)
return GeneratedCertificate.objects.filter(**cert_filter_args).order_by('modified_date')
def get_certs_with_modified_overrides(course_keys=None, start_date=None, end_date=None, user_ids=None):
"""
Returns a QuerySet of certificates with a CertificateDateOverride modified
within the given start_date and end_date. Uses the history table to catch
deleted overrides too.
"""
override_filter_args = {}
if start_date:
override_filter_args['history_date__gte'] = start_date
if end_date:
override_filter_args['history_date__lte'] = end_date
# Get the HistoricalCertificateDateOverrides that have entries within the
# given date range. We check the history table to catch deleted overrides
# along with those created / updated
overrides = CertificateDateOverride.history.filter(**override_filter_args)
# Get the associated GeneratedCertificate ids.
override_cert_ids = overrides.values_list('generated_certificate', flat=True)
# Build the args for the GeneratedCertificate query. First, filter by all
# certs identified in override_cert_ids; then by the other arguments passed,
# if present.
cert_filter_args = {'pk__in': override_cert_ids}
if course_keys:
cert_filter_args['course_id__in'] = course_keys
if user_ids:
cert_filter_args['user__id__in'] = user_ids
return GeneratedCertificate.objects.filter(**cert_filter_args)
def generate_certificate_task(user, course_key, generation_mode=None):
"""
Create a task to generate a certificate for this user in this course run, if the user is eligible and a certificate