Exemple #1
0
    def test_single_program_engagement(self, mock_get_programs):
        """
        Verify that correct program is returned when the user is enrolled in a
        course run appearing in one program.
        """
        course_run_key = generate_course_run_key()
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key),
                    ]),
                ]
            ),
            ProgramFactory(),
        ]
        mock_get_programs.return_value = data

        self._create_enrollments(course_run_key)
        meter = ProgramProgressMeter(self.user)

        self._attach_detail_url(data)
        program = data[0]
        self.assertEqual(meter.engaged_programs, [program])
        self._assert_progress(
            meter,
            ProgressFactory(uuid=program['uuid'], in_progress=1)
        )
        self.assertEqual(meter.completed_programs, [])
Exemple #2
0
    def test_course_progress(self, mock_get_programs):
        """
        Verify that the progress meter can represent progress in terms of
        serialized courses.
        """
        course_run_key = generate_course_run_key()
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key),
                    ]),
                ]
            )
        ]
        mock_get_programs.return_value = data

        self._create_enrollments(course_run_key)

        meter = ProgramProgressMeter(self.user)

        program = data[0]
        expected = [
            ProgressFactory(
                uuid=program['uuid'],
                completed=[],
                in_progress=[program['courses'][0]],
                not_started=[]
            )
        ]

        self.assertEqual(meter.progress(count_only=False), expected)
Exemple #3
0
    def test_single_program_engagement(self, mock_get_programs):
        """
        Verify that correct program is returned when the user is enrolled in a
        course run appearing in one program.
        """
        course_run_key = generate_course_run_key()
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key),
                    ]),
                ]
            ),
            ProgramFactory(),
        ]
        mock_get_programs.return_value = data

        self._create_enrollments(course_run_key)
        meter = ProgramProgressMeter(self.site, self.user)

        self._attach_detail_url(data)
        program = data[0]
        self.assertEqual(meter.engaged_programs, [program])
        self._assert_progress(
            meter,
            ProgressFactory(uuid=program['uuid'], in_progress=1, grades={course_run_key: 0.0})
        )
        self.assertEqual(meter.completed_programs, [])
Exemple #4
0
    def test_course_grade_results(self, mock_get_programs):
        grade_percent = .8
        with mock_passing_grade(percent=grade_percent):
            course_run_key = generate_course_run_key()
            data = [
                ProgramFactory(
                    courses=[
                        CourseFactory(course_runs=[
                            CourseRunFactory(key=course_run_key),
                        ]),
                    ]
                )
            ]
            mock_get_programs.return_value = data

            self._create_enrollments(course_run_key)

            meter = ProgramProgressMeter(self.site, self.user)

            program = data[0]
            expected = [
                ProgressFactory(
                    uuid=program['uuid'],
                    completed=[],
                    in_progress=[program['courses'][0]],
                    not_started=[],
                    grades={course_run_key: grade_percent},
                )
            ]

            self.assertEqual(meter.progress(count_only=False), expected)
Exemple #5
0
    def test_mutiple_program_engagement(self, mock_get_programs):
        """
        Verify that correct programs are returned in the correct order when the
        user is enrolled in course runs appearing in programs.
        """
        newer_course_run_key, older_course_run_key = (
            generate_course_run_key() for __ in range(2))
        data = [
            ProgramFactory(courses=[
                CourseFactory(course_runs=[
                    CourseRunFactory(key=newer_course_run_key),
                ]),
            ]),
            ProgramFactory(courses=[
                CourseFactory(course_runs=[
                    CourseRunFactory(key=older_course_run_key),
                ]),
            ]),
            ProgramFactory(),
        ]
        mock_get_programs.return_value = data

        # The creation time of the enrollments matters to the test. We want
        # the first_course_run_key to represent the newest enrollment.
        self._create_enrollments(older_course_run_key, newer_course_run_key)
        meter = ProgramProgressMeter(self.user)

        self._attach_detail_url(data)
        programs = data[:2]
        self.assertEqual(meter.engaged_programs, programs)
        self._assert_progress(
            meter,
            *(ProgressFactory(uuid=program['uuid'], in_progress=1)
              for program in programs))
        self.assertEqual(meter.completed_programs, [])
Exemple #6
0
    def test_no_id_professional_in_progress(self, mock_get_programs):
        """
        Verify that the progress meter treats no-id-professional enrollments
        as professional.
        """
        course_run_key = generate_course_run_key()
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key, type=CourseMode.PROFESSIONAL),
                    ]),
                ]
            )
        ]
        mock_get_programs.return_value = data

        CourseEnrollmentFactory(
            user=self.user, course_id=course_run_key,
            mode=CourseMode.NO_ID_PROFESSIONAL_MODE
        )

        meter = ProgramProgressMeter(self.site, self.user)

        program = data[0]
        expected = [
            ProgressFactory(
                uuid=program['uuid'],
                completed=[],
                in_progress=[program['courses'][0]],
                not_started=[]
            )
        ]

        self.assertEqual(meter.progress(count_only=False), expected)
