* feat: removing visible_date-to-creds updates per-cert The credentials IDA now relies on the course certificate configuration and (if present) `certificate_available_date` for displayability. We no longer need to send `visible_date` updates for every awarded certificate when a course overview changes.
166 lines
7.4 KiB
Python
166 lines
7.4 KiB
Python
"""
|
|
This module contains signals / handlers related to programs.
|
|
"""
|
|
|
|
import logging
|
|
|
|
from django.dispatch import receiver
|
|
|
|
from openedx.core.djangoapps.content.course_overviews.signals import COURSE_PACING_CHANGED
|
|
from openedx.core.djangoapps.credentials.api import is_credentials_enabled
|
|
from openedx.core.djangoapps.credentials.helpers import is_learner_records_enabled_for_org
|
|
from openedx.core.djangoapps.signals.signals import (
|
|
COURSE_CERT_AWARDED,
|
|
COURSE_CERT_CHANGED,
|
|
COURSE_CERT_DATE_CHANGE,
|
|
COURSE_CERT_REVOKED,
|
|
)
|
|
|
|
LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
@receiver(COURSE_CERT_AWARDED)
|
|
def handle_course_cert_awarded(sender, user, course_key, mode, status, **kwargs): # pylint: disable=unused-argument
|
|
"""
|
|
If use of the Credentials IDA is enabled and a learner is awarded a course certificate, schedule a celery task to
|
|
determine if the learner is also eligible to be awarded any program certificates.
|
|
|
|
Args:
|
|
sender: class of the object instance that sent this signal
|
|
user(User): The user to whom a course certificate was awarded
|
|
course_key(CourseLocator): The course run key for which the course certificate was awarded
|
|
mode(str): The "mode" of the course (e.g. Audit, Honor, Verified, etc.)
|
|
status(str): The status of the course certificate that was awarded (e.g. "downloadable")
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
if not is_credentials_enabled():
|
|
return
|
|
|
|
LOGGER.debug(f"Handling COURSE_CERT_AWARDED: user={user}, course_key={course_key}, mode={mode}, status={status}")
|
|
# import here, because signal is registered at startup, but items in tasks are not yet able to be loaded
|
|
from openedx.core.djangoapps.programs.tasks import award_program_certificates
|
|
|
|
award_program_certificates.delay(user.username)
|
|
|
|
|
|
@receiver(COURSE_CERT_CHANGED)
|
|
def handle_course_cert_changed(sender, user, course_key, mode, status, **kwargs): # pylint: disable=unused-argument
|
|
"""
|
|
When the system updates a course certificate, enqueue a celery task responsible for syncing this change in the
|
|
Credentials IDA
|
|
|
|
***** Important *****
|
|
While the current name of the enqueue'd task is `award_course_certificate` it is *actually* responsible for both
|
|
awarding and revocation of course certificates in Credentials.
|
|
*********************
|
|
|
|
Args:
|
|
sender: class of the object instance that sent this signal
|
|
user(User): The user to whom a course certificate was awarded
|
|
course_key(CourseLocator): The course run key for which the course certificate was awarded
|
|
mode(str): The "mode" of the course (e.g. Audit, Honor, Verified, etc.)
|
|
status(str): The status of the course certificate that was awarded (e.g. "downloadable")
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
verbose = kwargs.get("verbose", False)
|
|
if verbose:
|
|
LOGGER.info(
|
|
f"Starting handle_course_cert_changed with params: sender [{sender}], user [{user}], course_key "
|
|
f"[{course_key}], mode [{mode}], status [{status}], kwargs [{kwargs}]"
|
|
)
|
|
|
|
if not is_credentials_enabled():
|
|
return
|
|
|
|
# Avoid scheduling new tasks if learner records are disabled for this site (right now, course certs are only
|
|
# used for learner records -- when that changes, we can remove this bit and always send course certs).
|
|
if not is_learner_records_enabled_for_org(course_key.org):
|
|
LOGGER.warning(f"Skipping send cert: the Learner Record feature is disabled for org [{course_key.org}]")
|
|
return
|
|
|
|
LOGGER.debug(f"Handling COURSE_CERT_CHANGED: user={user}, course_key={course_key}, mode={mode}, status={status}")
|
|
# import here, because signal is registered at startup, but items in tasks are not yet able to be loaded
|
|
from openedx.core.djangoapps.programs.tasks import award_course_certificate
|
|
|
|
award_course_certificate.delay(user.username, str(course_key))
|
|
|
|
|
|
@receiver(COURSE_CERT_REVOKED)
|
|
def handle_course_cert_revoked(sender, user, course_key, mode, status, **kwargs): # pylint: disable=unused-argument
|
|
"""
|
|
If use of the Credentials IDA is enabled and a learner has a course certificate revoked, schedule a celery task
|
|
to determine if there are any program certificates that must be revoked too.
|
|
|
|
Args:
|
|
sender: class of the object instance that sent this signal
|
|
user(User): The user to whom a course certificate was revoked
|
|
course_key(CourseLocator): The course run key for which the course certificate was revoked
|
|
mode(str): The "mode" of the course (e.g. "audit", "honor", "verified", etc.)
|
|
status(str): The status of the course certificate that was revoked (e.g. "revoked")
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
if not is_credentials_enabled():
|
|
return
|
|
|
|
LOGGER.info(f"Handling COURSE_CERT_REVOKED: user={user}, course_key={course_key}, mode={mode}, status={status}")
|
|
# import here, because signal is registered at startup, but items in tasks are not yet able to be loaded
|
|
from openedx.core.djangoapps.programs.tasks import revoke_program_certificates
|
|
|
|
revoke_program_certificates.delay(user.username, str(course_key))
|
|
|
|
|
|
@receiver(COURSE_CERT_DATE_CHANGE, dispatch_uid="course_certificate_date_change_handler")
|
|
def handle_course_cert_date_change(sender, course_key, **kwargs): # pylint: disable=unused-argument
|
|
"""
|
|
When a course run's configuration has been updated, and the system has detected an update related to the display
|
|
behavior or availability date of the certificates issued in that course, we should enqueue celery tasks responsible
|
|
for:
|
|
- updating the certificate available date of the course run's course certificate configuration in Credentials
|
|
|
|
Args:
|
|
sender: class of the object instance that sent this signal
|
|
course_key(CourseLocator): The course run key of the course run which was updated
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
if not is_credentials_enabled():
|
|
return
|
|
|
|
LOGGER.info(f"Handling COURSE_CERT_DATE_CHANGE for course {course_key}")
|
|
# import here, because signal is registered at startup, but items in tasks are not yet loaded
|
|
from openedx.core.djangoapps.programs.tasks import update_certificate_available_date_on_course_update
|
|
|
|
update_certificate_available_date_on_course_update.delay(str(course_key))
|
|
|
|
|
|
@receiver(COURSE_PACING_CHANGED, dispatch_uid="update_credentials_on_pacing_change")
|
|
def handle_course_pacing_change(sender, updated_course_overview, **kwargs): # pylint: disable=unused-argument
|
|
"""
|
|
If the pacing of a course run has been updated, we should enqueue the tasks responsible for updating the certificate
|
|
available date (CAD) stored in the Credentials IDA's internal records. This ensures that we are correctly managing
|
|
the visibiltiy of certificates on learners' program records.
|
|
|
|
Args:
|
|
sender: class of the object instance that sent this signal
|
|
updated_course_overview(CourseOverview): The course overview of the course run which was just updated
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
if not is_credentials_enabled():
|
|
return
|
|
|
|
course_id = str(updated_course_overview.id)
|
|
LOGGER.info(f"Handling COURSE_PACING_CHANGED for course {course_id}")
|
|
# import here, because signal is registered at startup, but items in tasks are not yet loaded
|
|
from openedx.core.djangoapps.programs.tasks import update_certificate_available_date_on_course_update
|
|
|
|
update_certificate_available_date_on_course_update.delay(course_id)
|