def test_unenrollment_email_on(self):
        """
        Do email on unenroll test
        """

        course = self.course

        #Create invited, but not registered, user
        cea = CourseEnrollmentAllowed(email='*****@*****.**', course_id=course.id)
        cea.save()

        url = reverse('instructor_dashboard', kwargs={'course_id': course.id})
        response = self.client.post(url, {'action': 'Unenroll multiple students', 'multiple_students': '[email protected], [email protected], [email protected]', 'email_students': 'on'})

        #Check the page output
        self.assertContains(response, '<td>[email protected]</td>')
        self.assertContains(response, '<td>[email protected]</td>')
        self.assertContains(response, '<td>un-enrolled, email sent</td>')

        #Check the outbox
        self.assertEqual(len(mail.outbox), 3)
        self.assertEqual(mail.outbox[0].subject, 'You have been un-enrolled from MITx/999/Robot_Super_Course')
        self.assertEqual(mail.outbox[0].body, "Dear Student,\n\nYou have been un-enrolled from course MITx/999/Robot_Super_Course by a member of the course staff. " +
                         "Please disregard the invitation previously sent.\n\n" +
                         "----\nThis email was automatically sent from edx.org to [email protected]")
        self.assertEqual(mail.outbox[1].subject, 'You have been un-enrolled from MITx/999/Robot_Super_Course')
Exemple #2
0
    def post(self, request):
        """
        POST /api/user/v1/accounts/retire/

        {
            'username': '******'
        }

        Retires the user with the given username.  This includes
        retiring this username, the associates email address, and
        any other PII associated with this user.
        """
        username = request.data['username']
        if is_username_retired(username):
            return Response(status=status.HTTP_404_NOT_FOUND)

        try:
            retirement_status = UserRetirementStatus.get_retirement_for_retirement_action(username)
            user = retirement_status.user
            retired_username = retirement_status.retired_username or get_retired_username_by_username(username)
            retired_email = retirement_status.retired_email or get_retired_email_by_email(user.email)
            original_email = retirement_status.original_email

            # Retire core user/profile information
            self.clear_pii_from_userprofile(user)
            self.delete_users_profile_images(user)
            self.delete_users_country_cache(user)

            # Retire data from Enterprise models
            self.retire_users_data_sharing_consent(username, retired_username)
            self.retire_sapsf_data_transmission(user)
            self.retire_user_from_pending_enterprise_customer_user(user, retired_email)
            self.retire_entitlement_support_detail(user)

            # Retire misc. models that may contain PII of this user
            SoftwareSecurePhotoVerification.retire_user(user.id)
            PendingEmailChange.delete_by_user_value(user, field='user')
            UserOrgTag.delete_by_user_value(user, field='user')

            # Retire any objects linked to the user via their original email
            CourseEnrollmentAllowed.delete_by_user_value(original_email, field='email')
            UnregisteredLearnerCohortAssignments.delete_by_user_value(original_email, field='email')

            # TODO: Password Reset links - https://openedx.atlassian.net/browse/PLAT-2104
            # TODO: Delete OAuth2 records - https://openedx.atlassian.net/browse/EDUCATOR-2703

            user.first_name = ''
            user.last_name = ''
            user.is_active = False
            user.username = retired_username
            user.save()
        except UserRetirementStatus.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        except RetirementStateError as exc:
            return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST)
        except Exception as exc:  # pylint: disable=broad-except
            return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        return Response(status=status.HTTP_204_NO_CONTENT)
Exemple #3
0
    def post(self, request):
        """
        POST /api/user/v1/accounts/retire/

        {
            'username': '******'
        }

        Retires the user with the given username.  This includes
        retiring this username, the associated email address, and
        any other PII associated with this user.
        """
        username = request.data['username']

        try:
            retirement_status = UserRetirementStatus.get_retirement_for_retirement_action(username)
            user = retirement_status.user
            retired_username = retirement_status.retired_username or get_retired_username_by_username(username)
            retired_email = retirement_status.retired_email or get_retired_email_by_email(user.email)
            original_email = retirement_status.original_email

            # Retire core user/profile information
            self.clear_pii_from_userprofile(user)
            self.delete_users_profile_images(user)
            self.delete_users_country_cache(user)

            # Retire data from Enterprise models
            self.retire_users_data_sharing_consent(username, retired_username)
            self.retire_sapsf_data_transmission(user)
            self.retire_degreed_data_transmission(user)
            self.retire_user_from_pending_enterprise_customer_user(user, retired_email)
            self.retire_entitlement_support_detail(user)

            # Retire misc. models that may contain PII of this user
            PendingEmailChange.delete_by_user_value(user, field='user')
            UserOrgTag.delete_by_user_value(user, field='user')

            # Retire any objects linked to the user via their original email
            CourseEnrollmentAllowed.delete_by_user_value(original_email, field='email')
            UnregisteredLearnerCohortAssignments.delete_by_user_value(original_email, field='email')

            # This signal allows code in higher points of LMS to retire the user as necessary
            USER_RETIRE_LMS_CRITICAL.send(sender=self.__class__, user=user)

            user.first_name = ''
            user.last_name = ''
            user.is_active = False
            user.username = retired_username
            user.save()
        except UserRetirementStatus.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        except RetirementStateError as exc:
            return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST)
        except Exception as exc:  # pylint: disable=broad-except
            return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        return Response(status=status.HTTP_204_NO_CONTENT)