Exemple #7
0
    def test_course_progress(self, mock_get_programs):
        """
        Verify that the progress meter can represent progress in terms of
        serialized courses.
        """
        course_run_key = generate_course_run_key()
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key),
                    ]),
                ]
            )
        ]
        mock_get_programs.return_value = data

        self._create_enrollments(course_run_key)

        meter = ProgramProgressMeter(self.site, self.user)

        program = data[0]
        expected = [
            ProgressFactory(
                uuid=program['uuid'],
                completed=[],
                in_progress=[program['courses'][0]],
                not_started=[],
                grades={course_run_key: 0.0},
            )
        ]

        self.assertEqual(meter.progress(count_only=False), expected)
Exemple #8
0
    def test_nonverified_course_run_completion(self, mock_completed_course_runs, mock_get_programs):
        """
        Course runs aren't necessarily of type verified. Verify that a program can
        still be completed when this is the case.
        """
        course_run_key = generate_course_run_key()
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key, type='honor'),
                        CourseRunFactory(),
                    ]),
                ]
            ),
            ProgramFactory(),
        ]
        mock_get_programs.return_value = data

        self._create_enrollments(course_run_key)
        mock_completed_course_runs.return_value = [
            {'course_run_id': course_run_key, 'type': MODES.honor},
        ]
        meter = ProgramProgressMeter(self.site, self.user)

        program, program_uuid = data[0], data[0]['uuid']
        self._assert_progress(
            meter,
            ProgressFactory(uuid=program_uuid, completed=1, grades={course_run_key: 0.0})
        )
        self.assertEqual(meter.completed_programs, [program_uuid])
Exemple #9
0
    def test_course_grade_results(self, mock_get_programs):
        grade_percent = .8
        with mock_passing_grade(percent=grade_percent):
            course_run_key = generate_course_run_key()
            data = [
                ProgramFactory(
                    courses=[
                        CourseFactory(course_runs=[
                            CourseRunFactory(key=course_run_key),
                        ]),
                    ]
                )
            ]
            mock_get_programs.return_value = data

            self._create_enrollments(course_run_key)

            meter = ProgramProgressMeter(self.site, self.user)

            program = data[0]
            expected = [
                ProgressFactory(
                    uuid=program['uuid'],
                    completed=[],
                    in_progress=[program['courses'][0]],
                    not_started=[],
                    grades={course_run_key: grade_percent},
                )
            ]

            self.assertEqual(meter.progress(count_only=False), expected)
 def setUp(self):
     """
     Set up test data
     """
     super(ProgramCourseEnrollmentModelTests, self).setUp()
     self.user = UserFactory.create()
     self.program_uuid = uuid4()
     self.program_enrollment = ProgramEnrollment.objects.create(
         user=self.user,
         external_user_key='abc',
         program_uuid=self.program_uuid,
         curriculum_uuid=uuid4(),
         status='enrolled'
     )
     self.course_key = CourseKey.from_string(generate_course_run_key())
     self.course_enrollment = CourseEnrollmentFactory.create(
         course_id=self.course_key,
         user=self.user,
     )
     self.program_course_enrollment = ProgramCourseEnrollment.objects.create(
         program_enrollment=self.program_enrollment,
         course_key=self.course_key,
         course_enrollment=self.course_enrollment,
         status="active"
     )
Exemple #11
0
    def test_nonverified_course_run_completion(self, mock_completed_course_runs, mock_get_programs):
        """
        Course runs aren't necessarily of type verified. Verify that a program can
        still be completed when this is the case.
        """
        course_run_key = generate_course_run_key()
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key, type='honor'),
                        CourseRunFactory(),
                    ]),
                ]
            ),
            ProgramFactory(),
        ]
        mock_get_programs.return_value = data

        self._create_enrollments(course_run_key)
        mock_completed_course_runs.return_value = [
            {'course_run_id': course_run_key, 'type': MODES.honor},
        ]
        meter = ProgramProgressMeter(self.user)

        program, program_uuid = data[0], data[0]['uuid']
        self._assert_progress(
            meter,
            ProgressFactory(uuid=program_uuid, completed=1)
        )
        self.assertEqual(meter.completed_programs, [program_uuid])
Exemple #12
0
    def test_no_id_professional_in_progress(self, mock_get_programs):
        """
        Verify that the progress meter treats no-id-professional enrollments
        as professional.
        """
        course_run_key = generate_course_run_key()
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key, type=CourseMode.PROFESSIONAL),
                    ]),
                ]
            )
        ]
        mock_get_programs.return_value = data

        CourseEnrollmentFactory(
            user=self.user, course_id=course_run_key,
            mode=CourseMode.NO_ID_PROFESSIONAL_MODE
        )

        meter = ProgramProgressMeter(self.user)

        program = data[0]
        expected = [
            ProgressFactory(
                uuid=program['uuid'],
                completed=[],
                in_progress=[program['courses'][0]],
                not_started=[]
            )
        ]

        self.assertEqual(meter.progress(count_only=False), expected)
Exemple #13
0
    def test_no_programs(self, mock_get_programs):
        """Verify behavior when enrollments exist, but no matching programs do."""
        mock_get_programs.return_value = []

        course_run_id = generate_course_run_key()
        self._create_enrollments(course_run_id)
        meter = ProgramProgressMeter(self.user)

        self.assertEqual(meter.engaged_programs, [])
        self._assert_progress(meter)
        self.assertEqual(meter.completed_programs, [])
