def test_exam_authorization_when_not_passed_course(self): """ test exam_authorization when user has not passed course but paid. """ create_order(self.user, self.course_run) with patch('dashboard.utils.MMTrack.has_passed_course', autospec=True, return_value=False): mmtrack = get_mmtrack(self.user, self.program) expected_errors_message = MESSAGE_NOT_PASSED_OR_EXIST_TEMPLATE.format( user=mmtrack.user.username, course_id=self.course_run.edx_course_key) assert mmtrack.has_paid(self.course_run.edx_course_key) is True assert mmtrack.has_passed_course( self.course_run.edx_course_key) is False # 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 with self.assertRaises(ExamAuthorizationException) as eae: authorize_for_exam_run(self.user, self.course_run, self.exam_run) assert eae.exception.args[0] == expected_errors_message # assert exam profile created but user is not authorized assert ExamProfile.objects.filter( profile=mmtrack.user.profile).exists() is True assert ExamAuthorization.objects.filter( user=mmtrack.user, course=self.course_run.course).exists() is False
def generate_program_letter(user, program): """ Create a program letter if the user has a MM course certificate for each course in the program and program is non-fa. Args: user (User): a Django user. program (programs.models.Program): program where the user is enrolled. """ if MicromastersProgramCommendation.objects.filter(user=user, program=program).exists(): log.info('User [%s] already has a letter for program [%s]', user, program) return if (program.financial_aid_availability and MicromastersProgramCertificate.objects.filter(user=user, program=program).exists()): MicromastersProgramCommendation.objects.create(user=user, program=program) return mmtrack = get_mmtrack(user, program) courses_passed = mmtrack.count_courses_passed() program_course_count = (program.num_required_courses if program.electives_set.exists() else program.course_set.count()) if courses_passed < program_course_count: return MicromastersProgramCommendation.objects.create(user=user, program=program) log.info( 'Created MM program letter for [%s] in program [%s]', user.username, program.title )
def update_or_create_combined_final_grade(user, course): """ Update or create CombinedFinalGrade Args: user (User): a django User course (Course): a course model object """ if not course.has_exam: return mmtrack = get_mmtrack(user, course.program) final_grade = mmtrack.get_best_final_grade_for_course(course) if final_grade is None: log.warning('User [%s] does not have a final for course [%s]', user, course) return best_exam = mmtrack.get_best_proctored_exam_grade(course) if best_exam is None: log.warning('User [%s] does not have a passing exam grade for course [%s]', user, course) return calculated_grade = round(final_grade.grade_percent * COURSE_GRADE_WEIGHT + best_exam.score * EXAM_GRADE_WEIGHT, 1) combined_grade, _ = CombinedFinalGrade.objects.update_or_create( user=user, course=course, defaults={'grade': calculated_grade} ) combined_grade.save_and_log(None)
def test_exam_authorization_when_not_passed_course(self): """ test exam_authorization when user has not passed course but paid. """ create_order(self.user, self.course_run) with patch('dashboard.utils.MMTrack.has_passed_course', autospec=True, return_value=False): mmtrack = get_mmtrack(self.user, self.program) expected_errors_message = MESSAGE_NOT_PASSED_OR_EXIST_TEMPLATE.format( user=mmtrack.user.username, course_id=self.course_run.edx_course_key ) assert mmtrack.has_paid(self.course_run.edx_course_key) is True assert mmtrack.has_passed_course(self.course_run.edx_course_key) is False # 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 with self.assertRaises(ExamAuthorizationException) as eae: authorize_for_exam_run(mmtrack, self.course_run, self.exam_run) assert eae.exception.args[0] == expected_errors_message # assert exam profile created but user is not authorized assert ExamProfile.objects.filter(profile=mmtrack.user.profile).exists() is True assert ExamAuthorization.objects.filter( user=mmtrack.user, course=self.course_run.course ).exists() is False
def get_user_program_info(user, edx_client): """ Provides a detailed serialization all of a User's enrolled Programs with enrollment/grade info Args: user (User): A User edx_client (EdxApi): An EdxApi instance Returns: list: Enrolled Program information """ # update cache # NOTE: this part can be moved to an asynchronous task if edx_client is not None: try: for cache_type in CachedEdxDataApi.SUPPORTED_CACHES: CachedEdxDataApi.update_cache_if_expired( user, edx_client, cache_type) except InvalidCredentialStored: # this needs to raise in order to force the user re-login raise except: # pylint: disable=bare-except log.exception('Impossible to refresh edX cache') response_data = { "programs": [], "is_edx_data_fresh": CachedEdxDataApi.are_all_caches_fresh(user) } all_programs = (Program.objects.filter( live=True, programenrollment__user=user).prefetch_related( 'course_set__courserun_set')) for program in all_programs: mmtrack_info = get_mmtrack(user, program) response_data['programs'].append(get_info_for_program(mmtrack_info)) return response_data
def test_exam_authorization_when_not_paid(self): """ test exam_authorization when user has passed course but not paid. """ with mute_signals(post_save): self.final_grade.course_run_paid_on_edx = False self.final_grade.save() mmtrack = get_mmtrack(self.user, self.program) assert mmtrack.has_paid(self.course_run.edx_course_key) is False expected_errors_message = MESSAGE_NOT_ELIGIBLE_TEMPLATE.format( user=mmtrack.user.username, course_id=self.course_run.edx_course_key ) # 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 with self.assertRaises(ExamAuthorizationException) as eae: authorize_for_exam_run(mmtrack, self.course_run, self.exam_run) assert eae.exception.args[0] == expected_errors_message # Assert user has no exam profile and authorization after exception. 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_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 generate_program_letter(user, program): """ Create a program letter if the user has a MM course certificate for each course in the program and program is non-fa. Args: user (User): a Django user. program (programs.models.Program): program where the user is enrolled. """ if program.financial_aid_availability: log.error('Congratulation letter is only available for non-financial aid programs.') return if MicromastersProgramCommendation.objects.filter(user=user, program=program).exists(): log.info('User [%s] already has a letter for program [%s]', user, program) return mmtrack = get_mmtrack(user, program) courses_passed = mmtrack.count_courses_passed() program_course_count = program.course_set.count() if courses_passed < program_course_count: return MicromastersProgramCommendation.objects.create(user=user, program=program) log.info( 'Created MM program letter for [%s] in program [%s]', user.username, program.title )
def authorize_for_exam_run(user, course_run, exam_run): """ Authorize user for exam if he has paid for course and passed course. Args: mmtrack (dashboard.utils.MMTrack): a instance of all user information about a program. course_run (courses.models.CourseRun): A CourseRun object. exam_run (exams.models.ExamRun): the ExamRun we're authorizing for """ mmtrack = get_mmtrack(user, course_run.course.program) if not mmtrack.user.is_active: raise ExamAuthorizationException( "Inactive user '{}' cannot be authorized for the exam for course id '{}'" .format(mmtrack.user.username, course_run.course)) if course_run.course != exam_run.course: raise ExamAuthorizationException( "Course '{}' on CourseRun doesn't match Course '{}' on ExamRun". format(course_run.course, exam_run.course)) if not exam_run.is_schedulable: raise ExamAuthorizationException( "Exam isn't schedulable currently: {}".format(exam_run)) # If user has not paid for course then we dont need to process authorization if not mmtrack.has_paid(course_run.edx_course_key): errors_message = MESSAGE_NOT_ELIGIBLE_TEMPLATE.format( user=mmtrack.user.username, course_id=course_run.edx_course_key) raise ExamAuthorizationException(errors_message) # if user paid for a course then create his exam profile if it is not created yet ExamProfile.objects.get_or_create(profile=mmtrack.user.profile) # if they didn't pass, they don't get authorized if not mmtrack.has_passed_course(course_run.edx_course_key): errors_message = MESSAGE_NOT_PASSED_OR_EXIST_TEMPLATE.format( user=mmtrack.user.username, course_id=course_run.edx_course_key) raise ExamAuthorizationException(errors_message) # if they have run out of attempts, they don't get authorized if has_to_pay_for_exam(mmtrack, course_run.course): errors_message = MESSAGE_NO_ATTEMPTS_TEMPLATE.format( user=mmtrack.user.username, course_id=course_run.edx_course_key) raise ExamAuthorizationException(errors_message) # if they paid after deadline they don't get authorized for same term exam current_course_run = get_corresponding_course_run(exam_run) if current_course_run and mmtrack.paid_but_missed_deadline( current_course_run): errors_message = MESSAGE_MISSED_DEADLINE_TEMPLATE.format( user=mmtrack.user.username, course_id=course_run.edx_course_key) raise ExamAuthorizationException(errors_message) ExamAuthorization.objects.get_or_create( user=mmtrack.user, course=course_run.course, exam_run=exam_run, status=ExamAuthorization.STATUS_SUCCESS, ) log.info( '[Exam authorization] user "%s" is authorized for the exam for course id "%s"', mmtrack.user.username, course_run.edx_course_key)
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 get_user_program_info(user, edx_client): """ Provides a detailed serialization all of a User's enrolled Programs with enrollment/grade info Args: user (User): A User edx_client (EdxApi): An EdxApi instance Returns: list: Enrolled Program information """ # update cache # NOTE: this part can be moved to an asynchronous task if edx_client is not None: try: for cache_type in CachedEdxDataApi.SUPPORTED_CACHES: CachedEdxDataApi.update_cache_if_expired(user, edx_client, cache_type) except InvalidCredentialStored: # this needs to raise in order to force the user re-login raise except: # pylint: disable=bare-except log.exception('Impossible to refresh edX cache') response_data = { "programs": [], "is_edx_data_fresh": CachedEdxDataApi.are_all_caches_fresh(user) } all_programs = ( Program.objects.filter(live=True, programenrollment__user=user).prefetch_related('course_set__courserun_set') ) for program in all_programs: mmtrack_info = get_mmtrack(user, program) response_data['programs'].append(get_info_for_program(mmtrack_info)) return response_data
def test_exam_authorization_when_not_paid(self): """ test exam_authorization when user has passed course but not paid. """ with mute_signals(post_save): self.final_grade.course_run_paid_on_edx = False self.final_grade.save() mmtrack = get_mmtrack(self.user, self.program) assert mmtrack.has_paid(self.course_run.edx_course_key) is False expected_errors_message = MESSAGE_NOT_ELIGIBLE_TEMPLATE.format( user=mmtrack.user.username, course_id=self.course_run.edx_course_key) # 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 with self.assertRaises(ExamAuthorizationException) as eae: authorize_for_exam_run(self.user, self.course_run, self.exam_run) assert eae.exception.args[0] == expected_errors_message # Assert user has no exam profile and authorization after exception. 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 update_exam_authorization_cached_enrollment(sender, instance, **kwargs): # pylint: disable=unused-argument """ Signal handler to trigger an exam profile when user enroll in a course. """ mmtrack = get_mmtrack(instance.user, instance.course_run.course.program) if is_eligible_for_exam(mmtrack, instance.course_run): # if user paid for a course then create his exam profile if it is not created yet. ExamProfile.objects.get_or_create(profile=mmtrack.user.profile)
def populate_final_grade(apps, schema_editor): MicromastersCourseCertificate = apps.get_model('grades', 'MicromastersCourseCertificate') for certificate in MicromastersCourseCertificate.objects.select_related('final_grade').all(): if not certificate.final_grade: mmtrack = get_mmtrack(certificate.user, certificate.course.program) certificate.final_grade = mmtrack.get_best_final_grade_for_course(certificate.course) certificate.save()
def test_set_to_failed(self, course_run_program_type): """set_to_failed should set a CourseRun to failed for a given User""" course_run = self.course_runs[course_run_program_type] grade = Decimal('0.55') set_to_failed(user=self.user, course_run=course_run, grade=grade) mmtrack = get_mmtrack(self.user, course_run.course.program) assert not mmtrack.has_passed_course(course_run.edx_course_key) assert int(mmtrack.get_final_grade_percent( course_run.edx_course_key)) == (grade * 100)
def test_get_mmtrack(self): """ test creation of mmtrack(dashboard.utils.MMTrack) object. """ self.pay_for_fa_course(self.crun_fa.edx_course_key) mmtrack = get_mmtrack(self.user, self.program_financial_aid) key = self.crun_fa.edx_course_key assert mmtrack.user == self.user assert mmtrack.has_paid(key) is True
def test_set_payment_status(self, course_run_program_type): """Commands that set a User's payment status should work based on the audit flag value""" course_run = self.course_runs[course_run_program_type] for audit_setting in (True, False): set_to_enrolled(user=self.user, course_run=course_run, audit=audit_setting) mmtrack = get_mmtrack(self.user, course_run.course.program) assert mmtrack.has_paid( course_run.edx_course_key) is not audit_setting
def populate_final_grade(apps, schema_editor): MicromastersCourseCertificate = apps.get_model( 'grades', 'MicromastersCourseCertificate') for certificate in MicromastersCourseCertificate.objects.select_related( 'final_grade').all(): if not certificate.final_grade: mmtrack = get_mmtrack(certificate.user, certificate.course.program) certificate.final_grade = mmtrack.get_best_final_grade_for_course( certificate.course) certificate.save()
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 get_purchasable_course_run(course_key, user): """ Gets a course run, or raises Http404 if not purchasable. To be purchasable a course run must not already be purchased, must be part of a live program, must be part of a program with financial aid enabled, with a financial aid object, and must have a valid price. Args: course_key (str): An edX course key user (User): The purchaser of the course run Returns: CourseRun: A course run """ # Make sure it's connected to a live program, it has a valid price, and the user is enrolled in the program already try: course_run = get_object_or_404( CourseRun, edx_course_key=course_key, course__program__live=True, course__program__financial_aid_availability=True, ) except Http404: log.warning("Course run %s is not purchasable", course_key) raise if not FinancialAid.objects.filter( tier_program__current=True, tier_program__program__course__courserun=course_run, user=user, status__in=FinancialAidStatus.TERMINAL_STATUSES, ).exists(): log.warning("Course run %s has no attached financial aid for user %s", course_key, get_social_username(user)) raise ValidationError( "Course run {} does not have a current attached financial aid application" .format(course_key)) # Make sure it's not already purchased if Line.objects.filter( order__status__in=Order.FULFILLED_STATUSES, order__user=user, course_key=course_run.edx_course_key, ).exists(): mmtrack = get_mmtrack(user, course_run.course.program) if not has_to_pay_for_exam(mmtrack, course_run.course): log.warning("Course run %s is already purchased by user %s", course_key, user) raise ValidationError( "Course run {} is already purchased".format(course_key)) return course_run
def bulk_authorize_for_exam_run(exam_run): """ Authorize all eligible users for the given exam run Args: exam_run(exams.models.ExamRun): the exam run to authorize for """ for program_enrollment in ProgramEnrollment.objects.filter( program=exam_run.course.program).iterator(): mmtrack = get_mmtrack(program_enrollment.user, program_enrollment.program) authorize_for_latest_passed_course(mmtrack, exam_run)
def get_purchasable_course_run(course_key, user): """ Gets a course run, or raises Http404 if not purchasable. To be purchasable a course run must not already be purchased, must be part of a live program, must be part of a program with financial aid enabled, with a financial aid object, and must have a valid price. Args: course_key (str): An edX course key user (User): The purchaser of the course run Returns: CourseRun: A course run """ # Make sure it's connected to a live program, it has a valid price, and the user is enrolled in the program already try: course_run = get_object_or_404( CourseRun, edx_course_key=course_key, course__program__live=True, course__program__financial_aid_availability=True, ) except Http404: log.warning("Course run %s is not purchasable", course_key) raise if not FinancialAid.objects.filter( tier_program__current=True, tier_program__program__course__courserun=course_run, user=user, status__in=FinancialAidStatus.TERMINAL_STATUSES, ).exists(): log.warning("Course run %s has no attached financial aid for user %s", course_key, get_social_username(user)) raise ValidationError( "Course run {} does not have a current attached financial aid application".format(course_key) ) # Make sure it's not already purchased if Line.objects.filter( order__status__in=Order.FULFILLED_STATUSES, order__user=user, course_key=course_run.edx_course_key, ).exists(): mmtrack = get_mmtrack(user, course_run.course.program) if not has_to_pay_for_exam(mmtrack, course_run.course): log.warning("Course run %s is already purchased by user %s", course_key, user) raise ValidationError("Course run {} is already purchased".format(course_key)) return course_run
def bulk_authorize_for_exam_run(exam_run): """ Authorize all eligible users for the given exam run Args: exam_run(exams.models.ExamRun): the exam run to authorize for """ for program_enrollment in ProgramEnrollment.objects.filter( program=exam_run.course.program ).iterator(): mmtrack = get_mmtrack( program_enrollment.user, program_enrollment.program ) authorize_for_latest_passed_course(mmtrack, exam_run)
def authorize_user_for_schedulable_exam_runs(user, course_run): """ Authorizes a user for all schedulable ExamRuns for a CourseRun Args: user (django.contib.auth.models.User): the user to authorize course_run (courses.models.CourseRun): the course run to check """ mmtrack = get_mmtrack(user, course_run.course.program) # for each ExamRun for this course that is currently schedulable, attempt to authorize the user for exam_run in ExamRun.get_currently_schedulable(course_run.course): try: authorize_for_exam_run(mmtrack, course_run, exam_run) except ExamAuthorizationException: log.debug('Unable to authorize user: %s for exam on course_id: %s', user.username, course_run.course.id)
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) enrollment = get_object_or_404(ProgramEnrollment, hash=kwargs.get('record_hash')) user = enrollment.user authenticated = not user.is_anonymous js_settings = { "gaTrackingID": settings.GA_TRACKING_ID, "reactGaDebug": settings.REACT_GA_DEBUG, "edx_base_url": settings.EDXORG_BASE_URL, "authenticated": authenticated, "partner_schools": list(PartnerSchool.objects.values_list("id", "name")), "hash": enrollment.hash } context["js_settings_json"] = json.dumps(js_settings) context["is_public"] = True courses = enrollment.program.course_set.all() mmtrack = get_mmtrack(user, enrollment.program) combined_grade = CombinedFinalGrade.objects.filter( user=user, course__in=courses.values_list("id", flat=True) ).order_by("updated_on").last() context["program_title"] = enrollment.program.title context["program_status"] = "completed" if MicromastersProgramCertificate.objects.filter( user=user, program=enrollment.program).exists() else "partially" context["last_updated"] = combined_grade.updated_on if combined_grade else "" context["profile"] = { "username": user.username, "email": user.email, "full_name": user.profile.full_name } context['courses'] = [] for course in courses: best_grade = mmtrack.get_best_final_grade_for_course(course) combined_grade = CombinedFinalGrade.objects.filter(user=user, course=course).first() context['courses'].append({ "title": course.title, "edx_course_key": best_grade.course_run.edx_course_key if best_grade else "", "attempts": mmtrack.get_course_proctorate_exam_results(course).count(), "letter_grade": convert_to_letter(combined_grade.grade) if combined_grade else "", "status": "Earned" if get_certificate_url(mmtrack, course) else "Not Earned", "date_earned": combined_grade.created_on if combined_grade else "", "overall_grade": mmtrack.get_overall_final_grade_for_course(course) }) return context
def serialize(cls, program_enrollment): """ Serializes a ProgramEnrollment object """ user = program_enrollment.user program = program_enrollment.program mmtrack = get_mmtrack(user, program) return { 'id': program.id, 'enrollments': cls.serialize_enrollments(mmtrack), 'courses': cls.serialize_course_enrollments(mmtrack), 'course_runs': cls.serialize_course_runs_enrolled(mmtrack), 'grade_average': mmtrack.calculate_final_grade_average(), 'is_learner': is_learner(user, program), 'num_courses_passed': mmtrack.count_courses_passed(), 'total_courses': program.course_set.count() }
def authorize_user_for_schedulable_exam_runs(user, course_run): """ Authorizes a user for all schedulable ExamRuns for a CourseRun Args: user (django.contib.auth.models.User): the user to authorize course_run (courses.models.CourseRun): the course run to check """ mmtrack = get_mmtrack(user, course_run.course.program) # for each ExamRun for this course that is currently schedulable, attempt to authorize the user for exam_run in ExamRun.get_currently_schedulable(course_run.course): try: authorize_for_exam_run(mmtrack, course_run, exam_run) except ExamAuthorizationException: log.debug( 'Unable to authorize user: %s for exam on course_id: %s', user.username, course_run.course.id )
def test_exam_authorization_for_inactive_user(self): """ test exam_authorization when inactive user passed and paid for course. """ with mute_signals(post_save): profile = ProfileFactory.create() user = profile.user user.is_active = False user.save() with mute_signals(post_save): CachedEnrollmentFactory.create(user=user, course_run=self.course_run) with mute_signals(post_save): FinalGradeFactory.create( user=user, course_run=self.course_run, passed=True, course_run_paid_on_edx=False, ) create_order(user, self.course_run) mmtrack = get_mmtrack(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)) # 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 with self.assertRaises(ExamAuthorizationException): authorize_for_exam_run(mmtrack, self.course_run, self.exam_run) # Assert user doesn't have 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_exam_authorization_for_inactive_user(self): """ test exam_authorization when inactive user passed and paid for course. """ with mute_signals(post_save): profile = ProfileFactory.create() user = profile.user user.is_active = False user.save() with mute_signals(post_save): CachedEnrollmentFactory.create(user=user, course_run=self.course_run) with mute_signals(post_save): FinalGradeFactory.create( user=user, course_run=self.course_run, passed=True, course_run_paid_on_edx=False, ) create_order(user, self.course_run) mmtrack = get_mmtrack(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)) # 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 with self.assertRaises(ExamAuthorizationException): authorize_for_exam_run(self.user, self.course_run, self.exam_run) # Assert user doesn't have 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_exam_authorization(self): """ test exam_authorization when user passed and paid for course. """ 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)) # 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 authorize_for_exam_run(self.user, self.course_run, self.exam_run) # Assert user has exam profile and authorization. assert ExamProfile.objects.filter( profile=mmtrack.user.profile).exists() is True assert ExamAuthorization.objects.filter( user=mmtrack.user, course=self.course_run.course).exists() is 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(self): """ test exam_authorization when user passed and paid for course. """ 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)) # 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 authorize_for_exam_run(mmtrack, self.course_run, self.exam_run) # Assert user has exam profile and authorization. assert ExamProfile.objects.filter(profile=mmtrack.user.profile).exists() is True assert ExamAuthorization.objects.filter( user=mmtrack.user, course=self.course_run.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 get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) enrollment = self.get_program_enrollment(**kwargs) user = enrollment.user authenticated = not user.is_anonymous share_hash_absolute_url = self.request.build_absolute_uri( reverse("shared_grade_records", kwargs=dict(enrollment_id=enrollment.id, record_share_hash=enrollment.share_hash))) js_settings = { "gaTrackingID": settings.GA_TRACKING_ID, "reactGaDebug": settings.REACT_GA_DEBUG, "edx_base_url": settings.EDXORG_BASE_URL, "authenticated": authenticated, "partner_schools": list(PartnerSchool.objects.values_list("id", "name")), "hash": enrollment.share_hash, "enrollment_id": enrollment.id, "absolute_record_share_hash": "" if not enrollment.share_hash else share_hash_absolute_url } context["js_settings_json"] = json.dumps(js_settings) courses = enrollment.program.course_set.prefetch_related( 'electivecourse') mmtrack = get_mmtrack(user, enrollment.program) combined_grade = CombinedFinalGrade.objects.filter( user=user, course__in=courses.values_list( "id", flat=True)).order_by("updated_on").last() context["program_title"] = enrollment.program.title context[ "program_status"] = "completed" if MicromastersProgramCertificate.objects.filter( user=user, program=enrollment.program).exists() else "partially" context[ "last_updated"] = combined_grade.updated_on if combined_grade else "" context["has_electives"] = mmtrack.program.electives_set.exists() context["is_owner"] = self.request.user == user context["public_jquery"] = True context["profile"] = { "username": user.username, "email": user.email, "full_name": user.profile.full_name } context['courses'] = [] for course in courses: best_grade = mmtrack.get_best_final_grade_for_course(course) combined_grade = CombinedFinalGrade.objects.filter( user=user, course=course).first() context['courses'].append({ "title": course.title, "edx_course_key": best_grade.course_run.edx_course_key if best_grade else "", "attempts": mmtrack.get_course_proctorate_exam_results(course).count(), "letter_grade": convert_to_letter(combined_grade.grade) if combined_grade else "", "status": "Earned" if get_certificate_url(mmtrack, course) else "Not Earned", "date_earned": combined_grade.created_on if combined_grade else "", "overall_grade": mmtrack.get_overall_final_grade_for_course(course), "elective_tag": "elective" if (getattr(course, "electivecourse", None) is not None) else "core" }) return context