Exemple #4
0
    def __init__(self, course_id, email):
        # N.B. retired users are not a concern here because they should be
        # handled at a higher level (i.e. in enroll_email).  Besides, this
        # class creates readonly objects.
        exists_user = User.objects.filter(email=email).exists()
        if exists_user:
            user = User.objects.get(email=email)
            mode, is_active = CourseEnrollment.enrollment_mode_for_user(user, course_id)
            # is_active is `None` if the user is not enrolled in the course
            exists_ce = is_active is not None and is_active
            full_name = user.profile.name
            ceas = CourseEnrollmentAllowed.for_user(user).filter(course_id=course_id).all()
        else:
            mode = None
            exists_ce = False
            full_name = None
            ceas = CourseEnrollmentAllowed.objects.filter(email=email, course_id=course_id).all()
        exists_allowed = ceas.exists()
        state_auto_enroll = exists_allowed and ceas[0].auto_enroll

        self.user = exists_user
        self.enrollment = exists_ce
        self.allowed = exists_allowed
        self.auto_enroll = bool(state_auto_enroll)
        self.full_name = full_name
        self.mode = mode
 def test_retiring_nonexistent_user_doesnt_modify_records(self):
     is_successful = CourseEnrollmentAllowed.delete_by_user_value(
         value='*****@*****.**',
         field='email'
     )
     self.assertFalse(is_successful)
     user_search_results = CourseEnrollmentAllowed.objects.filter(
         email=self.email
     )
     self.assertTrue(user_search_results.exists())
 def test_retiring_user_deletes_record(self):
     is_successful = CourseEnrollmentAllowed.delete_by_user_value(
         value=self.email,
         field='email'
     )
     self.assertTrue(is_successful)
     user_search_results = CourseEnrollmentAllowed.objects.filter(
         email=self.email
     )
     self.assertFalse(user_search_results)
    def test_unenrollment_email_on(self):
        """
        Do email on unenroll test
        """

        course = self.course

        # Create invited, but not registered, user
        cea = CourseEnrollmentAllowed(email="*****@*****.**", course_id=course.id)
        cea.save()

        url = reverse("instructor_dashboard_legacy", kwargs={"course_id": course.id.to_deprecated_string()})
        response = self.client.post(
            url,
            {
                "action": "Unenroll multiple students",
                "multiple_students": "[email protected], [email protected], [email protected]",
                "email_students": "on",
            },
        )

        # Check the page output
        self.assertContains(response, "<td>[email protected]</td>")
        self.assertContains(response, "<td>[email protected]</td>")
        self.assertContains(response, "<td>un-enrolled, email sent</td>")

        # Check the outbox
        self.assertEqual(len(mail.outbox), 3)
        self.assertEqual(mail.outbox[0].subject, "You have been un-enrolled from {}".format(course.display_name))
        self.assertEqual(
            mail.outbox[0].body,
            "Dear Student,\n\nYou have been un-enrolled from course "
            "{} by a member of the course staff. "
            "Please disregard the invitation previously sent.\n\n"
            "----\nThis email was automatically sent from edx.org "
            "to [email protected]".format(course.display_name),
        )
        self.assertEqual(mail.outbox[1].subject, "You have been un-enrolled from {}".format(course.display_name))