Exemple #14
0
    def test_no_programs(self, mock_get_programs):
        """Verify behavior when enrollments exist, but no matching programs do."""
        mock_get_programs.return_value = []

        course_run_id = generate_course_run_key()
        self._create_enrollments(course_run_id)
        meter = ProgramProgressMeter(self.site, self.user)

        self.assertEqual(meter.engaged_programs, [])
        self._assert_progress(meter)
        self.assertEqual(meter.completed_programs, [])
    def test_unrelated_program_not_listed(self, mock_get_programs):
        """Verify that unrelated programs don't appear in the listing."""
        nonexistent_course_run_id = generate_course_run_key()

        course_run = CourseRunFactory(key=nonexistent_course_run_id)
        course = CatalogCourseFactory(course_runs=[course_run])
        unrelated_program = ProgramFactory(courses=[course])

        mock_get_programs.return_value = self.programs + [unrelated_program]

        response = self.client.get(self.url)
        self.assert_related_programs(response)
        self.assertNotContains(response, unrelated_program['title'])
Exemple #16
0
    def test_unrelated_program_not_listed(self, mock_get_programs):
        """Verify that unrelated programs don't appear in the listing."""
        nonexistent_course_run_id = generate_course_run_key()

        course_run = CourseRunFactory(key=nonexistent_course_run_id)
        course = CatalogCourseFactory(course_runs=[course_run])
        unrelated_program = ProgramFactory(courses=[course])

        mock_get_programs.return_value = self.programs + [unrelated_program]

        response = self.client.get(self.url)
        self.assert_related_programs(response)
        self.assertNotContains(response, unrelated_program['title'])
Exemple #17
0
    def test_shared_enrollment_engagement(self, mock_get_programs):
        """
        Verify that correct programs are returned when the user is enrolled in a
        single course run appearing in multiple programs.
        """
        shared_course_run_key, solo_course_run_key = (generate_course_run_key() for __ in range(2))

        batch = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=shared_course_run_key),
                    ]),
                ]
            )
            for __ in range(2)
        ]

        joint_programs = sorted(batch, key=lambda program: program['title'])
        data = joint_programs + [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=solo_course_run_key),
                    ]),
                ]
            ),
            ProgramFactory(),
        ]

        mock_get_programs.return_value = data

        # Enrollment for the shared course run created last (most recently).
        self._create_enrollments(solo_course_run_key, shared_course_run_key)
        meter = ProgramProgressMeter(self.site, self.user)

        self._attach_detail_url(data)
        programs = data[:3]
        self.assertEqual(meter.engaged_programs, programs)

        grades = {
            solo_course_run_key: 0.0,
            shared_course_run_key: 0.0,
        }

        self._assert_progress(
            meter,
            *(ProgressFactory(uuid=program['uuid'], in_progress=1, grades=grades) for program in programs)
        )
        self.assertEqual(meter.completed_programs, [])
Exemple #18
0
    def test_shared_enrollment_engagement(self, mock_get_programs):
        """
        Verify that correct programs are returned when the user is enrolled in a
        single course run appearing in multiple programs.
        """
        shared_course_run_key, solo_course_run_key = (generate_course_run_key() for __ in range(2))

        batch = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=shared_course_run_key),
                    ]),
                ]
            )
            for __ in range(2)
        ]

        joint_programs = sorted(batch, key=lambda program: program['title'])
        data = joint_programs + [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=solo_course_run_key),
                    ]),
                ]
            ),
            ProgramFactory(),
        ]

        mock_get_programs.return_value = data

        # Enrollment for the shared course run created last (most recently).
        self._create_enrollments(solo_course_run_key, shared_course_run_key)
        meter = ProgramProgressMeter(self.site, self.user)

        self._attach_detail_url(data)
        programs = data[:3]
        self.assertEqual(meter.engaged_programs, programs)

        grades = {
            solo_course_run_key: 0.0,
            shared_course_run_key: 0.0,
        }

        self._assert_progress(
            meter,
            *(ProgressFactory(uuid=program['uuid'], in_progress=1, grades=grades) for program in programs)
        )
        self.assertEqual(meter.completed_programs, [])
Exemple #19
0
 def test_credit_course_counted_complete_for_verified(self, mock_completed_course_runs, mock_get_programs):
     """
     Verify that 'credit' course certificate type are treated as if they were
     "verified" when checking for course completion status.
     """
     course_run_key = generate_course_run_key()
     course = CourseFactory(course_runs=[
         CourseRunFactory(key=course_run_key, type='credit'),
     ])
     program = ProgramFactory(courses=[course])
     mock_get_programs.return_value = [program]
     self._create_enrollments(course_run_key)
     meter = ProgramProgressMeter(self.site, self.user)
     mock_completed_course_runs.return_value = [{'course_run_id': course_run_key, 'type': 'verified'}]
     self.assertEqual(meter._is_course_complete(course), True)
Exemple #20
0
 def test_credit_course_counted_complete_for_verified(self, mock_completed_course_runs, mock_get_programs):
     """
     Verify that 'credit' course certificate type are treated as if they were
     "verified" when checking for course completion status.
     """
     course_run_key = generate_course_run_key()
     course = CourseFactory(course_runs=[
         CourseRunFactory(key=course_run_key, type='credit'),
     ])
     program = ProgramFactory(courses=[course])
     mock_get_programs.return_value = [program]
     self._create_enrollments(course_run_key)
     meter = ProgramProgressMeter(self.user)
     mock_completed_course_runs.return_value = [{'course_run_id': course_run_key, 'type': 'verified'}]
     self.assertEqual(meter._is_course_complete(course), True)
