Пример #1
0
    def test_user_retirement(self):
        """
        Test that the external_user_key is uccessfully retired for a user's program enrollments and history.
        """
        new_status = 'withdrawn'

        self.enrollment.status = new_status
        self.enrollment.save()

        # Ensure that all the records had values for external_user_key
        self.assertEquals(self.enrollment.external_user_key, 'abc')

        self.assertTrue(self.enrollment.historical_records.all())
        for record in self.enrollment.historical_records.all():
            self.assertEquals(record.external_user_key, 'abc')

        ProgramEnrollment.retire_user(self.user.id)
        self.enrollment.refresh_from_db()

        # Ensure those values are retired
        self.assertEquals(self.enrollment.external_user_key, None)

        self.assertTrue(self.enrollment.historical_records.all())
        for record in self.enrollment.historical_records.all():
            self.assertEquals(record.external_user_key, None)
Пример #2
0
    def test_user_retirement(self):
        """
        Test that the external_user_key is successfully retired for a user's program enrollments
        and history.
        """
        new_status = 'canceled'

        self.enrollment.status = new_status
        self.enrollment.save()

        # Ensure that all the records had values for external_user_key
        self.assertEqual(self.enrollment.external_user_key, 'abc')

        self.assertTrue(self.enrollment.historical_records.all())
        for record in self.enrollment.historical_records.all():
            self.assertEqual(record.external_user_key, 'abc')

        ProgramEnrollment.retire_user(self.user.id)
        self.enrollment.refresh_from_db()

        # Ensure those values are retired
        self.assertEqual(self.enrollment.external_user_key, None)

        self.assertTrue(self.enrollment.historical_records.all())
        for record in self.enrollment.historical_records.all():
            self.assertEqual(record.external_user_key, None)
    def validate(self, attrs):
        """ This modifies self.instance in the case of updates """
        if not self.instance:
            enrollment = ProgramEnrollment(**attrs)
            enrollment.full_clean()
        else:
            for key, value in attrs.items():
                setattr(self.instance, key, value)
            self.instance.full_clean()

        return attrs
Пример #4
0
    def validate(self, attrs):
        """ This modifies self.instance in the case of updates """
        if not self.instance:
            enrollment = ProgramEnrollment(**attrs)
            enrollment.full_clean()
        else:
            for key, value in attrs.items():
                setattr(self.instance, key, value)
            self.instance.full_clean()

        return attrs
Пример #5
0
    def patch(self, request, **kwargs):
        """
        Modify the program enrollments for a list of learners
        """
        if len(request.data) > MAX_ENROLLMENT_RECORDS:
            return Response(
                status=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
                content_type='application/json',
            )

        program_uuid = kwargs['program_uuid']
        student_data = self._request_data_by_student_key(request, program_uuid)
        if None in student_data:
            return Response('invalid enrollment record',
                            status.HTTP_422_UNPROCESSABLE_ENTITY)

        response_data = {}
        response_data.update(
            self._remove_duplicate_entries(request, student_data))

        existing_enrollments = {
            enrollment.external_user_key: enrollment
            for enrollment in ProgramEnrollment.bulk_read_by_student_key(
                program_uuid, student_data)
        }

        enrollments_to_create = {}

        for external_user_key in student_data.keys():
            if external_user_key not in existing_enrollments:
                student_data.pop(external_user_key)
                response_data[
                    external_user_key] = CourseEnrollmentResponseStatuses.NOT_IN_PROGRAM

        for external_user_key, enrollment in existing_enrollments.items():
            student = {
                key: value
                for key, value in student_data[external_user_key].items()
                if key == 'status'
            }
            enrollment_serializer = ProgramEnrollmentSerializer(enrollment,
                                                                data=student,
                                                                partial=True)
            if enrollment_serializer.is_valid():
                enrollments_to_create[(
                    external_user_key,
                    enrollment.curriculum_uuid)] = enrollment_serializer
                enrollment_serializer.save()
                response_data[external_user_key] = student['status']
            else:
                serializer_is_invalid = enrollment_serializer.errors['status'][
                    0].code == 'invalid_choice'
                if 'status' in enrollment_serializer.errors and serializer_is_invalid:
                    response_data[
                        external_user_key] = CourseEnrollmentResponseStatuses.INVALID_STATUS

        return self._get_created_or_updated_response(request,
                                                     enrollments_to_create,
                                                     response_data,
                                                     status.HTTP_200_OK)
Пример #6
0
 def get_existing_program_enrollments(self, program_uuid, student_data):
     """ Returns the existing program enrollments for the given students and program """
     student_keys = [data['student_key'] for data in student_data]
     return {
         e.external_user_key: e
         for e in ProgramEnrollment.bulk_read_by_student_key(
             program_uuid, student_keys)
     }
