This reverts commit a67b9f70a16a0f16a842aad84754b245a2480b5f, reinstating commit cf78660ed35712f9bb7c112f70411179070d7382. The original commit was reverted because I thought I found bugs in it while verifying it on Stage, but it turns out that it was simply misconfigured Stage data that causing errors. The original commit's message has has been copied below: This commit completes the program_enrollments LMS app Python API for the time being. It does the following: * Add bulk-lookup of users by external key in api/reading.py * Add bulk-writing of program enrollments in api/writing.py * Move grade-reading to api/grades.py * Refactor api/linking.py to use api/writing.py * Refactor signals.py to use api/linking.py * Update rest_api/v1/views.py to utilize all these changes * Update linking management command and support tool to use API * Remove outdated tests from test_models.py * Misc. cleanup EDUCATOR-4321
98 lines
3.4 KiB
Python
98 lines
3.4 KiB
Python
"""
|
|
Signal handlers for program enrollments
|
|
"""
|
|
from __future__ import absolute_import, unicode_literals
|
|
|
|
import logging
|
|
|
|
from django.db.models.signals import post_save
|
|
from django.dispatch import receiver
|
|
from social_django.models import UserSocialAuth
|
|
|
|
from openedx.core.djangoapps.catalog.utils import get_programs
|
|
from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_LMS_MISC
|
|
from third_party_auth.models import SAMLProviderConfig
|
|
|
|
from .api import fetch_program_enrollments_by_student, link_program_enrollment_to_lms_user
|
|
from .models import ProgramEnrollment
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@receiver(USER_RETIRE_LMS_MISC)
|
|
def _listen_for_lms_retire(sender, **kwargs): # pylint: disable=unused-argument
|
|
"""
|
|
Listener for the USER_RETIRE_LMS_MISC signal, does user retirement
|
|
"""
|
|
user = kwargs.get('user')
|
|
ProgramEnrollment.retire_user(user.id)
|
|
|
|
|
|
@receiver(post_save, sender=UserSocialAuth)
|
|
def listen_for_social_auth_creation(sender, instance, created, **kwargs): # pylint: disable=unused-argument
|
|
"""
|
|
Post-save signal that will attempt to link a social auth entry with waiting enrollments
|
|
"""
|
|
if not created:
|
|
return
|
|
|
|
try:
|
|
matriculate_learner(instance.user, instance.uid)
|
|
except Exception as e:
|
|
logger.warning(
|
|
'Unable to link waiting enrollments for user %s, social auth creation failed: %s',
|
|
instance.user.id,
|
|
e,
|
|
)
|
|
raise
|
|
|
|
|
|
def matriculate_learner(user, uid):
|
|
"""
|
|
Update any waiting program enrollments with a user,
|
|
and enroll the user in any waiting course enrollments.
|
|
|
|
In most cases this will just short-circuit. Enrollments will only be updated
|
|
for a SAML provider with a linked organization.
|
|
"""
|
|
try:
|
|
provider_slug, external_user_key = uid.split(':')
|
|
authorizing_org = SAMLProviderConfig.objects.current_set().get(slug=provider_slug).organization
|
|
|
|
if not authorizing_org:
|
|
return
|
|
except (AttributeError, ValueError):
|
|
logger.info('Ignoring non-saml social auth entry for user=%s', user.id)
|
|
return
|
|
except SAMLProviderConfig.DoesNotExist:
|
|
logger.warning(
|
|
'Got incoming social auth for provider=%s but no such provider exists', provider_slug
|
|
)
|
|
return
|
|
except SAMLProviderConfig.MultipleObjectsReturned:
|
|
logger.warning(
|
|
'Unable to activate waiting enrollments for user=%s.'
|
|
' Multiple active SAML configurations found for slug=%s. Expected one.',
|
|
user.id,
|
|
provider_slug)
|
|
return
|
|
|
|
incomplete_enrollments = fetch_program_enrollments_by_student(
|
|
external_user_key=external_user_key,
|
|
waiting_only=True,
|
|
).prefetch_related('program_course_enrollments')
|
|
|
|
for enrollment in incomplete_enrollments:
|
|
try:
|
|
enrollment_org = get_programs(uuid=enrollment.program_uuid)['authoring_organizations'][0]
|
|
if enrollment_org['key'] != authorizing_org.short_name:
|
|
continue
|
|
except (KeyError, TypeError):
|
|
logger.warning(
|
|
'Failed to complete waiting enrollments for organization=%s.'
|
|
' No catalog programs with matching authoring_organization exist.',
|
|
authorizing_org.short_name
|
|
)
|
|
continue
|
|
link_program_enrollment_to_lms_user(enrollment, user)
|