Exemple #21
0
 def setUp(self):
     """
     Set up test data
     """
     super(ProgramCourseEnrollmentModelTests, self).setUp()
     RequestCache.clear_all_namespaces()
     self.user = UserFactory.create()
     self.program_uuid = uuid4()
     self.program_enrollment = ProgramEnrollment.objects.create(
         user=self.user,
         external_user_key='abc',
         program_uuid=self.program_uuid,
         curriculum_uuid=uuid4(),
         status='enrolled')
     self.course_key = CourseKey.from_string(generate_course_run_key())
     CourseOverviewFactory(id=self.course_key)
Exemple #22
0
 def setUp(self):
     """
     Set up test data
     """
     super(ProgramCourseEnrollmentModelTests, self).setUp()
     RequestCache.clear_all_namespaces()
     self.user = UserFactory.create()
     self.program_uuid = uuid4()
     self.program_enrollment = ProgramEnrollment.objects.create(
         user=self.user,
         external_user_key='abc',
         program_uuid=self.program_uuid,
         curriculum_uuid=uuid4(),
         status='enrolled'
     )
     self.course_key = CourseKey.from_string(generate_course_run_key())
     CourseOverviewFactory(id=self.course_key)
Exemple #23
0
    def test_mutiple_program_engagement(self, mock_get_programs):
        """
        Verify that correct programs are returned in the correct order when the
        user is enrolled in course runs appearing in programs.
        """
        newer_course_run_key, older_course_run_key = (generate_course_run_key() for __ in range(2))
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=newer_course_run_key),
                    ]),
                ]
            ),
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=older_course_run_key),
                    ]),
                ]
            ),
            ProgramFactory(),
        ]
        mock_get_programs.return_value = data

        # The creation time of the enrollments matters to the test. We want
        # the first_course_run_key to represent the newest enrollment.
        self._create_enrollments(older_course_run_key, newer_course_run_key)
        meter = ProgramProgressMeter(self.site, self.user)

        self._attach_detail_url(data)
        programs = data[:2]
        self.assertEqual(meter.engaged_programs, programs)

        grades = {
            newer_course_run_key: 0.0,
            older_course_run_key: 0.0,
        }
        self._assert_progress(
            meter,
            *(ProgressFactory(uuid=program['uuid'], in_progress=1, grades=grades) for program in programs)
        )
        self.assertEqual(meter.completed_programs, [])
Exemple #24
0
    def test_in_progress_course_upgrade_deadline_check(self, offset,
                                                       mock_get_programs):
        """
        Verify that if the user's enrollment is not of the same type as the course run,
        the course will only count as in progress if there is another available seat with
        the right type for which the upgrade deadline has not passed.
        """
        course_run_key = generate_course_run_key()
        now = datetime.datetime.now(utc)
        upgrade_deadline = None if not offset else str(now +
                                                       datetime.timedelta(
                                                           days=offset))
        required_seat = SeatFactory(type='verified',
                                    upgrade_deadline=upgrade_deadline)
        enrolled_seat = SeatFactory(type='audit')
        seats = [required_seat, enrolled_seat]

        data = [
            ProgramFactory(courses=[
                CourseFactory(course_runs=[
                    CourseRunFactory(
                        key=course_run_key, type='verified', seats=seats),
                ]),
            ])
        ]
        mock_get_programs.return_value = data

        CourseEnrollmentFactory(user=self.user,
                                course_id=course_run_key,
                                mode='audit')

        meter = ProgramProgressMeter(self.site, self.user)

        program = data[0]
        expected = [
            ProgressFactory(uuid=program['uuid'],
                            completed=0,
                            in_progress=1 if offset in [None, 1] else 0,
                            not_started=1 if offset in [-1] else 0)
        ]

        self.assertEqual(meter.progress(count_only=True), expected)