Exemple #8
0
def list_may_enroll(course_key, features):
    """
    Return info about students who may enroll in a course as a dict.

    list_may_enroll(course_key, ['email'])
    would return [
        {'email': 'email1'}
        {'email': 'email2'}
        {'email': 'email3'}
    ]

    Note that result does not include students who may enroll and have
    already done so.
    """
    may_enroll_and_unenrolled = CourseEnrollmentAllowed.may_enroll_and_unenrolled(course_key)

    def extract_student(student, features):
        """
        Build dict containing information about a single student.
        """
        return dict((feature, getattr(student, feature)) for feature in features)

    return [extract_student(student, features) for student in may_enroll_and_unenrolled]
Exemple #9
0
    def __init__(self, course_id, email):
        exists_user = User.objects.filter(email=email).exists()
        if exists_user:
            user = User.objects.get(email=email)
            mode, is_active = CourseEnrollment.enrollment_mode_for_user(user, course_id)
            # is_active is `None` if the user is not enrolled in the course
            exists_ce = is_active is not None and is_active
            full_name = user.profile.name
            ceas = CourseEnrollmentAllowed.for_user(user).filter(course_id=course_id).all()
        else:
            mode = None
            exists_ce = False
            full_name = None
            ceas = CourseEnrollmentAllowed.objects.filter(email=email, course_id=course_id).all()
        exists_allowed = ceas.exists()
        state_auto_enroll = exists_allowed and ceas[0].auto_enroll

        self.user = exists_user
        self.enrollment = exists_ce
        self.allowed = exists_allowed
        self.auto_enroll = bool(state_auto_enroll)
        self.full_name = full_name
        self.mode = mode
Exemple #10
0
def _do_enroll_students(course, course_key, students, secure=False, overload=False, auto_enroll=False, email_students=False, is_shib_course=False):
    """
    Do the actual work of enrolling multiple students, presented as a string
    of emails separated by commas or returns
    `course` is course object
    `course_key` id of course (a CourseKey)
    `students` string of student emails separated by commas or returns (a `str`)
    `overload` un-enrolls all existing students (a `boolean`)
    `auto_enroll` is user input preference (a `boolean`)
    `email_students` is user input preference (a `boolean`)
    """

    new_students, new_students_lc = get_and_clean_student_list(students)
    status = dict([x, 'unprocessed'] for x in new_students)

    if overload:  # delete all but staff
        todelete = CourseEnrollment.objects.filter(course_id=course_key)
        for enrollee in todelete:
            if not has_access(enrollee.user, 'staff', course) and enrollee.user.email.lower() not in new_students_lc:
                status[enrollee.user.email] = 'deleted'
                enrollee.deactivate()
            else:
                status[enrollee.user.email] = 'is staff'
        ceaset = CourseEnrollmentAllowed.objects.filter(course_id=course_key)
        for cea in ceaset:
            status[cea.email] = 'removed from pending enrollment list'
        ceaset.delete()

    if email_students:
        protocol = 'https' if secure else 'http'
        stripped_site_name = microsite.get_value(
            'SITE_NAME',
            settings.SITE_NAME
        )
        # TODO: Use request.build_absolute_uri rather than '{proto}://{site}{path}'.format
        # and check with the Services team that this works well with microsites
        registration_url = '{proto}://{site}{path}'.format(
            proto=protocol,
            site=stripped_site_name,
            path=reverse('student.views.register_user')
        )
        course_url = '{proto}://{site}{path}'.format(
            proto=protocol,
            site=stripped_site_name,
            path=reverse('course_root', kwargs={'course_id': course_key.to_deprecated_string()})
        )
        # We can't get the url to the course's About page if the marketing site is enabled.
        course_about_url = None
        if not settings.FEATURES.get('ENABLE_MKTG_SITE', False):
            course_about_url = u'{proto}://{site}{path}'.format(
                proto=protocol,
                site=stripped_site_name,
                path=reverse('about_course', kwargs={'course_id': course_key.to_deprecated_string()})
            )

        # Composition of email
        email_data = {
            'site_name': stripped_site_name,
            'registration_url': registration_url,
            'course': course,
            'auto_enroll': auto_enroll,
            'course_url': course_url,
            'course_about_url': course_about_url,
            'is_shib_course': is_shib_course
        }

    for student in new_students:
        try:
            user = User.objects.get(email=student)
        except User.DoesNotExist:

            # Student not signed up yet, put in pending enrollment allowed table
            cea = CourseEnrollmentAllowed.objects.filter(email=student, course_id=course_key)

            # If enrollmentallowed already exists, update auto_enroll flag to however it was set in UI
            # Will be 0 or 1 records as there is a unique key on email + course_id
            if cea:
                cea[0].auto_enroll = auto_enroll
                cea[0].save()
                status[student] = 'user does not exist, enrollment already allowed, pending with auto enrollment ' \
                    + ('on' if auto_enroll else 'off')
                continue

            # EnrollmentAllowed doesn't exist so create it
            cea = CourseEnrollmentAllowed(email=student, course_id=course_key, auto_enroll=auto_enroll)
            cea.save()

            status[student] = 'user does not exist, enrollment allowed, pending with auto enrollment ' \
                + ('on' if auto_enroll else 'off')

            if email_students:
                # User is allowed to enroll but has not signed up yet
                email_data['email_address'] = student
                email_data['message'] = 'allowed_enroll'
                send_mail_ret = send_mail_to_student(student, email_data)
                status[student] += (', email sent' if send_mail_ret else '')
            continue

        # Student has already registered
        if CourseEnrollment.is_enrolled(user, course_key):
            status[student] = 'already enrolled'
            continue

        try:
            # Not enrolled yet
            CourseEnrollment.enroll(user, course_key)
            status[student] = 'added'

            if email_students:
                # User enrolled for first time, populate dict with user specific info
                email_data['email_address'] = student
                email_data['full_name'] = user.profile.name
                email_data['message'] = 'enrolled_enroll'
                send_mail_ret = send_mail_to_student(student, email_data)
                status[student] += (', email sent' if send_mail_ret else '')

        except Exception:  # pylint: disable=broad-except
            status[student] = 'rejected'

    datatable = {'header': ['StudentEmail', 'action']}
    datatable['data'] = [[x, status[x]] for x in sorted(status)]
    datatable['title'] = _('Enrollment of students')

    def sf(stat):
        return [x for x in status if status[x] == stat]

    data = dict(added=sf('added'), rejected=sf('rejected') + sf('exists'),
                deleted=sf('deleted'), datatable=datatable)

    return data
