Skip to content

Commit

Permalink
feat: populate assignment.lms_user_id on core.User.save()
Browse files Browse the repository at this point in the history
During the post_save hook for core.User, lookup any existing assignments
for the user and populate the lms_user_id into the assignments.  This
should happen whenever the user logs in, so it theoretically covers the
case where a learner is assigned content before authing with
enterprise-access.

The case where a learner is assigned content AFTER authing with
enterprise-access would be covered by ENT-7874.

ENT-7875
  • Loading branch information
pwnage101 committed Oct 26, 2023
1 parent 9ad845b commit b20fc2a
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 0 deletions.
17 changes: 17 additions & 0 deletions enterprise_access/apps/core/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""
App config for core.
"""
from django.apps import AppConfig


class CoreAppConfig(AppConfig):
"""
AppConfig for core.
"""
name = "enterprise_access.apps.core"
verbose_name = "Core"

def ready(self):
super().ready()

import enterprise_access.apps.core.signals # pylint: disable=unused-import, import-outside-toplevel
39 changes: 39 additions & 0 deletions enterprise_access/apps/core/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""
Signal handlers for core app.
"""
import logging

from django.db.models.signals import post_save
from django.dispatch import receiver

from enterprise_access.apps.content_assignments.models import LearnerContentAssignment

from .models import User

logger = logging.getLogger(__name__)


@receiver(post_save, sender=User)
def update_assignment_lms_user_id_from_user_email(sender, **kwargs): # pylint: disable=unused-argument
"""
Post save hook to update assignment lms_user_id from core user records.
"""
user = kwargs['instance']
if user.lms_user_id:
assignments_to_update = LearnerContentAssignment.objects.filter(
learner_email=user.email,
lms_user_id=None,
)

# Update multiple assignments in a history-safe way.
for assignment in assignments_to_update:
assignment.lms_user_id = user.lms_user_id
num_assignments_updated = LearnerContentAssignment.bulk_update(assignments_to_update, ['lms_user_id'])

# Intentionally not logging PII (email).
if len(assignments_to_update) > 0:
logger.info(
f'Set lms_user_id={user.lms_user_id} on {num_assignments_updated} assignments for User.id={user.id}'
)
else:
logger.info(f'lms_user_id NOT updated on any assignments for User.id={user.id}')
74 changes: 74 additions & 0 deletions enterprise_access/apps/core/tests/test_signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""
Signals and handlers tests.
"""
from django.test import TestCase
from django.utils import timezone

from enterprise_access.apps.content_assignments.tests.factories import LearnerContentAssignmentFactory

from .factories import UserFactory

TEST_EMAIL = 'test@example.com'


class SignalsTests(TestCase):
"""
Tests for signals and handlers.
"""

def test_update_assignment_lms_user_id_from_user_email_registration(self):
"""
Test that `update_assignment_lms_user_id_from_user_email()` correctly updates the `lms_user_id` field on any
assignments for new learners registering.
"""
# Simulate creating asignments for the test learner BEFORE learner registration.
assignments_pre_register = [
LearnerContentAssignmentFactory(learner_email=TEST_EMAIL, lms_user_id=None),
LearnerContentAssignmentFactory(learner_email=TEST_EMAIL, lms_user_id=None),
]
# Simulate registration by creating a user for test learner.
test_user = UserFactory(email=TEST_EMAIL)
# User creation *should* trigger the post_save hook to update lms_user_id for assignments_pre_create.
for assignment in assignments_pre_register:
assignment.refresh_from_db()
assert assignment.lms_user_id == test_user.lms_user_id

def test_update_assignment_lms_user_id_from_user_email_login(self):
"""
Test that `update_assignment_lms_user_id_from_user_email()` correctly updates the `lms_user_id` field on any
assignments for existing learners logging in.
"""
# Simulate registration by creating a user for test learner.
test_user = UserFactory(email=TEST_EMAIL)
# Simulate creating asignments for the test learner AFTER user creation.
assignments_post_register = [
LearnerContentAssignmentFactory(learner_email=TEST_EMAIL, lms_user_id=None),
LearnerContentAssignmentFactory(learner_email=TEST_EMAIL, lms_user_id=None),
]
# Simulate the learner logging in.
test_user.last_login = timezone.now()
test_user.save()
# User login *should* trigger the post_save hook to update lms_user_id for assignments_post_create.
for assignment in assignments_post_register:
assignment.refresh_from_db()
assert assignment.lms_user_id == test_user.lms_user_id

def test_update_assignment_lms_user_id_from_user_email_other_assignments(self):
"""
Test that `update_assignment_lms_user_id_from_user_email()` DOES NOT update any assignments for learners that
aren't registering/logging in during the test.
"""
# Assignments for a different learner should never have their lms_user_id set during the course of this test.
assignments_other_learner = [
LearnerContentAssignmentFactory(learner_email='other@example.com', lms_user_id=None),
LearnerContentAssignmentFactory(learner_email='other@example.com', lms_user_id=None),
]
# Simulate registration by creating a user for test learner.
test_user = UserFactory(email=TEST_EMAIL)
# Simulate the learner logging in.
test_user.last_login = timezone.now()
test_user.save()
# Assignments for other learners *should not* have their lms_user_id updated.
for assignment in assignments_other_learner:
assignment.refresh_from_db()
assert assignment.lms_user_id is None

0 comments on commit b20fc2a

Please sign in to comment.