diff --git a/lms/djangoapps/save_for_later/models.py b/lms/djangoapps/save_for_later/models.py index 406f82bba9..3a6b5ed243 100644 --- a/lms/djangoapps/save_for_later/models.py +++ b/lms/djangoapps/save_for_later/models.py @@ -7,14 +7,30 @@ from model_utils.models import TimeStampedModel from django.db import models from opaque_keys.edx.django.models import CourseKeyField +from openedx.core.djangolib.model_mixins import DeletableByUserValue -class SavedCourse(TimeStampedModel): + +class SavedCourse(DeletableByUserValue, TimeStampedModel): + """ + Tracks save course by email. + + .. pii: Stores email address of the User. + .. pii_types: email_address + .. pii_retirement: local_api + """ user_id = models.IntegerField(null=True, blank=True) email = models.EmailField(db_index=True) course_id = CourseKeyField(max_length=255, db_index=True) -class SavedProgram(TimeStampedModel): +class SavedProgram(DeletableByUserValue, TimeStampedModel): + """ + Tracks save program by email. + + .. pii: Stores email address of the User. + .. pii_types: email_address + .. pii_retirement: local_api + """ user_id = models.IntegerField(null=True, blank=True) email = models.EmailField(db_index=True) program_uuid = models.UUIDField() diff --git a/lms/djangoapps/save_for_later/signals.py b/lms/djangoapps/save_for_later/signals.py new file mode 100644 index 0000000000..1537dcc549 --- /dev/null +++ b/lms/djangoapps/save_for_later/signals.py @@ -0,0 +1,14 @@ +""" +Signal handler for save for later +""" +from django.dispatch.dispatcher import receiver + +from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_LMS_CRITICAL +from .models import SavedCourse, SavedProgram + + +@receiver(USER_RETIRE_LMS_CRITICAL) +def _listen_for_lms_retire(sender, **kwargs): # pylint: disable=unused-argument + user = kwargs.get('user') + SavedCourse.delete_by_user_value(user.id, field='user_id') + SavedProgram.delete_by_user_value(user.id, field='user_id') diff --git a/lms/djangoapps/save_for_later/tests/__init__.py b/lms/djangoapps/save_for_later/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lms/djangoapps/save_for_later/tests/test_signals.py b/lms/djangoapps/save_for_later/tests/test_signals.py new file mode 100644 index 0000000000..e3d19b8760 --- /dev/null +++ b/lms/djangoapps/save_for_later/tests/test_signals.py @@ -0,0 +1,42 @@ +""" +Unit tests for the signals +""" +from uuid import uuid4 +from django.test import TestCase + +from common.djangoapps.student.tests.factories import UserFactory +from ..models import SavedCourse, SavedProgram +from ..signals import _listen_for_lms_retire + + +class RetirementSignalTest(TestCase): + """ + Tests for the user retirement signal + """ + + def setUp(self): + super().setUp() + self.user = UserFactory() + self.email = self.user.email + + def _create_objects(self): + """ + Create test objects. + """ + SavedCourse.objects.create(user_id=self.user.id, email=self.email, course_id='course-v1:TestX+TestX101+1T2022') + SavedProgram.objects.create(user_id=self.user.id, email=self.email, program_uuid=uuid4()) + + assert SavedCourse.objects.filter(email=self.email).exists() + assert SavedProgram.objects.filter(email=self.email).exists() + + def test_retire_success(self): + self._create_objects() + _listen_for_lms_retire(sender=self.__class__, user=self.user, email=self.email) + + assert not SavedCourse.objects.filter(email=self.email).exists() + assert not SavedProgram.objects.filter(email=self.email).exists() + + def test_retire_success_no_entries(self): + assert not SavedCourse.objects.filter(email=self.email).exists() + assert not SavedProgram.objects.filter(email=self.email).exists() + _listen_for_lms_retire(sender=self.__class__, user=self.user, email=self.email)