def _do_enroll_students(
    course,
    course_key,
    students,
    secure=False,
    overload=False,
    auto_enroll=False,
    email_students=False,
    is_shib_course=False,
):
    """
    Do the actual work of enrolling multiple students, presented as a string
    of emails separated by commas or returns
    `course` is course object
    `course_key` id of course (a CourseKey)
    `students` string of student emails separated by commas or returns (a `str`)
    `overload` un-enrolls all existing students (a `boolean`)
    `auto_enroll` is user input preference (a `boolean`)
    `email_students` is user input preference (a `boolean`)
    """

    new_students, new_students_lc = get_and_clean_student_list(students)
    status = dict([x, "unprocessed"] for x in new_students)

    if overload:  # delete all but staff
        todelete = CourseEnrollment.objects.filter(course_id=course_key)
        for enrollee in todelete:
            if not has_access(enrollee.user, "staff", course) and enrollee.user.email.lower() not in new_students_lc:
                status[enrollee.user.email] = "deleted"
                enrollee.deactivate()
            else:
                status[enrollee.user.email] = "is staff"
        ceaset = CourseEnrollmentAllowed.objects.filter(course_id=course_key)
        for cea in ceaset:
            status[cea.email] = "removed from pending enrollment list"
        ceaset.delete()

    if email_students:
        protocol = "https" if secure else "http"
        stripped_site_name = microsite.get_value("SITE_NAME", settings.SITE_NAME)
        # TODO: Use request.build_absolute_uri rather than '{proto}://{site}{path}'.format
        # and check with the Services team that this works well with microsites
        registration_url = "{proto}://{site}{path}".format(
            proto=protocol, site=stripped_site_name, path=reverse("register_user")
        )
        course_url = "{proto}://{site}{path}".format(
            proto=protocol,
            site=stripped_site_name,
            path=reverse("course_root", kwargs={"course_id": course_key.to_deprecated_string()}),
        )
        # We can't get the url to the course's About page if the marketing site is enabled.
        course_about_url = None
        if not settings.FEATURES.get("ENABLE_MKTG_SITE", False):
            course_about_url = u"{proto}://{site}{path}".format(
                proto=protocol,
                site=stripped_site_name,
                path=reverse("about_course", kwargs={"course_id": course_key.to_deprecated_string()}),
            )

        # Composition of email
        email_data = {
            "site_name": stripped_site_name,
            "registration_url": registration_url,
            "course": course,
            "auto_enroll": auto_enroll,
            "course_url": course_url,
            "course_about_url": course_about_url,
            "is_shib_course": is_shib_course,
        }

    for student in new_students:
        try:
            user = User.objects.get(email=student)
        except User.DoesNotExist:

            # Student not signed up yet, put in pending enrollment allowed table
            cea = CourseEnrollmentAllowed.objects.filter(email=student, course_id=course_key)

            # If enrollmentallowed already exists, update auto_enroll flag to however it was set in UI
            # Will be 0 or 1 records as there is a unique key on email + course_id
            if cea:
                cea[0].auto_enroll = auto_enroll
                cea[0].save()
                status[student] = "user does not exist, enrollment already allowed, pending with auto enrollment " + (
                    "on" if auto_enroll else "off"
                )
                continue

            # EnrollmentAllowed doesn't exist so create it
            cea = CourseEnrollmentAllowed(email=student, course_id=course_key, auto_enroll=auto_enroll)
            cea.save()

            status[student] = "user does not exist, enrollment allowed, pending with auto enrollment " + (
                "on" if auto_enroll else "off"
            )

            if email_students:
                # User is allowed to enroll but has not signed up yet
                email_data["email_address"] = student
                email_data["message"] = "allowed_enroll"
                send_mail_ret = send_mail_to_student(student, email_data)
                status[student] += ", email sent" if send_mail_ret else ""
            continue

        # Student has already registered
        if CourseEnrollment.is_enrolled(user, course_key):
            status[student] = "already enrolled"
            continue

        try:
            # Not enrolled yet
            CourseEnrollment.enroll(user, course_key)
            status[student] = "added"

            if email_students:
                # User enrolled for first time, populate dict with user specific info
                email_data["email_address"] = student
                email_data["full_name"] = user.profile.name
                email_data["message"] = "enrolled_enroll"
                send_mail_ret = send_mail_to_student(student, email_data)
                status[student] += ", email sent" if send_mail_ret else ""

        except Exception:  # pylint: disable=broad-except
            status[student] = "rejected"

    datatable = {"header": ["StudentEmail", "action"]}
    datatable["data"] = [[x, status[x]] for x in sorted(status)]
    datatable["title"] = _("Enrollment of students")

    def sf(stat):
        return [x for x in status if status[x] == stat]

    data = dict(added=sf("added"), rejected=sf("rejected") + sf("exists"), deleted=sf("deleted"), datatable=datatable)

    return data