Exemple #25
0
    def test_in_progress_course_upgrade_deadline_check(self, offset, mock_get_programs):
        """
        Verify that if the user's enrollment is not of the same type as the course run,
        the course will only count as in progress if there is another available seat with
        the right type for which the upgrade deadline has not passed.
        """
        course_run_key = generate_course_run_key()
        now = datetime.datetime.now(utc)
        upgrade_deadline = None if not offset else str(now + datetime.timedelta(days=offset))
        required_seat = SeatFactory(type='verified', upgrade_deadline=upgrade_deadline)
        enrolled_seat = SeatFactory(type='audit')
        seats = [required_seat, enrolled_seat]

        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key, type='verified', seats=seats),
                    ]),
                ]
            )
        ]
        mock_get_programs.return_value = data

        CourseEnrollmentFactory(user=self.user, course_id=course_run_key, mode='audit')

        meter = ProgramProgressMeter(self.site, self.user)

        program = data[0]
        expected = [
            ProgressFactory(
                uuid=program['uuid'],
                completed=0,
                in_progress=1 if offset in [None, 1] else 0,
                not_started=1 if offset in [-1] else 0,
                grades={course_run_key: 0.0},
            )
        ]

        self.assertEqual(meter.progress(count_only=True), expected)
    def test_in_progress_course_upgrade_deadline_check(self, modifier, mock_get_programs):
        """
        Verify that if the user's enrollment is not of the same type as the course run,
        the course will only count as in progress if there is another available seat with
        the right type, where the upgrade deadline has not expired.
        """
        course_run_key = generate_course_run_key()
        now = datetime.datetime.now(utc)
        date_modifier = modifier * datetime.timedelta(days=1)
        seat_with_upgrade_deadline = SeatFactory(type='test', upgrade_deadline=str(now + date_modifier))
        enrolled_seat = SeatFactory(type='verified')
        seats = [seat_with_upgrade_deadline, enrolled_seat]

        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key, type='test', seats=seats),
                    ]),
                ]
            )
        ]
        mock_get_programs.return_value = data

        self._create_enrollments(course_run_key)

        meter = ProgramProgressMeter(self.user)

        program = data[0]
        expected = [
            ProgressFactory(
                uuid=program['uuid'],
                completed=0,
                in_progress=1 if modifier == 1 else 0,
                not_started=1 if modifier == -1 else 0
            )
        ]
        self.assertEqual(meter.progress(count_only=True), expected)
 def setUp(self):
     """
     Set up test data
     """
     super(ProgramCourseEnrollmentModelTests, self).setUp()
     self.user = UserFactory.create()
     self.program_uuid = uuid4()
     self.program_enrollment = ProgramEnrollment.objects.create(
         user=self.user,
         external_user_key='abc',
         program_uuid=self.program_uuid,
         curriculum_uuid=uuid4(),
         status='enrolled')
     self.course_key = CourseKey.from_string(generate_course_run_key())
     self.course_enrollment = CourseEnrollmentFactory.create(
         course_id=self.course_key,
         user=self.user,
     )
     self.program_course_enrollment = ProgramCourseEnrollment.objects.create(
         program_enrollment=self.program_enrollment,
         course_key=self.course_key,
         course_enrollment=self.course_enrollment,
         status="active")
Exemple #28
0
    def test_simulate_progress(self, mock_completed_course_runs, mock_get_programs):
        """Simulate the entirety of a user's progress through a program."""
        first_course_run_key, second_course_run_key = (generate_course_run_key() for __ in range(2))
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=first_course_run_key),
                    ]),
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=second_course_run_key),
                    ]),
                ]
            ),
            ProgramFactory(),
        ]
        mock_get_programs.return_value = data

        # No enrollments, no programs in progress.
        meter = ProgramProgressMeter(self.site, self.user)
        self._assert_progress(meter)
        self.assertEqual(meter.completed_programs, [])

        # One enrollment, one program in progress.
        self._create_enrollments(first_course_run_key)
        meter = ProgramProgressMeter(self.site, self.user)
        program, program_uuid = data[0], data[0]['uuid']
        self._assert_progress(
            meter,
            ProgressFactory(uuid=program_uuid, in_progress=1, not_started=1, grades={first_course_run_key: 0.0})
        )
        self.assertEqual(meter.completed_programs, [])

        # Two enrollments, all courses in progress.
        self._create_enrollments(second_course_run_key)
        meter = ProgramProgressMeter(self.site, self.user)
        self._assert_progress(
            meter,
            ProgressFactory(
                uuid=program_uuid,
                in_progress=2,
                grades={
                    first_course_run_key: 0.0,
                    second_course_run_key: 0.0,
                },
            )
        )
        self.assertEqual(meter.completed_programs, [])

        # One valid certificate earned, one course complete.
        mock_completed_course_runs.return_value = [
            {'course_run_id': first_course_run_key, 'type': MODES.verified},
        ]
        meter = ProgramProgressMeter(self.site, self.user)
        self._assert_progress(
            meter,
            ProgressFactory(
                uuid=program_uuid,
                completed=1,
                in_progress=1,
                grades={
                    first_course_run_key: 0.0,
                    second_course_run_key: 0.0,
                }
            )
        )
        self.assertEqual(meter.completed_programs, [])

        # Invalid certificate earned, still one course to complete.
        mock_completed_course_runs.return_value = [
            {'course_run_id': first_course_run_key, 'type': MODES.verified},
            {'course_run_id': second_course_run_key, 'type': MODES.honor},
        ]
        meter = ProgramProgressMeter(self.site, self.user)
        self._assert_progress(
            meter,
            ProgressFactory(
                uuid=program_uuid,
                completed=1,
                in_progress=1,
                grades={
                    first_course_run_key: 0.0,
                    second_course_run_key: 0.0,
                }
            )
        )
        self.assertEqual(meter.completed_programs, [])

        # Second valid certificate obtained, all courses complete.
        mock_completed_course_runs.return_value = [
            {'course_run_id': first_course_run_key, 'type': MODES.verified},
            {'course_run_id': second_course_run_key, 'type': MODES.verified},
        ]
        meter = ProgramProgressMeter(self.site, self.user)
        self._assert_progress(
            meter,
            ProgressFactory(
                uuid=program_uuid,
                completed=2,
                grades={
                    first_course_run_key: 0.0,
                    second_course_run_key: 0.0,
                }
            )
        )
        self.assertEqual(meter.completed_programs, [program_uuid])