Пример #7
0
 def _remove_existing_entries(self, program_uuid, student_data):
     """ Helper method to remove entries that have existing ProgramEnrollment records. """
     result = {}
     existing_enrollments = ProgramEnrollment.bulk_read_by_student_key(program_uuid, student_data)
     for enrollment in existing_enrollments:
         result[enrollment.external_user_key] = CourseEnrollmentResponseStatuses.CONFLICT
         student_data.pop(enrollment.external_user_key)
     return result
Пример #8
0
    def test_bulk_read_by_student_key(self):
        curriculum_a = uuid4()
        curriculum_b = uuid4()
        enrollments = []
        student_data = {}

        for i in range(5):
            # This will give us 4 program enrollments for self.program_uuid
            # and 1 enrollment for self.other_program_uuid
            user_curriculum = curriculum_b if i % 2 else curriculum_a
            user_status = 'pending' if i % 2 else 'enrolled'
            user_program = self.other_program_uuid if i == 4 else self.program_uuid
            user_key = 'student-{}'.format(i)
            enrollments.append(
                ProgramEnrollment.objects.create(
                    user=None,
                    external_user_key=user_key,
                    program_uuid=user_program,
                    curriculum_uuid=user_curriculum,
                    status=user_status,
                ))
            student_data[user_key] = {'curriculum_uuid': user_curriculum}

        enrollment_records = ProgramEnrollment.bulk_read_by_student_key(
            self.program_uuid, student_data)

        expected = {
            'student-0': {
                'curriculum_uuid': curriculum_a,
                'status': 'enrolled',
                'program_uuid': self.program_uuid
            },
            'student-1': {
                'curriculum_uuid': curriculum_b,
                'status': 'pending',
                'program_uuid': self.program_uuid
            },
            'student-2': {
                'curriculum_uuid': curriculum_a,
                'status': 'enrolled',
                'program_uuid': self.program_uuid
            },
            'student-3': {
                'curriculum_uuid': curriculum_b,
                'status': 'pending',
                'program_uuid': self.program_uuid
            },
        }
        assert expected == {
            enrollment.external_user_key: {
                'curriculum_uuid': enrollment.curriculum_uuid,
                'status': enrollment.status,
                'program_uuid': enrollment.program_uuid,
            }
            for enrollment in enrollment_records
        }
Пример #9
0
 def _remove_existing_entries(self, program_uuid, student_data):
     """ Helper method to remove entries that have existing ProgramEnrollment records. """
     result = {}
     existing_enrollments = ProgramEnrollment.bulk_read_by_student_key(
         program_uuid, student_data)
     for enrollment in existing_enrollments:
         result[
             enrollment.
             external_user_key] = CourseEnrollmentResponseStatuses.CONFLICT
         student_data.pop(enrollment.external_user_key)
     return result
 def get_program_enrollments(self, program_uuid, external_student_keys):
     """
     Does a bulk read of ProgramEnrollments for a given program and list of external student keys
     and returns a dict keyed by external student key
     """
     program_enrollments = ProgramEnrollment.bulk_read_by_student_key(
         program_uuid, external_student_keys).prefetch_related(
             'program_course_enrollments').select_related('user')
     return {
         program_enrollment.external_user_key: program_enrollment
         for program_enrollment in program_enrollments
     }
Пример #11
0
    def patch(self, request, **kwargs):
        """
        Modify the program enrollments for a list of learners
        """
        if len(request.data) > MAX_ENROLLMENT_RECORDS:
            return Response(
                status=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
                content_type='application/json',
            )

        program_uuid = kwargs['program_uuid']
        student_data = self._request_data_by_student_key(request, program_uuid)

        response_data = {}
        response_data.update(self._remove_duplicate_entries(request, student_data))

        existing_enrollments = {
            enrollment.external_user_key: enrollment
            for enrollment in
            ProgramEnrollment.bulk_read_by_student_key(program_uuid, student_data)
        }

        enrollments_to_create = {}

        for external_user_key in student_data.keys():
            if external_user_key not in existing_enrollments:
                student_data.pop(external_user_key)
                response_data[external_user_key] = CourseEnrollmentResponseStatuses.NOT_IN_PROGRAM

        for external_user_key, enrollment in existing_enrollments.items():
            student = {key: value for key, value in student_data[external_user_key].items() if key == 'status'}
            enrollment_serializer = ProgramEnrollmentSerializer(enrollment, data=student, partial=True)
            if enrollment_serializer.is_valid():
                enrollments_to_create[(external_user_key, enrollment.curriculum_uuid)] = enrollment_serializer
                enrollment_serializer.save()
                response_data[external_user_key] = student['status']
            else:
                serializer_is_invalid = enrollment_serializer.errors['status'][0].code == 'invalid_choice'
                if 'status' in enrollment_serializer.errors and serializer_is_invalid:
                    response_data[external_user_key] = CourseEnrollmentResponseStatuses.INVALID_STATUS

        return self._get_created_or_updated_response(request, enrollments_to_create, response_data, status.HTTP_200_OK)