Exemple #12
0
def _do_enroll_students(course, course_key, students, secure=False, overload=False, auto_enroll=False, email_students=False, is_shib_course=False):
    """
    Do the actual work of enrolling multiple students, presented as a string
    of emails separated by commas or returns
    `course` is course object
    `course_key` id of course (a CourseKey)
    `students` string of student emails separated by commas or returns (a `str`)
    `overload` un-enrolls all existing students (a `boolean`)
    `auto_enroll` is user input preference (a `boolean`)
    `email_students` is user input preference (a `boolean`)
    """

    new_students, new_students_lc = get_and_clean_student_list(students)
    status = dict([x, 'unprocessed'] for x in new_students)

    if overload:  # delete all but staff
        todelete = CourseEnrollment.objects.filter(course_id=course_key)
        for enrollee in todelete:
            if not has_access(enrollee.user, 'staff', course) and enrollee.user.email.lower() not in new_students_lc:
                status[enrollee.user.email] = 'deleted'
                enrollee.deactivate()
            else:
                status[enrollee.user.email] = 'is staff'
        ceaset = CourseEnrollmentAllowed.objects.filter(course_id=course_key)
        for cea in ceaset:
            status[cea.email] = 'removed from pending enrollment list'
        ceaset.delete()

    if email_students:
        protocol = 'https' if secure else 'http'
        stripped_site_name = microsite.get_value(
            'SITE_NAME',
            settings.SITE_NAME
        )
        # TODO: Use request.build_absolute_uri rather than '{proto}://{site}{path}'.format
        # and check with the Services team that this works well with microsites
        registration_url = '{proto}://{site}{path}'.format(
            proto=protocol,
            site=stripped_site_name,
            path=reverse('register_user')
        )
        course_url = '{proto}://{site}{path}'.format(
            proto=protocol,
            site=stripped_site_name,
            path=reverse('course_root', kwargs={'course_id': course_key.to_deprecated_string()})
        )
        # We can't get the url to the course's About page if the marketing site is enabled.
        course_about_url = None
        if not settings.FEATURES.get('ENABLE_MKTG_SITE', False):
            course_about_url = u'{proto}://{site}{path}'.format(
                proto=protocol,
                site=stripped_site_name,
                path=reverse('about_course', kwargs={'course_id': course_key.to_deprecated_string()})
            )

        # Composition of email
        email_data = {
            'site_name': stripped_site_name,
            'registration_url': registration_url,
            'course': course,
            'auto_enroll': auto_enroll,
            'course_url': course_url,
            'course_about_url': course_about_url,
            'is_shib_course': is_shib_course
        }

    for student in new_students:
        try:
            user = User.objects.get(email=student)
        except User.DoesNotExist:

            # Student not signed up yet, put in pending enrollment allowed table
            cea = CourseEnrollmentAllowed.objects.filter(email=student, course_id=course_key)

            # If enrollmentallowed already exists, update auto_enroll flag to however it was set in UI
            # Will be 0 or 1 records as there is a unique key on email + course_id
            if cea:
                cea[0].auto_enroll = auto_enroll
                cea[0].save()
                status[student] = 'user does not exist, enrollment already allowed, pending with auto enrollment ' \
                    + ('on' if auto_enroll else 'off')
                continue

            # EnrollmentAllowed doesn't exist so create it
            cea = CourseEnrollmentAllowed(email=student, course_id=course_key, auto_enroll=auto_enroll)
            cea.save()

            status[student] = 'user does not exist, enrollment allowed, pending with auto enrollment ' \
                + ('on' if auto_enroll else 'off')

            if email_students:
                # User is allowed to enroll but has not signed up yet
                email_data['email_address'] = student
                email_data['message'] = 'allowed_enroll'
                send_mail_ret = send_mail_to_student(student, email_data)
                status[student] += (', email sent' if send_mail_ret else '')
            continue

        # Student has already registered
        if CourseEnrollment.is_enrolled(user, course_key):
            status[student] = 'already enrolled'
            continue

        try:
            # Not enrolled yet
            CourseEnrollment.enroll(user, course_key)
            status[student] = 'added'

            if email_students:
                # User enrolled for first time, populate dict with user specific info
                email_data['email_address'] = student
                email_data['full_name'] = user.profile.name
                email_data['message'] = 'enrolled_enroll'
                send_mail_ret = send_mail_to_student(student, email_data)
                status[student] += (', email sent' if send_mail_ret else '')

        except Exception:  # pylint: disable=broad-except
            status[student] = 'rejected'

    datatable = {'header': ['StudentEmail', 'action']}
    datatable['data'] = [[x, status[x]] for x in sorted(status)]
    datatable['title'] = _('Enrollment of students')

    def sf(stat):  # pylint: disable=invalid-name
        return [x for x in status if status[x] == stat]

    data = dict(added=sf('added'), rejected=sf('rejected') + sf('exists'),
                deleted=sf('deleted'), datatable=datatable)

    return data
