def two_no_show_exam_attempts(self): """Passed and later failed course, and two exam attempts""" self.make_fa_program_enrollment(FinancialAidStatus.AUTO_APPROVED) course = Course.objects.get(title='Digital Learning 200') course_run = CourseRunFactory(course=course, edx_course_key='course-passed') call_command( "alter_data", 'set_to_passed', '--username', 'staff', '--course-run-key', course_run.edx_course_key ) ExamProfileFactory.create(status='success', profile=self.user.profile) # run 1 exam_run = ExamRunFactory.create(course=course, eligibility_past=True, scheduling_past=True) ExamAuthorizationFactory.create( user=self.user, course=course, exam_run=exam_run, status='success', exam_taken=True, exam_no_show=True ) # run 2 exam_run = ExamRunFactory.create(course=course, eligibility_past=True, scheduling_past=True) ExamAuthorizationFactory.create( user=self.user, course=course, exam_run=exam_run, status='success', exam_taken=True, exam_no_show=True ) # another offered course_run = CourseRunFactory.create(course=course, edx_course_key='course-enrollable') call_command( "alter_data", 'set_to_offered', '--username', 'staff', '--course-run-key', course_run.edx_course_key ) course_run = CourseRunFactory.create(course=course, edx_course_key='course-failed') call_command( "alter_data", 'set_to_failed', '--username', 'staff', '--course-run-key', course_run.edx_course_key, '--audit', )
def test_count_courses_mixed_fa(self): """ Test count_courses_passed with mixed course-exam configuration """ mmtrack = MMTrack( user=self.user, program=self.program_financial_aid, edx_user_data=self.cached_edx_user_data ) # this is course with exam run and the user has CombinedFinalGrade for it course_with_exam_1 = CourseFactory.create(program=self.program_financial_aid) ExamRunFactory.create(course=course_with_exam_1, date_grades_available=now_in_utc()-timedelta(weeks=1)) CombinedFinalGrade.objects.create(user=self.user, course=course_with_exam_1, grade=0.7) # create course with exam run the user did not pass ExamRunFactory.create( course__program=self.program_financial_aid, date_grades_available=now_in_utc() - timedelta(weeks=1) ) # another course with no exam FinalGradeFactory.create( user=self.user, course_run=self.crun_fa, passed=True ) assert mmtrack.count_courses_passed() == 2
def test_authorize_exam_runs(self, authorized, authorize_for_latest_passed_course_mock): """Test authorize_exam_runs()""" program, _ = create_program() course = program.course_set.first() enrollment = ProgramEnrollmentFactory.create(program=program) current_run = ExamRunFactory.create(course=course, authorized=authorized) past_run = ExamRunFactory.create(course=course, scheduling_future=True, authorized=authorized) future_run = ExamRunFactory.create(course=course, scheduling_past=True, authorized=authorized) authorize_exam_runs() if authorized: assert authorize_for_latest_passed_course_mock.call_count == 0 else: assert authorize_for_latest_passed_course_mock.call_count == 2 authorize_for_latest_passed_course_mock.assert_any_call( enrollment.user, current_run) authorize_for_latest_passed_course_mock.assert_any_call( enrollment.user, future_run) for exam_run in (current_run, future_run): exam_run.refresh_from_db() assert exam_run.authorized is True past_run.refresh_from_db() assert past_run.authorized is False
def test_count_courses_mixed_fa(self): """ Test count_courses_passed with mixed course-exam configuration """ mmtrack = MMTrack( user=self.user, program=self.program_financial_aid, edx_user_data=self.cached_edx_user_data ) # this is course with exam run and the user has CombinedFinalGrade for it course_with_exam_1 = CourseFactory.create(program=self.program_financial_aid) ExamRunFactory.create(course=course_with_exam_1, date_grades_available=now_in_utc()-timedelta(weeks=1)) CombinedFinalGrade.objects.create(user=self.user, course=course_with_exam_1, grade=0.7) # create course with exam run the user did not pass ExamRunFactory.create( course__program=self.program_financial_aid, date_grades_available=now_in_utc() - timedelta(weeks=1) ) # another course with no exam FinalGradeFactory.create( user=self.user, course_run=self.crun_fa, passed=True ) assert mmtrack.count_courses_passed() == 2
def create_missed_payment_for_exam(self, enrollable, future_exam, current): """Passed course but missed deadline to pay to take exam""" self.make_fa_program_enrollment(FinancialAidStatus.AUTO_APPROVED) if current: call_command( "alter_data", 'set_to_enrolled', '--username', 'staff', '--course-title', 'Digital Learning 200', '--missed-deadline' ) else: call_command( "alter_data", 'set_past_run_to_passed', '--username', 'staff', '--course-title', 'Digital Learning 200', '--grade', '87', '--audit', '--missed-deadline' ) course = Course.objects.get(title='Digital Learning 200') ExamProfileFactory.create(status='success', profile=self.user.profile) ExamRunFactory.create(course=course, eligibility_past=True, scheduling_past=True) if enrollable: course_run = CourseRunFactory.create(course=course, edx_course_key='course-enrollable') call_command( "alter_data", 'set_to_offered', '--username', 'staff', '--course-run-key', course_run.edx_course_key ) if future_exam: ExamRunFactory.create( scheduling_past=False, scheduling_future=True, authorized=True, course=course )
def test_get_exam_card_status_for_edx_exams(self, profile_status, expected_status, make_exam_run, make_profile, make_auth): """ test get_exam_card_status """ now = now_in_utc() exam_run = None if make_exam_run: exam_run = ExamRunFactory.create( course=self.course, date_first_eligible=now - timedelta(weeks=1), date_last_eligible=now + timedelta(weeks=1), ) if make_profile: ExamProfileFactory.create( profile=self.user.profile, status=profile_status, ) if make_auth: ExamAuthorizationFactory.create( user=self.user, status=ExamAuthorization.STATUS_SUCCESS, exam_run=exam_run, ) mmtrack = MMTrack( user=self.user, program=self.program, edx_user_data=self.cached_edx_user_data ) assert mmtrack.get_exam_card_status() == expected_status
def test_get_overall_final_grade_for_course(self): """ Test for get_overall_final_grade_for_course to return CombinedFinalGrade for course """ mmtrack = MMTrack( user=self.user, program=self.program_financial_aid, edx_user_data=self.cached_edx_user_data ) finaid_course = self.crun_fa.course assert mmtrack.get_overall_final_grade_for_course(finaid_course) == "" FinalGradeFactory.create(user=self.user, course_run=self.crun_fa, passed=True, grade=0.8) assert mmtrack.get_overall_final_grade_for_course(finaid_course) == "80" ExamRunFactory.create(course=finaid_course) CombinedFinalGrade.objects.create(user=self.user, course=finaid_course, grade="74") assert mmtrack.get_overall_final_grade_for_course(finaid_course) == "74"
def test_get_pearson_exam_status(self, profile_status, expected_status, make_exam_run, make_profile, make_auth, log_error_called, log_mock): """ test get_pearson_exam_status """ now = now_in_utc() exam_run = None if make_exam_run: exam_run = ExamRunFactory.create( course=self.course, date_first_eligible=now - timedelta(weeks=1), date_last_eligible=now + timedelta(weeks=1), ) if make_profile: ExamProfileFactory.create( profile=self.user.profile, status=profile_status, ) if make_auth: ExamAuthorizationFactory.create( user=self.user, status=ExamAuthorization.STATUS_SUCCESS, exam_run=exam_run, ) mmtrack = MMTrack( user=self.user, program=self.program, edx_user_data=self.cached_edx_user_data ) assert mmtrack.get_pearson_exam_status() == expected_status assert log_mock.error.called is log_error_called
def test_exam_authorization_attempts_consumed(self): """ test exam_authorization when user passed and paid, but used all their attempts """ create_order(self.user, self.course_run) mmtrack = get_mmtrack(self.user, self.program) self.assertTrue(mmtrack.has_paid(self.course_run.edx_course_key)) self.assertTrue(mmtrack.has_passed_course(self.course_run.edx_course_key)) old_run = ExamRunFactory.create(course=self.course_run.course) ExamAuthorizationFactory.create_batch( ATTEMPTS_PER_PAID_RUN, exam_run=old_run, user=mmtrack.user, course=self.course_run.course, exam_taken=True, ) assert ExamAuthorization.objects.filter( user=mmtrack.user, course=self.course_run.course ).count() == 2 with self.assertRaises(ExamAuthorizationException): authorize_for_exam_run(mmtrack, self.course_run, self.exam_run) # assert no new authorizations got created assert ExamAuthorization.objects.filter( user=mmtrack.user, course=self.course_run.course ).count() == 2
def test_exam_authorization_attempts_consumed(self): """ test exam_authorization when user passed and paid, but used all their attempts """ create_order(self.user, self.course_run) mmtrack = get_mmtrack(self.user, self.program) self.assertTrue(mmtrack.has_paid(self.course_run.edx_course_key)) self.assertTrue( mmtrack.has_passed_course(self.course_run.edx_course_key)) old_run = ExamRunFactory.create(course=self.course_run.course) ExamAuthorizationFactory.create_batch( ATTEMPTS_PER_PAID_RUN, exam_run=old_run, user=mmtrack.user, course=self.course_run.course, exam_taken=True, ) assert ExamAuthorization.objects.filter( user=mmtrack.user, course=self.course_run.course).count() == 2 with self.assertRaises(ExamAuthorizationException): authorize_for_exam_run(self.user, self.course_run, self.exam_run) # assert no new authorizations got created assert ExamAuthorization.objects.filter( user=mmtrack.user, course=self.course_run.course).count() == 2
def test_get_overall_final_grade_for_course(self): """ Test for get_overall_final_grade_for_course to return CombinedFinalGrade for course """ mmtrack = MMTrack( user=self.user, program=self.program_financial_aid, edx_user_data=self.cached_edx_user_data ) finaid_course = self.crun_fa.course assert mmtrack.get_overall_final_grade_for_course(finaid_course) == "" FinalGradeFactory.create(user=self.user, course_run=self.crun_fa, passed=True, grade=0.8) assert mmtrack.get_overall_final_grade_for_course(finaid_course) == "80" ExamRunFactory.create(course=finaid_course) CombinedFinalGrade.objects.create(user=self.user, course=finaid_course, grade="74") assert mmtrack.get_overall_final_grade_for_course(finaid_course) == "74"
def test_update_exam_run(self, update_mock): """Test update_exam_run()""" exam_run = ExamRunFactory.create() update_exam_run(exam_run.id) update_exam_run(-exam_run.id) update_mock.assert_called_once_with(exam_run)
def test_update_exam_run(self, update_mock): """Test update_exam_run()""" exam_run = ExamRunFactory.create() update_exam_run(exam_run.id) update_exam_run(-exam_run.id) update_mock.assert_called_once_with(exam_run)
def setUpTestData(cls): cls.user = SocialUserFactory.create() cls.course_run = CourseRunFactory.create( course__program__financial_aid_availability=True) cls.exam_run = ExamRunFactory.create( course=cls.course_run.course, date_grades_available=now_in_utc() - timedelta(weeks=1)) cls.not_passing_final_grade = FinalGradeFactory.create( user=cls.user, course_run=cls.course_run, grade=0.5, passed=False)
def test_exam_authorization_multiple_runs(self): """Test that if the first enrollment is invalid it checks the second, but not the third""" exam_run = ExamRunFactory.create(course=self.course) mmtrack = get_mmtrack(self.user, self.program) with patch('exams.api.authorize_for_exam_run') as mock: mock.side_effect = [ExamAuthorizationException('invalid'), None, None] authorize_for_latest_passed_course(mmtrack, exam_run) assert mock.call_count == 2 for enrollment in self.final_grades[:2]: # two most recent runs mock.assert_any_call(mmtrack, enrollment.course_run, exam_run)
def test_authorize_exam_runs(self, authorized, bulk_authorize_for_exam_run_mock): """Test authorize_exam_runs()""" current_run = ExamRunFactory.create(authorized=authorized) past_run = ExamRunFactory.create(scheduling_future=True, authorized=authorized) future_run = ExamRunFactory.create(scheduling_past=True, authorized=authorized) authorize_exam_runs() if authorized: assert bulk_authorize_for_exam_run_mock.call_count == 0 else: assert bulk_authorize_for_exam_run_mock.call_count == 2 # past run shouldn't be called bulk_authorize_for_exam_run_mock.assert_any_call(current_run) bulk_authorize_for_exam_run_mock.assert_any_call(future_run) for exam_run in (current_run, future_run): exam_run.refresh_from_db() assert exam_run.authorized is True past_run.refresh_from_db() assert past_run.authorized is False
def test_bulk_authorize_for_exam_run_current(self): """Test that we do create an authorization for a current run""" exam_run = ExamRunFactory.create(course=self.course_run.course) assert ExamAuthorization.objects.filter( course=exam_run.course, ).count() == 0 bulk_authorize_for_exam_run(exam_run) assert ExamAuthorization.objects.filter( course=exam_run.course, ).count() == 1
def test_authorize_enrollment_for_exam_run(self, authorize_for_latest_passed_course_mock): """Test authorize_enrollment_for_exam_run()""" program, _ = create_program() course = program.course_set.first() enrollment_1 = ProgramEnrollmentFactory.create(program=program) enrollment_2 = ProgramEnrollmentFactory.create(program=program) exam_run = ExamRunFactory.create(course=course) authorize_enrollment_for_exam_run([enrollment_1.id, enrollment_2.id], exam_run.id) assert authorize_for_latest_passed_course_mock.call_count == 2 authorize_for_latest_passed_course_mock.assert_any_call(enrollment_1.user, exam_run) authorize_for_latest_passed_course_mock.assert_any_call(enrollment_2.user, exam_run)
def test_get_corresponding_course_run(self, weeks, has_course_run): """test get_past_recent_exam_run""" now = now_in_utc() exam_run = ExamRunFactory.create(date_first_schedulable=now + datetime.timedelta(weeks=weeks)) expected = CourseRunFactory.create(course=exam_run.course, start_date=now - datetime.timedelta(weeks=16), end_date=now) if has_course_run: self.assertEqual(get_corresponding_course_run(exam_run), expected) else: self.assertEqual(get_corresponding_course_run(exam_run), None)
def test_has_future_exam(self): """Test course has exam""" course_run = self.create_run() assert course_run.has_future_exam is False exam_run = ExamRunFactory.create(course=course_run.course) assert course_run.has_future_exam is True exam_run.delete() assert course_run.has_future_exam is False exam_run = ExamRunFactory.create( course=course_run.course, date_last_eligible=now_in_utc().date(), ) assert course_run.has_future_exam is False exam_run.delete() assert course_run.has_future_exam is False ExamRunFactory.create( course=course_run.course, date_last_eligible=(now_in_utc() - timedelta(days=1)).date(), ) assert course_run.has_future_exam is False
def test_has_future_exam(self): """Test course has exam""" course_run = self.create_run() assert course_run.has_future_exam is False exam_run = ExamRunFactory.create(course=course_run.course) assert course_run.has_future_exam is True exam_run.delete() assert course_run.has_future_exam is False exam_run = ExamRunFactory.create( course=course_run.course, date_last_eligible=now_in_utc().date(), ) assert course_run.has_future_exam is False exam_run.delete() assert course_run.has_future_exam is False ExamRunFactory.create( course=course_run.course, date_last_eligible=(now_in_utc() - timedelta(days=1)).date(), ) assert course_run.has_future_exam is False
def test_bulk_authorize_for_exam_run_multiple(self): """Test that we check all program enrollments""" ProgramEnrollmentFactory.create(program=self.program) exam_run = ExamRunFactory.create(course=self.course_run.course) assert ExamAuthorization.objects.filter( course=exam_run.course, ).count() == 0 bulk_authorize_for_exam_run(exam_run) assert ExamAuthorization.objects.filter( course=exam_run.course, user=self.user ).count() == 1
def test_authorize_exam_runs(self, authorized, bulk_authorize_for_exam_run_mock): """Test authorize_exam_runs()""" current_run = ExamRunFactory.create(authorized=authorized) past_run = ExamRunFactory.create(scheduling_future=True, authorized=authorized) future_run = ExamRunFactory.create(scheduling_past=True, authorized=authorized) authorize_exam_runs() if authorized: assert bulk_authorize_for_exam_run_mock.call_count == 0 else: assert bulk_authorize_for_exam_run_mock.call_count == 2 # past run shouldn't be called bulk_authorize_for_exam_run_mock.assert_any_call(current_run) bulk_authorize_for_exam_run_mock.assert_any_call(future_run) for exam_run in (current_run, future_run): exam_run.refresh_from_db() assert exam_run.authorized is True past_run.refresh_from_db() assert past_run.authorized is False
def setUpTestData(cls): cls.user = SocialUserFactory.create() cls.course_run = CourseRunFactory.create( course__program__financial_aid_availability=True ) cls.exam_run = ExamRunFactory.create( course=cls.course_run.course, date_grades_available=now_in_utc() - timedelta(weeks=1) ) cls.not_passing_final_grade = FinalGradeFactory.create( user=cls.user, course_run=cls.course_run, grade=0.5, passed=False )
def test_bulk_authorize_for_exam_run_past(self): """Test that we don't create an authorization for a run in the past""" exam_run = ExamRunFactory.create( scheduling_past=True, course=self.course_run.course, ) assert ExamAuthorization.objects.filter( course=exam_run.course, ).count() == 0 bulk_authorize_for_exam_run(exam_run) assert ExamAuthorization.objects.filter( course=exam_run.course, ).count() == 0
def setUpTestData(cls): with mute_signals(post_save): profile = ProfileFactory.create() cls.program, _ = create_program(past=True) cls.user = profile.user cls.course_run = cls.program.course_set.first().courserun_set.first() with mute_signals(post_save): CachedEnrollmentFactory.create(user=cls.user, course_run=cls.course_run) cls.exam_run = ExamRunFactory.create(course=cls.course_run.course) with mute_signals(post_save): cls.final_grade = FinalGradeFactory.create( user=cls.user, course_run=cls.course_run, passed=True, course_run_paid_on_edx=False, )
def test_update_authorizations_for_exam_run(self, is_future, is_past): """Tests update_authorizations_for_exam_run()""" exam_run = ExamRunFactory.create(scheduling_future=is_future, scheduling_past=is_past) taken_auth = ExamAuthorizationFactory.create( exam_run=exam_run, exam_taken=True, status=ExamAuthorization.STATUS_SUCCESS, operation=ExamAuthorization.OPERATION_ADD, ) pending_auth = ExamAuthorizationFactory.create( exam_run=exam_run, status=ExamAuthorization.STATUS_PENDING, operation=ExamAuthorization.OPERATION_ADD, ) nonpending_auths = [] for status in [ ExamAuthorization.STATUS_IN_PROGRESS, ExamAuthorization.STATUS_FAILED, ExamAuthorization.STATUS_SUCCESS, ]: nonpending_auths.append( ExamAuthorizationFactory.create( exam_run=exam_run, status=status, operation=ExamAuthorization.OPERATION_ADD, )) update_authorizations_for_exam_run(exam_run) taken_auth.refresh_from_db() assert taken_auth.status == ExamAuthorization.STATUS_SUCCESS assert taken_auth.operation == ExamAuthorization.OPERATION_ADD pending_auth.refresh_from_db() assert pending_auth.status == ExamAuthorization.STATUS_PENDING assert pending_auth.operation == ExamAuthorization.OPERATION_ADD for auth in nonpending_auths: auth.refresh_from_db() if is_past or is_future: assert auth.status != ExamAuthorization.STATUS_PENDING assert auth.operation == ExamAuthorization.OPERATION_ADD else: assert auth.status == ExamAuthorization.STATUS_PENDING assert auth.operation == ExamAuthorization.OPERATION_UPDATE
def test_update_authorizations_for_exam_run(self, is_future, is_past): """Tests update_authorizations_for_exam_run()""" exam_run = ExamRunFactory.create(scheduling_future=is_future, scheduling_past=is_past) taken_auth = ExamAuthorizationFactory.create( exam_run=exam_run, exam_taken=True, status=ExamAuthorization.STATUS_SUCCESS, operation=ExamAuthorization.OPERATION_ADD, ) pending_auth = ExamAuthorizationFactory.create( exam_run=exam_run, status=ExamAuthorization.STATUS_PENDING, operation=ExamAuthorization.OPERATION_ADD, ) nonpending_auths = [] for status in [ ExamAuthorization.STATUS_IN_PROGRESS, ExamAuthorization.STATUS_FAILED, ExamAuthorization.STATUS_SUCCESS, ]: nonpending_auths.append(ExamAuthorizationFactory.create( exam_run=exam_run, status=status, operation=ExamAuthorization.OPERATION_ADD, )) update_authorizations_for_exam_run(exam_run) taken_auth.refresh_from_db() assert taken_auth.status == ExamAuthorization.STATUS_SUCCESS assert taken_auth.operation == ExamAuthorization.OPERATION_ADD pending_auth.refresh_from_db() assert pending_auth.status == ExamAuthorization.STATUS_PENDING assert pending_auth.operation == ExamAuthorization.OPERATION_ADD for auth in nonpending_auths: auth.refresh_from_db() if is_past or is_future: assert auth.status != ExamAuthorization.STATUS_PENDING assert auth.operation == ExamAuthorization.OPERATION_ADD else: assert auth.status == ExamAuthorization.STATUS_PENDING assert auth.operation == ExamAuthorization.OPERATION_UPDATE
def test_exam_authorization_course_mismatch(self): """ test exam_authorization fails if course_run and exam_run courses mismatch """ # Neither user has exam profile nor authorization. assert ExamProfile.objects.filter( profile=self.user.profile).exists() is False assert ExamAuthorization.objects.filter( user=self.user, course=self.course_run.course).exists() is False exam_run = ExamRunFactory.create() with self.assertRaises(ExamAuthorizationException): authorize_for_exam_run(self.user, self.course_run, exam_run) # Assert user has exam profile and authorization. assert ExamProfile.objects.filter( profile=self.user.profile).exists() is False assert ExamAuthorization.objects.filter( user=self.user, course=self.course_run.course).exists() is False
def test_create_combined_final_grade(mocker): """ Test create_combined_final_grade creates the grade when it is missing """ update_mock = mocker.patch( 'grades.api.update_or_create_combined_final_grade', autospec=True) course_run = CourseRunFactory.create( freeze_grade_date=now_in_utc() - timedelta(days=1), course__program__financial_aid_availability=True, course__program__live=True) course = course_run.course CourseRunGradingStatus.objects.create(course_run=course_run, status='complete') # Create exam run for course with date_grades_available True exam_run_grades_available = ExamRunFactory.create( course=course, date_grades_available=now_in_utc() - timedelta(weeks=1)) exam_grades = ProctoredExamGradeFactory.create_batch( 5, course=course, exam_run=exam_run_grades_available, passed=True, ) for exam_grade in exam_grades[:3]: CombinedFinalGrade.objects.create(user=exam_grade.user, course=course, grade=0.7) # Only 3 users will have combined grades for exam_grade in exam_grades[3:]: FinalGradeFactory.create(user=exam_grade.user, course_run=course_run, passed=True) tasks.create_combined_final_grades.delay() assert update_mock.call_count == 2 update_mock.assert_has_calls( [call(exam_grades[3].user, course), call(exam_grades[4].user, course)], any_order=True)
def setUpTestData(cls): with mute_signals(post_save): cls.profile = ProfileFactory.create() cls.program, _ = create_program(past=True) cls.course_run = cls.program.course_set.first().courserun_set.first() CachedCurrentGradeFactory.create( user=cls.profile.user, course_run=cls.course_run, data={ "passed": True, "percent": 0.9, "course_key": cls.course_run.edx_course_key, "username": cls.profile.user.username } ) CachedCertificateFactory.create(user=cls.profile.user, course_run=cls.course_run) cls.exam_run = ExamRunFactory.create( course=cls.course_run.course, date_first_schedulable=now_in_utc() - timedelta(days=1), )
def setUpTestData(cls): with mute_signals(post_save): cls.profile = ProfileFactory.create() cls.program, _ = create_program(past=True) cls.course_run = cls.program.course_set.first().courserun_set.first() CachedCurrentGradeFactory.create(user=cls.profile.user, course_run=cls.course_run, data={ "passed": True, "percent": 0.9, "course_key": cls.course_run.edx_course_key, "username": cls.profile.user.username }) CachedCertificateFactory.create(user=cls.profile.user, course_run=cls.course_run) cls.exam_run = ExamRunFactory.create( course=cls.course_run.course, date_first_schedulable=now_in_utc() - timedelta(days=1), )
def test_create_combined_final_grade(mocker): """ Test create_combined_final_grade creates the grade when it is missing """ update_mock = mocker.patch('grades.api.update_or_create_combined_final_grade', autospec=True) course_run = CourseRunFactory.create( freeze_grade_date=now_in_utc()-timedelta(days=1), course__program__financial_aid_availability=True, course__program__live=True ) course = course_run.course CourseRunGradingStatus.objects.create(course_run=course_run, status='complete') # Create exam run for course with date_grades_available True exam_run_grades_available = ExamRunFactory.create( course=course, date_grades_available=now_in_utc() - timedelta(weeks=1)) exam_grades = ProctoredExamGradeFactory.create_batch( 5, course=course, exam_run=exam_run_grades_available, passed=True, ) for exam_grade in exam_grades[:3]: CombinedFinalGrade.objects.create(user=exam_grade.user, course=course, grade=0.7) # Only 3 users will have combined grades for exam_grade in exam_grades[3:]: FinalGradeFactory.create(user=exam_grade.user, course_run=course_run, passed=True) tasks.create_combined_final_grades.delay() assert update_mock.call_count == 2 update_mock.assert_has_calls( [call(exam_grades[3].user, course), call(exam_grades[4].user, course)], any_order=True )
def test_exam_authorization_for_missed_payment_deadline(self): """ test exam_authorization when user paid, but only after deadline """ create_order(self.user, self.course_run) mmtrack = get_mmtrack(self.user, self.program) course = self.course_run.course now = now_in_utc() exam_run = ExamRunFactory.create(course=course, authorized=True, date_first_schedulable=now, date_last_schedulable=now + datetime.timedelta(weeks=2)) self.course_run.upgrade_deadline = exam_run.date_first_schedulable - datetime.timedelta( weeks=8) self.course_run.save() # paid after deadline order_qset = Order.objects.filter( user=self.user, line__course_key=self.course_run.edx_course_key) order_qset.update(modified_at=now - datetime.timedelta(weeks=5)) with patch('exams.api.get_corresponding_course_run') as mock: mock.side_effect = [self.course_run] with self.assertRaises(ExamAuthorizationException): authorize_for_exam_run(self.user, self.course_run, self.exam_run) assert ExamAuthorization.objects.filter( user=mmtrack.user, course=course).exists() is False # paid before deadline order_qset.update(modified_at=exam_run.date_first_schedulable - datetime.timedelta(weeks=9)) authorize_for_exam_run(self.user, self.course_run, self.exam_run) assert ExamAuthorization.objects.filter(user=mmtrack.user, course=course).exists() is True
def test_exam_authorization_course_mismatch(self): """ test exam_authorization fails if course_run and exam_run courses mismatch """ mmtrack = get_mmtrack(self.user, self.program) # Neither user has exam profile nor authorization. assert ExamProfile.objects.filter(profile=mmtrack.user.profile).exists() is False assert ExamAuthorization.objects.filter( user=mmtrack.user, course=self.course_run.course ).exists() is False exam_run = ExamRunFactory.create() with self.assertRaises(ExamAuthorizationException): authorize_for_exam_run(mmtrack, self.course_run, exam_run) # Assert user has exam profile and authorization. assert ExamProfile.objects.filter(profile=mmtrack.user.profile).exists() is False assert ExamAuthorization.objects.filter( user=mmtrack.user, course=self.course_run.course ).exists() is False
def test_update_existing_combined_final_grade_for_exam_run(self, update_or_create_mock): """ Test update_existing_combined_final_grade_for_exam_run """ ProctoredExamGradeFactory.create( user=self.user, course=self.course_run.course, percentage_grade=0.6, passed=True, exam_run=self.exam_run ) FinalGradeFactory.create(user=self.user, course_run__course=self.course_run.course, grade=0.8, passed=True) # should only update if combined grade already exists for user api.update_existing_combined_final_grade_for_exam_run(self.exam_run) assert update_or_create_mock.called is False CombinedFinalGrade.objects.create(user=self.user, course=self.course_run.course, grade=0.7) # should call it once since there is an existing combined grade api.update_existing_combined_final_grade_for_exam_run(self.exam_run) update_or_create_mock.assert_called_once_with(self.user, self.course_run.course) exam_run = ExamRunFactory.create( course=self.course_run.course, date_grades_available=now_in_utc() - timedelta(weeks=1) ) ProctoredExamGradeFactory.create( user=self.user, course=self.course_run.course, percentage_grade=0.8, passed=True, exam_run=exam_run ) # should call it again for a different exam grade api.update_existing_combined_final_grade_for_exam_run(exam_run) assert update_or_create_mock.call_count == 2
def create_exams(self, current, edx_passed, exam_passed, new_offering, can_schedule, future_exam, need_to_pay): """Create an exam and mark it and the related course as passed or not passed""" # pylint: disable-msg=too-many-arguments self.make_fa_program_enrollment(FinancialAidStatus.AUTO_APPROVED) course = Course.objects.get(title='Digital Learning 200') if current: course_run = CourseRunFactory(course=course) call_command( "alter_data", 'set_to_enrolled', '--username', 'staff', '--course-run-key', course_run.edx_course_key ) FinalGradeFactory.create( user=self.user, course_run=course_run, grade=0.8 if edx_passed else 0.2, passed=True ) else: if edx_passed: call_command( "alter_data", 'set_to_passed', '--username', 'staff', '--course-title', 'Digital Learning 200', '--grade', '75', ) else: call_command( "alter_data", 'set_to_failed', '--username', 'staff', '--course-title', 'Digital Learning 200', '--grade', '45', ) course_run = course.courserun_set.first() ExamProfileFactory.create(status='success', profile=self.user.profile) exam_run = ExamRunFactory.create(course=course, eligibility_past=True, scheduling_past=True) ExamAuthorizationFactory.create( user=self.user, course=course, exam_run=exam_run, status='success', exam_taken=True ) LineFactory.create( order__status=Order.FULFILLED, course_key=course_run ) ProctoredExamGradeFactory.create( user=self.user, course=course, exam_run=exam_run, passed=exam_passed, percentage_grade=0.8 if exam_passed else 0.3 ) if new_offering: CourseRunFactory.create(course=course) if can_schedule: exam_run = ExamRunFactory.create( scheduling_past=False, scheduling_future=False, authorized=True, course=course ) ExamAuthorizationFactory.create( user=self.user, course=course, exam_run=exam_run, status='success', ) if future_exam: ExamRunFactory.create( scheduling_past=False, scheduling_future=True, authorized=True, course=course ) if need_to_pay: exam_run = ExamRunFactory.create(course=course, eligibility_past=True, scheduling_past=True) ExamAuthorizationFactory.create( user=self.user, course=course, exam_run=exam_run, status='success', exam_taken=True ) ProctoredExamGradeFactory.create( user=self.user, course=course, exam_run=exam_run, passed=False, percentage_grade=0.3 )
def test_has_exam(self): """test that creating an exam run makes has_exam == true""" assert self.course.has_exam is False ExamRunFactory.create(course=self.course) assert self.course.has_exam is True
def test_generate_course_certificates(): """ Test that generate_course_certificates_for_fa_students creates certificates for appropriate FinalGrades """ program = ProgramFactory.create(financial_aid_availability=True, live=True) week_ago = now_in_utc() - timedelta(weeks=1) # Course without exams course = CourseFactory.create(program=program) passed_final_grades = FinalGradeFactory.create_batch( 4, course_run__course=course, course_run__freeze_grade_date=week_ago, passed=True ) # create a duplicate final grade for course for user FinalGradeFactory.create( user=passed_final_grades[0].user, course_run__course=course, course_run__freeze_grade_date=week_ago, passed=True ) # 2nd course for user course_2 = CourseFactory.create(program=program) FinalGradeFactory.create( course_run__course=course_2, course_run__freeze_grade_date=week_ago, passed=True, user=passed_final_grades[1].user ) # Another non-fa course non_fa_course = CourseFactory.create(program__financial_aid_availability=False) # Course with exams # Create two exam runs for course with different date_grades_available exam_run_grades_available = ExamRunFactory.create( course__program=program, date_grades_available=now_in_utc() - timedelta(weeks=1)) course_with_exams = exam_run_grades_available.course exam_run_no_grades = ExamRunFactory.create( course=course_with_exams, date_grades_available=now_in_utc() + timedelta(weeks=1)) passed_final_grades_with_exam = FinalGradeFactory.create_batch( 6, course_run__course=course_with_exams, passed=True ) # Create ProctoredExamGrade records with a mix of passed and failed outcomes, and exam grade availability final_grades_with_passed_exam = passed_final_grades_with_exam[:2] ProctoredExamGradeFactory.create_batch( 2, user=factory.Iterator([final_grade.user for final_grade in final_grades_with_passed_exam]), course=course_with_exams, exam_run=exam_run_grades_available, passed=True, ) ProctoredExamGradeFactory.create_batch( 2, user=factory.Iterator([final_grade.user for final_grade in passed_final_grades_with_exam[2:4]]), course=course_with_exams, exam_run=exam_run_no_grades, passed=True, ) ProctoredExamGradeFactory.create_batch( 2, user=factory.Iterator([final_grade.user for final_grade in passed_final_grades_with_exam[4:6]]), course=course_with_exams, passed=False, ) # course runs need to have CourseRunGradingStatus to get certificates all_grades = FinalGrade.objects.filter(course_run__course__in=[course, course_2, course_with_exams, non_fa_course]) for final_grade in all_grades: CourseRunGradingStatus.objects.create(course_run=final_grade.course_run, status='complete') tasks.generate_course_certificates_for_fa_students.delay() # Make sure that certificates were created only for passed and 'complete' status FinalGrades that either # had no course exam, or had a passed ProctoredExamGrade. certificates = MicromastersCourseCertificate.objects.filter(course__in=[course, course_2, course_with_exams]) assert certificates.count() == 7 expected_certificate_final_grades = passed_final_grades + final_grades_with_passed_exam assert set(certificates.values_list('user', flat=True)) == { final_grade.user.id for final_grade in expected_certificate_final_grades }
def test_has_exam(self): """test that creating an exam run makes has_exam == true""" assert self.course.has_exam is False ExamRunFactory.create(course=self.course) assert self.course.has_exam is True
def test_generate_course_certificates(): """ Test that generate_course_certificates_for_fa_students creates certificates for appropriate FinalGrades """ program = ProgramFactory.create(financial_aid_availability=True, live=True) week_ago = now_in_utc() - timedelta(weeks=1) # Course without exams course = CourseFactory.create(program=program) passed_final_grades = FinalGradeFactory.create_batch( 4, course_run__course=course, course_run__freeze_grade_date=week_ago, passed=True ) # create a duplicate final grade for course for user FinalGradeFactory.create( user=passed_final_grades[0].user, course_run__course=course, course_run__freeze_grade_date=week_ago, passed=True ) # 2nd course for user course_2 = CourseFactory.create(program=program) FinalGradeFactory.create( course_run__course=course_2, course_run__freeze_grade_date=week_ago, passed=True, user=passed_final_grades[1].user ) # Another non-fa course non_fa_course = CourseFactory.create(program__financial_aid_availability=False) # Course with exams # Create two exam runs for course with different date_grades_available exam_run_grades_available = ExamRunFactory.create( course__program=program, date_grades_available=now_in_utc() - timedelta(weeks=1)) course_with_exams = exam_run_grades_available.course exam_run_no_grades = ExamRunFactory.create( course=course_with_exams, date_grades_available=now_in_utc() + timedelta(weeks=1)) passed_final_grades_with_exam = FinalGradeFactory.create_batch( 6, course_run__course=course_with_exams, passed=True ) # Create ProctoredExamGrade records with a mix of passed and failed outcomes, and exam grade availability final_grades_with_passed_exam = passed_final_grades_with_exam[:2] ProctoredExamGradeFactory.create_batch( 2, user=factory.Iterator([final_grade.user for final_grade in final_grades_with_passed_exam]), course=course_with_exams, exam_run=exam_run_grades_available, passed=True, ) ProctoredExamGradeFactory.create_batch( 2, user=factory.Iterator([final_grade.user for final_grade in passed_final_grades_with_exam[2:4]]), course=course_with_exams, exam_run=exam_run_no_grades, passed=True, ) ProctoredExamGradeFactory.create_batch( 2, user=factory.Iterator([final_grade.user for final_grade in passed_final_grades_with_exam[4:6]]), course=course_with_exams, passed=False, ) # course runs need to have CourseRunGradingStatus to get certificates all_grades = FinalGrade.objects.filter(course_run__course__in=[course, course_2, course_with_exams, non_fa_course]) for final_grade in all_grades: CourseRunGradingStatus.objects.create(course_run=final_grade.course_run, status='complete') tasks.generate_course_certificates_for_fa_students.delay() # Make sure that certificates were created only for passed and 'complete' status FinalGrades that either # had no course exam, or had a passed ProctoredExamGrade. certificates = MicromastersCourseCertificate.objects.filter(course__in=[course, course_2, course_with_exams]) assert certificates.count() == 7 expected_certificate_final_grades = passed_final_grades + final_grades_with_passed_exam assert set(certificates.values_list('user', flat=True)) == set( [final_grade.user.id for final_grade in expected_certificate_final_grades] )