Пример #12
0
    def test_bulk_read_by_student_key(self):
        curriculum_a = uuid4()
        curriculum_b = uuid4()
        enrollments = []
        student_data = {}

        for i in xrange(5):
            # This will give us 4 program enrollments for self.program_uuid
            # and 1 enrollment for self.other_program_uuid
            user_curriculum = curriculum_b if i % 2 else curriculum_a
            user_status = 'pending' if i % 2 else 'enrolled'
            user_program = self.other_program_uuid if i == 4 else self.program_uuid
            user_key = 'student-{}'.format(i)
            enrollments.append(
                ProgramEnrollment.objects.create(
                    user=None,
                    external_user_key=user_key,
                    program_uuid=user_program,
                    curriculum_uuid=user_curriculum,
                    status=user_status,
                )
            )
            student_data[user_key] = {'curriculum_uuid': user_curriculum}

        enrollment_records = ProgramEnrollment.bulk_read_by_student_key(self.program_uuid, student_data)

        expected = {
            'student-0': {'curriculum_uuid': curriculum_a, 'status': 'enrolled', 'program_uuid': self.program_uuid},
            'student-1': {'curriculum_uuid': curriculum_b, 'status': 'pending', 'program_uuid': self.program_uuid},
            'student-2': {'curriculum_uuid': curriculum_a, 'status': 'enrolled', 'program_uuid': self.program_uuid},
            'student-3': {'curriculum_uuid': curriculum_b, 'status': 'pending', 'program_uuid': self.program_uuid},
        }
        assert expected == {
            enrollment.external_user_key: {
                'curriculum_uuid': enrollment.curriculum_uuid,
                'status': enrollment.status,
                'program_uuid': enrollment.program_uuid,
            }
            for enrollment in enrollment_records
        }
Пример #13
0
 def validate(self, attrs):
     enrollment = ProgramEnrollment(**attrs)
     enrollment.full_clean()
     return attrs
Пример #14
0
    def post(self, request, *args, **kwargs):
        """
        This is the POST for ProgramEnrollments
        """
        if len(request.data) > 25:
            return Response(
                status=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
                content_type='application/json',
            )

        program_uuid = kwargs['program_uuid']
        student_data = OrderedDict((
            row.get('external_user_key'),
            {
                'program_uuid': program_uuid,
                'curriculum_uuid': row.get('curriculum_uuid'),
                'status': row.get('status'),
                'external_user_key': row.get('external_user_key'),
            })
            for row in request.data
        )

        key_counter = Counter([enrollment.get('external_user_key') for enrollment in request.data])

        response_data = {}
        for student_key, count in key_counter.items():
            if count > 1:
                response_data[student_key] = CourseEnrollmentResponseStatuses.DUPLICATED
                student_data.pop(student_key)

        existing_enrollments = ProgramEnrollment.bulk_read_by_student_key(program_uuid, student_data)
        for enrollment in existing_enrollments:
            response_data[enrollment.external_user_key] = CourseEnrollmentResponseStatuses.CONFLICT
            student_data.pop(enrollment.external_user_key)

        enrollments_to_create = {}

        for student_key, data in student_data.items():
            curriculum_uuid = data['curriculum_uuid']
            existing_user = get_user_by_program_id(student_key, program_uuid)

            if existing_user:
                data['user'] = existing_user.id

            serializer = ProgramEnrollmentSerializer(data=data)
            if serializer.is_valid():
                enrollments_to_create[(student_key, curriculum_uuid)] = serializer
                response_data[student_key] = data.get('status')
            else:
                if 'status' in serializer.errors and serializer.errors['status'][0].code == 'invalid_choice':
                    response_data[student_key] = CourseEnrollmentResponseStatuses.INVALID_STATUS
                else:
                    return Response(
                        'invalid enrollment record',
                        status.HTTP_422_UNPROCESSABLE_ENTITY
                    )

        for enrollment_serializer in enrollments_to_create.values():
            # create the model
            enrollment_serializer.save()
            # TODO: make this a bulk save

        if not enrollments_to_create:
            return Response(
                status=status.HTTP_422_UNPROCESSABLE_ENTITY,
                data=response_data,
                content_type='application/json',
            )

        if len(request.data) != len(enrollments_to_create):
            return Response(
                status=status.HTTP_207_MULTI_STATUS,
                data=response_data,
                content_type='application/json',
            )

        return Response(
            status=status.HTTP_201_CREATED,
            data=response_data,
            content_type='application/json',
        )
Пример #15
0
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)
Пример #16
0
 def validate(self, attrs):
     enrollment = ProgramEnrollment(**attrs)
     enrollment.full_clean()
     return attrs
Пример #17
0
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)