Exemple #13
0
    def post(self, request):
        """
        POST /api/user/v1/accounts/retire/

        {
            'username': '******'
        }

        Retires the user with the given username.  This includes
        retiring this username, the associates email address, and
        any other PII associated with this user.
        """
        username = request.data['username']

        try:
            retirement_status = UserRetirementStatus.get_retirement_for_retirement_action(
                username)
            user = retirement_status.user
            retired_username = retirement_status.retired_username or get_retired_username_by_username(
                username)
            retired_email = retirement_status.retired_email or get_retired_email_by_email(
                user.email)
            original_email = retirement_status.original_email

            # Retire core user/profile information
            self.clear_pii_from_userprofile(user)
            self.delete_users_profile_images(user)
            self.delete_users_country_cache(user)

            # Retire data from Enterprise models
            self.retire_users_data_sharing_consent(username, retired_username)
            self.retire_sapsf_data_transmission(user)
            self.retire_degreed_data_transmission(user)
            self.retire_user_from_pending_enterprise_customer_user(
                user, retired_email)
            self.retire_entitlement_support_detail(user)

            # Retire misc. models that may contain PII of this user
            SoftwareSecurePhotoVerification.retire_user(user.id)
            PendingEmailChange.delete_by_user_value(user, field='user')
            UserOrgTag.delete_by_user_value(user, field='user')

            # Retire any objects linked to the user via their original email
            CourseEnrollmentAllowed.delete_by_user_value(original_email,
                                                         field='email')
            UnregisteredLearnerCohortAssignments.delete_by_user_value(
                original_email, field='email')

            user.first_name = ''
            user.last_name = ''
            user.is_active = False
            user.username = retired_username
            user.save()
        except UserRetirementStatus.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        except RetirementStateError as exc:
            return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST)
        except Exception as exc:  # pylint: disable=broad-except
            return Response(text_type(exc),
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        return Response(status=status.HTTP_204_NO_CONTENT)