class BackpopulateProgramCredentialsTests(CatalogIntegrationMixin, CredentialsApiConfigMixin, TestCase):
    """Tests for the backpopulate_program_credentials management command."""
    course_run_key, alternate_course_run_key = (generate_course_run_key() for __ in range(2))

    def setUp(self):
        super(BackpopulateProgramCredentialsTests, self).setUp()

        self.alice = UserFactory()
        self.bob = UserFactory()

        # Disable certification to prevent the task from being triggered when
        # setting up test data (i.e., certificates with a passing status), thereby
        # skewing mock call counts.
        self.create_credentials_config(enable_learner_issuance=False)

        catalog_integration = self.create_catalog_integration()
        UserFactory(username=catalog_integration.service_username)

    @ddt.data(True, False)
    def test_handle(self, commit, mock_task, mock_get_programs):
        """
        Verify that relevant tasks are only enqueued when the commit option is passed.
        """
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=self.course_run_key),
                    ]),
                ]
            ),
        ]
        mock_get_programs.return_value = data

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        GeneratedCertificateFactory(
            user=self.bob,
            course_id=self.alternate_course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        call_command('backpopulate_program_credentials', commit=commit)

        if commit:
            mock_task.assert_called_once_with(self.alice.username)
        else:
            mock_task.assert_not_called()

    @ddt.data(
        [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key),
                    ]),
                ]
            ),
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=alternate_course_run_key),
                    ]),
                ]
            ),
        ],
        [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key),
                    ]),
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=alternate_course_run_key),
                    ]),
                ]
            ),
        ],
        [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key),
                        CourseRunFactory(key=alternate_course_run_key),
                    ]),
                ]
            ),
        ],
    )
    def test_handle_flatten(self, data, mock_task, mock_get_programs):
        """Verify that program structures are flattened correctly."""
        mock_get_programs.return_value = data

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        GeneratedCertificateFactory(
            user=self.bob,
            course_id=self.alternate_course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        call_command('backpopulate_program_credentials', commit=True)

        calls = [
            mock.call(self.alice.username),
            mock.call(self.bob.username)
        ]
        mock_task.assert_has_calls(calls, any_order=True)

    def test_handle_username_dedup(self, mock_task, mock_get_programs):
        """
        Verify that only one task is enqueued for a user with multiple eligible
        course run certificates.
        """
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=self.course_run_key),
                        CourseRunFactory(key=self.alternate_course_run_key),
                    ]),
                ]
            ),
        ]
        mock_get_programs.return_value = data

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.alternate_course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        call_command('backpopulate_program_credentials', commit=True)

        mock_task.assert_called_once_with(self.alice.username)

    def test_handle_mode_slugs(self, mock_task, mock_get_programs):
        """
        Verify that course run types are taken into account when identifying
        qualifying course run certificates.
        """
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=self.course_run_key, type='honor'),
                    ]),
                ]
            ),
        ]
        mock_get_programs.return_value = data

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.course_run_key,
            mode=MODES.honor,
            status=CertificateStatuses.downloadable,
        )

        GeneratedCertificateFactory(
            user=self.bob,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        call_command('backpopulate_program_credentials', commit=True)

        mock_task.assert_called_once_with(self.alice.username)

    def test_handle_passing_status(self, mock_task, mock_get_programs):
        """
        Verify that only course run certificates with a passing status are selected.
        """
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=self.course_run_key),
                    ]),
                ]
            ),
        ]
        mock_get_programs.return_value = data

        passing_status = CertificateStatuses.downloadable
        failing_status = CertificateStatuses.notpassing

        self.assertIn(passing_status, CertificateStatuses.PASSED_STATUSES)
        self.assertNotIn(failing_status, CertificateStatuses.PASSED_STATUSES)

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=passing_status,
        )

        GeneratedCertificateFactory(
            user=self.bob,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=failing_status,
        )

        call_command('backpopulate_program_credentials', commit=True)

        mock_task.assert_called_once_with(self.alice.username)

    @mock.patch(COMMAND_MODULE + '.logger.exception')
    def test_handle_enqueue_failure(self, mock_log, mock_task, mock_get_programs):
        """Verify that failure to enqueue a task doesn't halt execution."""
        def side_effect(username):
            """Simulate failure to enqueue a task."""
            if username == self.alice.username:
                raise Exception

        mock_task.side_effect = side_effect

        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=self.course_run_key),
                    ]),
                ]
            ),
        ]
        mock_get_programs.return_value = data

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        GeneratedCertificateFactory(
            user=self.bob,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        call_command('backpopulate_program_credentials', commit=True)

        self.assertTrue(mock_log.called)

        calls = [
            mock.call(self.alice.username),
            mock.call(self.bob.username)
        ]
        mock_task.assert_has_calls(calls, any_order=True)
Exemple #30
0
class BackpopulateProgramCredentialsTests(CatalogIntegrationMixin,
                                          CredentialsApiConfigMixin, TestCase):
    """Tests for the backpopulate_program_credentials management command."""
    course_run_key, alternate_course_run_key, course_run_key_no_course_overview = (
        generate_course_run_key() for __ in range(3))
    # Constants for the _get_programs_data hierarchy types used in test_flatten()
    SEPARATE_PROGRAMS = 'separate_programs'
    SEPARATE_COURSES = 'separate_courses'
    SAME_COURSE = 'same_course'

    def setUp(self):
        super(BackpopulateProgramCredentialsTests, self).setUp()  # lint-amnesty, pylint: disable=super-with-arguments

        self.alice = UserFactory()
        self.bob = UserFactory()

        # We need CourseOverview instances to exist for the test courses
        CourseOverviewFactory(id=CourseKey.from_string(self.course_run_key))
        CourseOverviewFactory(
            id=CourseKey.from_string(self.alternate_course_run_key))

        # Disable certification to prevent the task from being triggered when
        # setting up test data (i.e., certificates with a passing status), thereby
        # skewing mock call counts.
        self.create_credentials_config(enable_learner_issuance=False)

        catalog_integration = self.create_catalog_integration()
        UserFactory(username=catalog_integration.service_username)

    def _get_programs_data(self, hierarchy_type):
        """
        Generate a mock response for get_programs() with the given type of
        course hierarchy.  Dramatically simplifies (and makes consistent
        between test runs) the ddt-generated test_flatten methods.
        """
        if hierarchy_type == self.SEPARATE_PROGRAMS:
            return [
                ProgramFactory(courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=self.course_run_key),
                    ]),
                ]),
                ProgramFactory(courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=self.alternate_course_run_key),
                    ]),
                ]),
            ]
        elif hierarchy_type == self.SEPARATE_COURSES:
            return [
                ProgramFactory(courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=self.course_run_key),
                    ]),
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=self.alternate_course_run_key),
                    ]),
                ]),
            ]
        else:  # SAME_COURSE
            return [
                ProgramFactory(courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=self.course_run_key),
                        CourseRunFactory(key=self.alternate_course_run_key),
                    ]),
                ]),
            ]

    @ddt.data(True, False)
    def test_handle(self, commit, mock_task, mock_get_programs):
        """
        Verify that relevant tasks are only enqueued when the commit option is passed.
        """
        data = [
            ProgramFactory(courses=[
                CourseFactory(course_runs=[
                    CourseRunFactory(key=self.course_run_key),
                ]),
            ]),
        ]
        mock_get_programs.return_value = data

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        GeneratedCertificateFactory(
            user=self.bob,
            course_id=self.alternate_course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        call_command('backpopulate_program_credentials', commit=commit)

        if commit:
            mock_task.assert_called_once_with(self.alice.username)
        else:
            mock_task.assert_not_called()

    def test_handle_professional(self, mock_task, mock_get_programs):
        """ Verify the task can handle both professional and no-id-professional modes. """
        mock_get_programs.return_value = [
            ProgramFactory(courses=[
                CourseFactory(course_runs=[
                    CourseRunFactory(key=self.course_run_key,
                                     type='professional'),
                ]),
            ]),
        ]

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.course_run_key,
            mode=CourseMode.PROFESSIONAL,
            status=CertificateStatuses.downloadable,
        )

        GeneratedCertificateFactory(
            user=self.bob,
            course_id=self.course_run_key,
            mode=CourseMode.NO_ID_PROFESSIONAL_MODE,
            status=CertificateStatuses.downloadable,
        )

        call_command('backpopulate_program_credentials', commit=True)

        # The task should be called for both users since professional and no-id-professional are equivalent.
        mock_task.assert_has_calls(
            [mock.call(self.alice.username),
             mock.call(self.bob.username)],
            any_order=True)

    @ddt.data(SEPARATE_PROGRAMS, SEPARATE_COURSES, SAME_COURSE)
    def test_handle_flatten(self, hierarchy_type, mock_task,
                            mock_get_programs):
        """Verify that program structures are flattened correctly."""
        mock_get_programs.return_value = self._get_programs_data(
            hierarchy_type)

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        GeneratedCertificateFactory(
            user=self.bob,
            course_id=self.alternate_course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        call_command('backpopulate_program_credentials', commit=True)

        calls = [mock.call(self.alice.username), mock.call(self.bob.username)]
        mock_task.assert_has_calls(calls, any_order=True)

    def test_handle_username_dedup(self, mock_task, mock_get_programs):
        """
        Verify that only one task is enqueued for a user with multiple eligible
        course run certificates.
        """
        data = [
            ProgramFactory(courses=[
                CourseFactory(course_runs=[
                    CourseRunFactory(key=self.course_run_key),
                    CourseRunFactory(key=self.alternate_course_run_key),
                ]),
            ]),
        ]
        mock_get_programs.return_value = data

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.alternate_course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        call_command('backpopulate_program_credentials', commit=True)

        mock_task.assert_called_once_with(self.alice.username)

    def test_handle_mode_slugs(self, mock_task, mock_get_programs):
        """
        Verify that course run types are taken into account when identifying
        qualifying course run certificates.
        """
        data = [
            ProgramFactory(courses=[
                CourseFactory(course_runs=[
                    CourseRunFactory(key=self.course_run_key, type='honor'),
                ]),
            ]),
        ]
        mock_get_programs.return_value = data

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.course_run_key,
            mode=MODES.honor,
            status=CertificateStatuses.downloadable,
        )

        GeneratedCertificateFactory(
            user=self.bob,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        call_command('backpopulate_program_credentials', commit=True)

        mock_task.assert_called_once_with(self.alice.username)

    def test_handle_passing_status(self, mock_task, mock_get_programs):
        """
        Verify that only course run certificates with a passing status are selected.
        """
        data = [
            ProgramFactory(courses=[
                CourseFactory(course_runs=[
                    CourseRunFactory(key=self.course_run_key),
                ]),
            ]),
        ]
        mock_get_programs.return_value = data

        passing_status = CertificateStatuses.downloadable
        failing_status = CertificateStatuses.notpassing

        self.assertIn(passing_status, CertificateStatuses.PASSED_STATUSES)
        self.assertNotIn(failing_status, CertificateStatuses.PASSED_STATUSES)

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=passing_status,
        )

        GeneratedCertificateFactory(
            user=self.bob,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=failing_status,
        )

        call_command('backpopulate_program_credentials', commit=True)

        mock_task.assert_called_once_with(self.alice.username)

    def test_handle_no_course_overview(self, mock_task, mock_get_programs):
        """
        Verify that the task is not enqueued for a user whose only certificate
        is for a course with no CourseOverview.
        """
        data = [
            ProgramFactory(courses=[
                CourseFactory(course_runs=[
                    CourseRunFactory(key=self.course_run_key),
                    CourseRunFactory(
                        key=self.course_run_key_no_course_overview),
                ]),
            ]),
        ]
        mock_get_programs.return_value = data

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )
        GeneratedCertificateFactory(
            user=self.bob,
            course_id=self.course_run_key_no_course_overview,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        call_command('backpopulate_program_credentials', commit=True)

        mock_task.assert_called_once_with(self.alice.username)

    @mock.patch(COMMAND_MODULE + '.logger.exception')
    def test_handle_enqueue_failure(self, mock_log, mock_task,
                                    mock_get_programs):
        """Verify that failure to enqueue a task doesn't halt execution."""
        def side_effect(username):
            """Simulate failure to enqueue a task."""
            if username == self.alice.username:
                raise Exception

        mock_task.side_effect = side_effect

        data = [
            ProgramFactory(courses=[
                CourseFactory(course_runs=[
                    CourseRunFactory(key=self.course_run_key),
                ]),
            ]),
        ]
        mock_get_programs.return_value = data

        GeneratedCertificateFactory(
            user=self.alice,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        GeneratedCertificateFactory(
            user=self.bob,
            course_id=self.course_run_key,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        call_command('backpopulate_program_credentials', commit=True)

        self.assertTrue(mock_log.called)

        calls = [mock.call(self.alice.username), mock.call(self.bob.username)]
        mock_task.assert_has_calls(calls, any_order=True)
Exemple #31
0
    def test_simulate_progress(self, mock_completed_course_runs, mock_get_programs):
        """Simulate the entirety of a user's progress through a program."""
        first_course_run_key, second_course_run_key = (generate_course_run_key() for __ in range(2))
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=first_course_run_key),
                    ]),
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=second_course_run_key),
                    ]),
                ]
            ),
            ProgramFactory(),
        ]
        mock_get_programs.return_value = data

        # No enrollments, no programs in progress.
        meter = ProgramProgressMeter(self.user)
        self._assert_progress(meter)
        self.assertEqual(meter.completed_programs, [])

        # One enrollment, one program in progress.
        self._create_enrollments(first_course_run_key)
        meter = ProgramProgressMeter(self.user)
        program, program_uuid = data[0], data[0]['uuid']
        self._assert_progress(
            meter,
            ProgressFactory(uuid=program_uuid, in_progress=1, not_started=1)
        )
        self.assertEqual(meter.completed_programs, [])

        # Two enrollments, all courses in progress.
        self._create_enrollments(second_course_run_key)
        meter = ProgramProgressMeter(self.user)
        self._assert_progress(
            meter,
            ProgressFactory(uuid=program_uuid, in_progress=2)
        )
        self.assertEqual(meter.completed_programs, [])

        # One valid certificate earned, one course complete.
        mock_completed_course_runs.return_value = [
            {'course_run_id': first_course_run_key, 'type': MODES.verified},
        ]
        meter = ProgramProgressMeter(self.user)
        self._assert_progress(
            meter,
            ProgressFactory(uuid=program_uuid, completed=1, in_progress=1)
        )
        self.assertEqual(meter.completed_programs, [])

        # Invalid certificate earned, still one course to complete.
        mock_completed_course_runs.return_value = [
            {'course_run_id': first_course_run_key, 'type': MODES.verified},
            {'course_run_id': second_course_run_key, 'type': MODES.honor},
        ]
        meter = ProgramProgressMeter(self.user)
        self._assert_progress(
            meter,
            ProgressFactory(uuid=program_uuid, completed=1, in_progress=1)
        )
        self.assertEqual(meter.completed_programs, [])

        # Second valid certificate obtained, all courses complete.
        mock_completed_course_runs.return_value = [
            {'course_run_id': first_course_run_key, 'type': MODES.verified},
            {'course_run_id': second_course_run_key, 'type': MODES.verified},
        ]
        meter = ProgramProgressMeter(self.user)
        self._assert_progress(
            meter,
            ProgressFactory(uuid=program_uuid, completed=2)
        )
        self.assertEqual(meter.completed_programs, [program_uuid])