def test_set_get_credit_requirements(self): """Test that if same requirement is added multiple times then it is added only one time and update for next all iterations. """ self.add_credit_course() requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 } }, { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.9 } } ] set_credit_requirements(self.course_key, requirements) self.assertEqual(len(get_credit_requirements(self.course_key)), 1) # now verify that the saved requirement has values of last requirement # from all same requirements self.assertEqual(get_credit_requirements(self.course_key)[0], requirements[1])
def test_set_credit_requirement_status(self): self.add_credit_course() requirements = [ {"namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": {"min_grade": 0.8}}, { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {}, }, ] api.set_credit_requirements(self.course_key, requirements) course_requirements = api.get_credit_requirements(self.course_key) self.assertEqual(len(course_requirements), 2) # Initially, the status should be None req_status = api.get_credit_requirement_status(self.course_key, "staff", namespace="grade", name="grade") self.assertEqual(req_status[0]["status"], None) # Set the requirement to "satisfied" and check that it's actually set api.set_credit_requirement_status("staff", self.course_key, "grade", "grade") req_status = api.get_credit_requirement_status(self.course_key, "staff", namespace="grade", name="grade") self.assertEqual(req_status[0]["status"], "satisfied") # Set the requirement to "failed" and check that it's actually set api.set_credit_requirement_status("staff", self.course_key, "grade", "grade", status="failed") req_status = api.get_credit_requirement_status(self.course_key, "staff", namespace="grade", name="grade") self.assertEqual(req_status[0]["status"], "failed")
def test_disable_credit_requirements(self): self.add_credit_course() requirements = [ { "namespace": "grade", "name": "grade", "criteria": { "min_grade": 0.8 } }, { "namespace": "grade", "name": "grade", "criteria": { "min_grade": 0.8 } } ] set_credit_requirements(self.course_key, requirements) self.assertEqual(len(get_credit_requirements(self.course_key)), 1) requirements = [ { "namespace": "reverification", "name": "midterm", "criteria": {} } ] set_credit_requirements(self.course_key, requirements) self.assertEqual(len(get_credit_requirements(self.course_key)), 1) grade_req = CreditRequirement.objects.filter(namespace="grade", name="grade") self.assertEqual(len(grade_req), 1) self.assertEqual(grade_req[0].active, False)
def setUp(self): super(TestMinGradedRequirementStatus, self).setUp() self.course = CourseFactory.create( org='Robot', number='999', display_name='Test Course' ) self.user = UserFactory() self.request = RequestFactory().get('/') self.request.user = self.user self.client.login(username=self.user.username, password=self.user.password) # Enable the course for credit CreditCourse.objects.create( course_key=self.course.id, enabled=True, ) # Configure a credit provider for the course CreditProvider.objects.create( provider_id="ASU", enable_integration=True, provider_url="https://credit.example.com/request", ) requirements = [{ "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": {"min_grade": 0.52}, }] # Add a single credit requirement (final grade) set_credit_requirements(self.course.id, requirements)
def test_declined_verification_on_skip(self): """Test that status with value 'declined' is added in credit requirement status model when a user skip's an ICRV. """ reverification_service = ReverificationService() checkpoint = VerificationCheckpoint.objects.create( course_id=unicode(self.course_id), checkpoint_location=self.final_checkpoint_location ) # Create credit course and set credit requirements. CreditCourse.objects.create(course_key=self.course_key, enabled=True) set_credit_requirements( self.course_key, [ { "namespace": "reverification", "name": checkpoint.checkpoint_location, "display_name": "Assessment 1", "criteria": {}, } ] ) reverification_service.skip_verification(self.user.id, unicode(self.course_id), self.final_checkpoint_location) requirement_status = get_credit_requirement_status( self.course_key, self.user.username, 'reverification', checkpoint.checkpoint_location ) self.assertEqual(SkippedReverification.objects.filter(user=self.user, course_id=self.course_id).count(), 1) self.assertEqual(len(requirement_status), 1) self.assertEqual(requirement_status[0].get('name'), checkpoint.checkpoint_location) self.assertEqual(requirement_status[0].get('status'), 'declined')
def test_disable_existing_requirement(self): self.add_credit_course() # Set initial requirements requirements = [ { "namespace": "reverification", "name": "midterm", "display_name": "Midterm", "criteria": {}, }, { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 }, } ] api.set_credit_requirements(self.course_key, requirements) # Update the requirements, removing an existing requirement api.set_credit_requirements(self.course_key, requirements[1:]) # Expect that now only the grade requirement is returned visible_reqs = api.get_credit_requirements(self.course_key) self.assertEqual(len(visible_reqs), 1) self.assertEqual(visible_reqs[0]["namespace"], "grade")
def setUp(self): """Create a course and an enrollment. """ super(CreditCourseDashboardTest, self).setUp() # Create a user and log in self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD) result = self.client.login(username=self.USERNAME, password=self.PASSWORD) self.assertTrue(result, msg="Could not log in") # Create a course and configure it as a credit course self.course = CourseFactory() CreditCourse.objects.create(course_key=self.course.id, enabled=True) # pylint: disable=no-member # Configure a credit provider CreditProvider.objects.create( provider_id=self.PROVIDER_ID, display_name=self.PROVIDER_NAME, provider_status_url=self.PROVIDER_STATUS_URL, enable_integration=True, ) # Configure a single credit requirement (minimum passing grade) credit_api.set_credit_requirements( self.course.id, # pylint: disable=no-member [{"namespace": "grade", "name": "grade", "display_name": "Final Grade", "criteria": {"min_grade": 0.8}}], ) # Enroll the user in the course as "verified" self.enrollment = CourseEnrollmentFactory( user=self.user, course_id=self.course.id, mode="verified" # pylint: disable=no-member )
def test_disable_credit_requirements(self): self.add_credit_course() requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 } } ] set_credit_requirements(self.course_key, requirements) self.assertEqual(len(get_credit_requirements(self.course_key)), 1) requirements = [ { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {} } ] set_credit_requirements(self.course_key, requirements) self.assertEqual(len(get_credit_requirements(self.course_key)), 1) grade_req = CreditRequirement.objects.filter(namespace="grade", name="grade") self.assertEqual(len(grade_req), 1) self.assertEqual(grade_req[0].active, False)
def test_get_credit_requirement(self): self.add_credit_course() requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 } } ] requirement = get_credit_requirement(self.course_key, "grade", "grade") self.assertIsNone(requirement) expected_requirement = { "course_key": self.course_key, "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 } } set_credit_requirements(self.course_key, requirements) requirement = get_credit_requirement(self.course_key, "grade", "grade") self.assertIsNotNone(requirement) self.assertEqual(requirement, expected_requirement)
def test_min_grade_credit_requirements_status(self): """ Test for credit course. If user passes minimum grade requirement then status will be updated as satisfied in requirement status table. """ self.basic_setup() self.submit_question_answer('p1', {'2_1': 'Correct'}) self.submit_question_answer('p2', {'2_1': 'Correct'}) # Enable the course for credit credit_course = CreditCourse.objects.create( course_key=self.course.id, enabled=True, ) # Configure a credit provider for the course CreditProvider.objects.create( provider_id="ASU", enable_integration=True, provider_url="https://credit.example.com/request", ) requirements = [{ "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": {"min_grade": 0.52}, }] # Add a single credit requirement (final grade) set_credit_requirements(self.course.id, requirements) self.get_grade_summary() req_status = get_credit_requirement_status(self.course.id, self.student_user.username, 'grade', 'grade') self.assertEqual(req_status[0]["status"], 'satisfied')
def test_eligibility_email_with_providers(self, providers_list, providers_email_message, expected_subject): """ Test the credit requirements, eligibility notification, email for different providers combinations. """ # Configure a course with two credit requirements self.add_credit_course() CourseFactory.create(org='edX', number='DemoX', display_name='Demo_Course') requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 }, }, { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {}, } ] api.set_credit_requirements(self.course_key, requirements) user = UserFactory.create(username=self.USER_INFO['username'], password=self.USER_INFO['password']) # Satisfy one of the requirements, but not the other api.set_credit_requirement_status( user.username, self.course_key, requirements[0]["namespace"], requirements[0]["name"] ) # Satisfy the other requirement. And mocked the api to return different kind of data. with mock.patch('openedx.core.djangoapps.credit.email_utils.get_credit_provider_display_names') as mock_method: mock_method.return_value = providers_list api.set_credit_requirement_status( "bob", self.course_key, requirements[1]["namespace"], requirements[1]["name"] ) # Now the user should be eligible self.assertTrue(api.is_user_eligible_for_credit("bob", self.course_key)) # Credit eligibility email should be sent self.assertEqual(len(mail.outbox), 1) # Verify the email subject self.assertEqual(mail.outbox[0].subject, expected_subject) # Now verify them email content email_payload_first = mail.outbox[0].attachments[0]._payload # pylint: disable=protected-access html_content_first = email_payload_first[0]._payload[1]._payload # pylint: disable=protected-access self.assertIn(providers_email_message, html_content_first) # test text email text_content_first = email_payload_first[0]._payload[0]._payload # pylint: disable=protected-access self.assertIn(providers_email_message, text_content_first)
def test_remove_credit_requirement_status(self): self.add_credit_course() requirements = [ {"namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": {"min_grade": 0.8}}, { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {}, }, ] api.set_credit_requirements(self.course_key, requirements) course_requirements = api.get_credit_requirements(self.course_key) self.assertEqual(len(course_requirements), 2) # before setting credit_requirement_status api.remove_credit_requirement_status("staff", self.course_key, "grade", "grade") req_status = api.get_credit_requirement_status(self.course_key, "staff", namespace="grade", name="grade") self.assertIsNone(req_status[0]["status"]) self.assertIsNone(req_status[0]["status_date"]) self.assertIsNone(req_status[0]["reason"]) # Set the requirement to "satisfied" and check that it's actually set api.set_credit_requirement_status("staff", self.course_key, "grade", "grade") req_status = api.get_credit_requirement_status(self.course_key, "staff", namespace="grade", name="grade") self.assertEqual(len(req_status), 1) self.assertEqual(req_status[0]["status"], "satisfied") # remove the credit requirement status and check that it's actually removed api.remove_credit_requirement_status("staff", self.course_key, "grade", "grade") req_status = api.get_credit_requirement_status(self.course_key, "staff", namespace="grade", name="grade") self.assertIsNone(req_status[0]["status"]) self.assertIsNone(req_status[0]["status_date"]) self.assertIsNone(req_status[0]["reason"])
def test_set_get_credit_requirements(self): # Test that if same requirement is added multiple times self.add_credit_course() requirements = [ {"namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": {"min_grade": 0.8}}, {"namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": {"min_grade": 0.9}}, ] api.set_credit_requirements(self.course_key, requirements) self.assertEqual(len(api.get_credit_requirements(self.course_key)), 1)
def test_set_credit_requirements_invalid_course(self): # Test that 'InvalidCreditCourse' exception is raise if we try to # set credit requirements for a non credit course. requirements = [{"namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": {}}] with self.assertRaises(InvalidCreditCourse): api.set_credit_requirements(self.course_key, requirements) self.add_credit_course(enabled=False) with self.assertRaises(InvalidCreditCourse): api.set_credit_requirements(self.course_key, requirements)
def test_satisfy_all_requirements(self): # Configure a course with two credit requirements self.add_credit_course() requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 }, }, { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {}, } ] api.set_credit_requirements(self.course_key, requirements) # Satisfy one of the requirements, but not the other with self.assertNumQueries(7): api.set_credit_requirement_status( "bob", self.course_key, requirements[0]["namespace"], requirements[0]["name"] ) # The user should not be eligible (because only one requirement is satisfied) self.assertFalse(api.is_user_eligible_for_credit("bob", self.course_key)) # Satisfy the other requirement with self.assertNumQueries(10): api.set_credit_requirement_status( "bob", self.course_key, requirements[1]["namespace"], requirements[1]["name"] ) # Now the user should be eligible self.assertTrue(api.is_user_eligible_for_credit("bob", self.course_key)) # The user should remain eligible even if the requirement status is later changed api.set_credit_requirement_status( "bob", self.course_key, requirements[0]["namespace"], requirements[0]["name"], status="failed" ) self.assertTrue(api.is_user_eligible_for_credit("bob", self.course_key))
def test_set_credit_requirements_invalid_course(self): requirements = [ { "namespace": "grade", "name": "grade", "criteria": {} } ] with self.assertRaises(InvalidCreditCourse): set_credit_requirements(self.course_key, requirements) self.add_credit_course(enabled=False) with self.assertRaises(InvalidCreditCourse): set_credit_requirements(self.course_key, requirements)
def test_requirements_to_disable(self): self.add_credit_course() requirements = [ { "namespace": "grade", "name": "grade", "criteria": { "min_grade": 0.8 } }, { "namespace": "grade", "name": "grade", "criteria": { "min_grade": 0.8 } } ] set_credit_requirements(self.course_key, requirements) old_requirements = CreditRequirement.get_course_requirements(self.course_key) self.assertEqual(len(old_requirements), 1) requirements = [ { "namespace": "reverification", "name": "midterm", "criteria": {} } ] requirements_to_disabled = _get_requirements_to_disable(old_requirements, requirements) self.assertEqual(len(requirements_to_disabled), 1) self.assertEqual(requirements_to_disabled[0], old_requirements[0].id) requirements = [ { "namespace": "grade", "name": "grade", "criteria": { "min_grade": 0.8 } }, { "namespace": "reverification", "name": "midterm", "criteria": {} } ] requirements_to_disabled = _get_requirements_to_disable(old_requirements, requirements) self.assertEqual(len(requirements_to_disabled), 0)
def test_set_credit_requirement_status_req_not_configured(self): # Configure a credit course with no requirements self.add_credit_course() # A user satisfies a requirement. This could potentially # happen if there's a lag when the requirements are updated # after the course is published. api.set_credit_requirement_status("bob", self.course_key, "grade", "grade") # Since the requirement hasn't been published yet, it won't show # up in the list of requirements. req_status = api.get_credit_requirement_status(self.course_key, "bob", namespace="grade", name="grade") self.assertEqual(req_status, []) # Now add the requirements, simulating what happens when a course is published. requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 }, }, { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {}, } ] api.set_credit_requirements(self.course_key, requirements) # The user should not have satisfied the requirements, since they weren't # in effect when the user completed the requirement req_status = api.get_credit_requirement_status(self.course_key, "bob") self.assertEqual(len(req_status), 2) self.assertEqual(req_status[0]["status"], None) self.assertEqual(req_status[0]["status"], None) # The user should *not* have satisfied the reverification requirement req_status = api.get_credit_requirement_status( self.course_key, "bob", namespace=requirements[1]["namespace"], name=requirements[1]["name"] ) self.assertEqual(len(req_status), 1) self.assertEqual(req_status[0]["status"], None)
def test_requirements_to_disable(self): self.add_credit_course() requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 } } ] set_credit_requirements(self.course_key, requirements) old_requirements = CreditRequirement.get_course_requirements(self.course_key) self.assertEqual(len(old_requirements), 1) requirements = [ { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {} } ] requirements_to_disabled = _get_requirements_to_disable(old_requirements, requirements) self.assertEqual(len(requirements_to_disabled), 1) self.assertEqual(requirements_to_disabled[0], old_requirements[0].id) requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 } }, { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {} } ] requirements_to_disabled = _get_requirements_to_disable(old_requirements, requirements) self.assertEqual(len(requirements_to_disabled), 0)
def setUp(self): super(ProgressPageCreditRequirementsTest, self).setUp() # Create a course and configure it as a credit course self.course = CourseFactory.create() CreditCourse.objects.create(course_key=self.course.id, enabled=True) # Configure credit requirements (passing grade and in-course reverification) credit_api.set_credit_requirements( self.course.id, [ { "namespace": "grade", "name": "grade", "display_name": self.MIN_GRADE_REQ_DISPLAY, "criteria": { "min_grade": 0.8 } }, { "namespace": "reverification", "name": "midterm", "display_name": self.VERIFICATION_REQ_DISPLAY, "criteria": {} } ] ) # Create a user and log in self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD) self.user.profile.name = self.USER_FULL_NAME self.user.profile.save() result = self.client.login(username=self.USERNAME, password=self.PASSWORD) self.assertTrue(result, msg="Could not log in") # Enroll the user in the course as "verified" self.enrollment = CourseEnrollmentFactory( user=self.user, course_id=self.course.id, mode="verified" )
def test_min_grade_credit_requirements_status(self, mode): """ Test for credit course. If user passes minimum grade requirement then status will be updated as satisfied in requirement status table. """ self.basic_setup() # Enroll student in credit eligible mode. # Note that we can't call self.enroll here since that goes through # the Django student views, and does not update enrollment if it already exists. CourseEnrollment.enroll(self.student_user, self.course.id, mode) # Enable the course for credit CreditCourse.objects.create(course_key=self.course.id, enabled=True) # Configure a credit provider for the course CreditProvider.objects.create( provider_id="ASU", enable_integration=True, provider_url="https://credit.example.com/request", ) requirements = [{ "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": {"min_grade": 0.52}, }] # Add a single credit requirement (final grade) set_credit_requirements(self.course.id, requirements) # Credit requirement is not satisfied before passing grade req_status = get_credit_requirement_status(self.course.id, self.student_user.username, 'grade', 'grade') self.assertEqual(req_status[0]["status"], None) self._stop_signal_patch() self.submit_question_answer('p1', {'2_1': 'Correct'}) self.submit_question_answer('p2', {'2_1': 'Correct'}) # Credit requirement is now satisfied after passing grade req_status = get_credit_requirement_status(self.course.id, self.student_user.username, 'grade', 'grade') self.assertEqual(req_status[0]["status"], 'satisfied')
def test_set_get_credit_requirements(self): self.add_credit_course() requirements = [ { "namespace": "grade", "name": "grade", "criteria": { "min_grade": 0.8 } }, { "namespace": "grade", "name": "grade", "criteria": { "min_grade": 0.8 } } ] set_credit_requirements(self.course_key, requirements) self.assertEqual(len(get_credit_requirements(self.course_key)), 1)
def setUp(self): super(ProgressPageCreditRequirementsTest, self).setUp() # Configure course as a credit course CreditCourse.objects.create(course_key=self.course.id, enabled=True) # Configure credit requirements (passing grade and in-course reverification) credit_api.set_credit_requirements( self.course.id, [ { "namespace": "grade", "name": "grade", "display_name": self.MIN_GRADE_REQ_DISPLAY, "criteria": { "min_grade": 0.8 } }, { "namespace": "reverification", "name": "midterm", "display_name": self.VERIFICATION_REQ_DISPLAY, "criteria": {} } ] ) # Create a user and log in self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD) self.user.profile.name = self.USER_FULL_NAME self.user.profile.save() result = self.client.login(username=self.USERNAME, password=self.PASSWORD) self.assertTrue(result, msg="Could not log in") # Enroll the user in the course as "verified" self.enrollment = CourseEnrollmentFactory( user=self.user, course_id=self.course.id, mode="verified" )
def test_min_grade_credit_requirements_status(self, mode): """ Test for credit course. If user passes minimum grade requirement then status will be updated as satisfied in requirement status table. """ self.basic_setup() # Enroll student in credit eligible mode. # Note that we can't call self.enroll here since that goes through # the Django student views, and does not update enrollment if it already exists. CourseEnrollment.enroll(self.student_user, self.course.id, mode) # Enable the course for credit CreditCourse.objects.create(course_key=self.course.id, enabled=True) # Configure a credit provider for the course CreditProvider.objects.create( provider_id="ASU", enable_integration=True, provider_url="https://credit.example.com/request", ) requirements = [{ "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": {"min_grade": 0.52}, }] # Add a single credit requirement (final grade) set_credit_requirements(self.course.id, requirements) # Credit requirement is not satisfied before passing grade req_status = get_credit_requirement_status(self.course.id, self.student_user.username, 'grade', 'grade') assert req_status[0]['status'] is None self.submit_question_answer('p1', {'2_1': 'Correct'}) self.submit_question_answer('p2', {'2_1': 'Correct'}) # Credit requirement is now satisfied after passing grade req_status = get_credit_requirement_status(self.course.id, self.student_user.username, 'grade', 'grade') assert req_status[0]['status'] == 'satisfied'
def setUp(self): """Create a course and an enrollment. """ super(CreditCourseDashboardTest, self).setUp() # Create a user and log in self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD) result = self.client.login(username=self.USERNAME, password=self.PASSWORD) self.assertTrue(result, msg="Could not log in") # Create a course and configure it as a credit course self.course = CourseFactory() CreditCourse.objects.create(course_key=self.course.id, enabled=True) # pylint: disable=no-member # Configure a credit provider CreditProvider.objects.create( provider_id=self.PROVIDER_ID, display_name=self.PROVIDER_NAME, provider_status_url=self.PROVIDER_STATUS_URL, enable_integration=True, ) # Configure a single credit requirement (minimum passing grade) credit_api.set_credit_requirements( self.course.id, # pylint: disable=no-member [{ "namespace": "grade", "name": "grade", "display_name": "Final Grade", "criteria": { "min_grade": 0.8 } }]) # Enroll the user in the course as "verified" self.enrollment = CourseEnrollmentFactory( user=self.user, course_id=self.course.id, # pylint: disable=no-member mode="verified")
def setUp(self): super().setUp() self.course = CourseFactory.create(org='Robot', number='999', display_name='Test Course') self.user = UserFactory() self.request = RequestFactory().get('/') self.request.user = self.user self.client.login(username=self.user.username, password=self.user.password) # Enable the course for credit CreditCourse.objects.create( course_key=self.course.id, enabled=True, ) # Configure a credit provider for the course CreditProvider.objects.create( provider_id="ASU", enable_integration=True, provider_url="https://credit.example.com/request", ) requirements = [{ "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.52 }, }] # Add a single credit requirement (final grade) set_credit_requirements(self.course.id, requirements) # Enroll user in verified mode. self.enrollment = CourseEnrollment.enroll(self.user, self.course.id, mode=CourseMode.VERIFIED)
def update_credit_course_requirements(course_id): """ Updates course requirements table for a course. Args: course_id(str): A string representation of course identifier Returns: None """ try: course_key = CourseKey.from_string(course_id) is_credit_course = CreditCourse.is_credit_course(course_key) if is_credit_course: requirements = _get_course_credit_requirements(course_key) set_credit_requirements(course_key, requirements) except (InvalidKeyError, ItemNotFoundError, InvalidCreditRequirements) as exc: LOGGER.error('Error on adding the requirements for course %s - %s', course_id, str(exc)) raise update_credit_course_requirements.retry(args=[course_id], exc=exc) else: LOGGER.info('Requirements added for course %s', course_id)
def update_credit_course_requirements(course_id): """ Updates course requirements table for a course. Args: course_id(str): A string representation of course identifier Returns: None """ try: course_key = CourseKey.from_string(course_id) is_credit_course = CreditCourse.is_credit_course(course_key) if is_credit_course: requirements = _get_course_credit_requirements(course_key) set_credit_requirements(course_key, requirements) except (InvalidKeyError, ItemNotFoundError, InvalidCreditRequirements) as exc: LOGGER.error(u'Error on adding the requirements for course %s - %s', course_id, six.text_type(exc)) raise update_credit_course_requirements.retry(args=[course_id], exc=exc) else: LOGGER.info(u'Requirements added for course %s', course_id)
def test_set_credit_requirement_status(self): self.add_credit_course() requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 } }, { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {} } ] set_credit_requirements(self.course_key, requirements) course_requirements = CreditRequirement.get_course_requirements(self.course_key) self.assertEqual(len(course_requirements), 2) requirement = get_credit_requirement(self.course_key, "grade", "grade") set_credit_requirement_status("staff", requirement, 'satisfied', {}) course_requirement = CreditRequirement.get_course_requirement( requirement['course_key'], requirement['namespace'], requirement['name'] ) status = CreditRequirementStatus.objects.get(username="******", requirement=course_requirement) self.assertEqual(status.requirement.namespace, requirement['namespace']) self.assertEqual(status.status, "satisfied") set_credit_requirement_status( "staff", requirement, 'failed', {'failure_reason': "requirements not satisfied"} ) status = CreditRequirementStatus.objects.get(username="******", requirement=course_requirement) self.assertEqual(status.requirement.namespace, requirement['namespace']) self.assertEqual(status.status, "failed")
def test_min_grade_credit_requirements_status(self): """ Test for credit course. If user passes minimum grade requirement then status will be updated as satisfied in requirement status table. """ self.basic_setup() self.submit_question_answer('p1', {'2_1': 'Correct'}) self.submit_question_answer('p2', {'2_1': 'Correct'}) # Enable the course for credit credit_course = CreditCourse.objects.create( # pylint: disable=unused-variable course_key=self.course.id, enabled=True, ) # Configure a credit provider for the course CreditProvider.objects.create( provider_id="ASU", enable_integration=True, provider_url="https://credit.example.com/request", ) requirements = [{ "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.52 }, }] # Add a single credit requirement (final grade) set_credit_requirements(self.course.id, requirements) self.get_grade_summary() req_status = get_credit_requirement_status(self.course.id, self.student_user.username, 'grade', 'grade') self.assertEqual(req_status[0]["status"], 'satisfied')
def test_set_get_credit_requirements(self): # Test that if same requirement is added multiple times self.add_credit_course() requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 }, }, { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.9 }, } ] api.set_credit_requirements(self.course_key, requirements) self.assertEqual(len(api.get_credit_requirements(self.course_key)), 1)
def test_set_credit_requirement_status(self): self.add_credit_course() requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 }, }, { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {}, } ] api.set_credit_requirements(self.course_key, requirements) course_requirements = api.get_credit_requirements(self.course_key) self.assertEqual(len(course_requirements), 2) # Initially, the status should be None req_status = api.get_credit_requirement_status(self.course_key, "staff", namespace="grade", name="grade") self.assertEqual(req_status[0]["status"], None) # Set the requirement to "satisfied" and check that it's actually set api.set_credit_requirement_status("staff", self.course_key, "grade", "grade") req_status = api.get_credit_requirement_status(self.course_key, "staff", namespace="grade", name="grade") self.assertEqual(req_status[0]["status"], "satisfied") # Set the requirement to "failed" and check that it's actually set api.set_credit_requirement_status("staff", self.course_key, "grade", "grade", status="failed") req_status = api.get_credit_requirement_status(self.course_key, "staff", namespace="grade", name="grade") self.assertEqual(req_status[0]["status"], "failed")
def test_satisfy_all_requirements(self): """ Test the credit requirements, eligibility notification, email content caching for a credit course. """ # Configure a course with two credit requirements self.add_credit_course() CourseFactory.create(org='edX', number='DemoX', display_name='Demo_Course') requirements = [{ "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 }, }, { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {}, }] api.set_credit_requirements(self.course_key, requirements) user = UserFactory.create(username=self.USER_INFO['username'], password=self.USER_INFO['password']) # Satisfy one of the requirements, but not the other with self.assertNumQueries(7): api.set_credit_requirement_status(user.username, self.course_key, requirements[0]["namespace"], requirements[0]["name"]) # The user should not be eligible (because only one requirement is satisfied) self.assertFalse( api.is_user_eligible_for_credit("bob", self.course_key)) # Satisfy the other requirement with self.assertNumQueries(11): api.set_credit_requirement_status("bob", self.course_key, requirements[1]["namespace"], requirements[1]["name"]) # Now the user should be eligible self.assertTrue(api.is_user_eligible_for_credit( "bob", self.course_key)) # Credit eligibility email should be sent self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].subject, 'Course Credit Eligibility') # Now verify them email content email_payload_first = mail.outbox[0].attachments[0]._payload # pylint: disable=protected-access # Test that email has two payloads [multipart (plain text and html # content), attached image] self.assertEqual(len(email_payload_first), 2) # pylint: disable=protected-access self.assertIn('text/plain', email_payload_first[0]._payload[0]['Content-Type']) # pylint: disable=protected-access self.assertIn('text/html', email_payload_first[0]._payload[1]['Content-Type']) self.assertIn('image/png', email_payload_first[1]['Content-Type']) # Now check that html email content has same logo image 'Content-ID' # as the attached logo image 'Content-ID' email_image = email_payload_first[1] html_content_first = email_payload_first[0]._payload[1]._payload # pylint: disable=protected-access # strip enclosing angle brackets from 'logo_image' cache 'Content-ID' image_id = email_image.get('Content-ID', '')[1:-1] self.assertIsNotNone(image_id) self.assertIn(image_id, html_content_first) # Delete the eligibility entries and satisfy the user's eligibility # requirement again to trigger eligibility notification CreditEligibility.objects.all().delete() with self.assertNumQueries(12): api.set_credit_requirement_status("bob", self.course_key, requirements[1]["namespace"], requirements[1]["name"]) # Credit eligibility email should be sent self.assertEqual(len(mail.outbox), 2) # Now check that on sending eligibility notification again cached # logo image is used email_payload_second = mail.outbox[1].attachments[0]._payload # pylint: disable=protected-access html_content_second = email_payload_second[0]._payload[1]._payload # pylint: disable=protected-access self.assertIn(image_id, html_content_second) # The user should remain eligible even if the requirement status is later changed api.set_credit_requirement_status("bob", self.course_key, requirements[0]["namespace"], requirements[0]["name"], status="failed") self.assertTrue(api.is_user_eligible_for_credit( "bob", self.course_key))
def test_set_credit_requirements_invalid_requirements(self, requirements): self.add_credit_course() with self.assertRaises(InvalidCreditRequirements): api.set_credit_requirements(self.course_key, requirements)
def test_satisfy_all_requirements(self): # Configure a course with two credit requirements self.add_credit_course() CourseFactory.create(org='edX', number='DemoX', display_name='Demo_Course') requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 }, }, { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {}, } ] api.set_credit_requirements(self.course_key, requirements) user = UserFactory.create(username=self.USER_INFO['username'], password=self.USER_INFO['password']) # Satisfy one of the requirements, but not the other with self.assertNumQueries(7): api.set_credit_requirement_status( user.username, self.course_key, requirements[0]["namespace"], requirements[0]["name"] ) # The user should not be eligible (because only one requirement is satisfied) self.assertFalse(api.is_user_eligible_for_credit("bob", self.course_key)) # Satisfy the other requirement with self.assertNumQueries(11): api.set_credit_requirement_status( "bob", self.course_key, requirements[1]["namespace"], requirements[1]["name"] ) # Now the user should be eligible self.assertTrue(api.is_user_eligible_for_credit("bob", self.course_key)) # Credit eligible mail should be sent self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].subject, 'Course Credit Eligibility') # The user should remain eligible even if the requirement status is later changed api.set_credit_requirement_status( "bob", self.course_key, requirements[0]["namespace"], requirements[0]["name"], status="failed" ) self.assertTrue(api.is_user_eligible_for_credit("bob", self.course_key))
def test_set_credit_requirement_status(self): username = "******" credit_course = self.add_credit_course() requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 }, }, { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {}, } ] api.set_credit_requirements(self.course_key, requirements) course_requirements = api.get_credit_requirements(self.course_key) self.assertEqual(len(course_requirements), 2) # Initially, the status should be None self.assert_grade_requirement_status(None, 0) # Requirement statuses cannot be changed if a CreditRequest exists credit_request = CreditRequest.objects.create( course=credit_course, provider=CreditProvider.objects.first(), username=username, ) api.set_credit_requirement_status(username, self.course_key, "grade", "grade") self.assert_grade_requirement_status(None, 0) credit_request.delete() # Set the requirement to "satisfied" and check that it's actually set api.set_credit_requirement_status(username, self.course_key, "grade", "grade") self.assert_grade_requirement_status('satisfied', 0) # Set the requirement to "failed" and check that it's actually set api.set_credit_requirement_status(username, self.course_key, "grade", "grade", status="failed") self.assert_grade_requirement_status('failed', 0) req_status = api.get_credit_requirement_status(self.course_key, "staff") self.assertEqual(req_status[0]["status"], "failed") self.assertEqual(req_status[0]["order"], 0) # make sure the 'order' on the 2nd requirement is set correctly (aka 1) self.assertEqual(req_status[1]["status"], None) self.assertEqual(req_status[1]["order"], 1) # Set the requirement to "declined" and check that it's actually set api.set_credit_requirement_status( username, self.course_key, "reverification", "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", status="declined" ) req_status = api.get_credit_requirement_status( self.course_key, username, namespace="reverification", name="i4x://edX/DemoX/edx-reverification-block/assessment_uuid" ) self.assertEqual(req_status[0]["status"], "declined")
def test_satisfy_all_requirements(self): """ Test the credit requirements, eligibility notification, email content caching for a credit course. """ # Configure a course with two credit requirements self.add_credit_course() CourseFactory.create(org='edX', number='DemoX', display_name='Demo_Course') requirements = [ { "namespace": "grade", "name": "grade", "display_name": "Grade", "criteria": { "min_grade": 0.8 }, }, { "namespace": "reverification", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid", "display_name": "Assessment 1", "criteria": {}, } ] api.set_credit_requirements(self.course_key, requirements) user = UserFactory.create(username=self.USER_INFO['username'], password=self.USER_INFO['password']) # Satisfy one of the requirements, but not the other with self.assertNumQueries(11): api.set_credit_requirement_status( user.username, self.course_key, requirements[0]["namespace"], requirements[0]["name"] ) # The user should not be eligible (because only one requirement is satisfied) self.assertFalse(api.is_user_eligible_for_credit("bob", self.course_key)) # Satisfy the other requirement with self.assertNumQueries(15): api.set_credit_requirement_status( "bob", self.course_key, requirements[1]["namespace"], requirements[1]["name"] ) # Now the user should be eligible self.assertTrue(api.is_user_eligible_for_credit("bob", self.course_key)) # Credit eligibility email should be sent self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].subject, 'Course Credit Eligibility') # Now verify them email content email_payload_first = mail.outbox[0].attachments[0]._payload # pylint: disable=protected-access # Test that email has two payloads [multipart (plain text and html # content), attached image] self.assertEqual(len(email_payload_first), 2) # pylint: disable=protected-access self.assertIn('text/plain', email_payload_first[0]._payload[0]['Content-Type']) # pylint: disable=protected-access self.assertIn('text/html', email_payload_first[0]._payload[1]['Content-Type']) self.assertIn('image/png', email_payload_first[1]['Content-Type']) # Now check that html email content has same logo image 'Content-ID' # as the attached logo image 'Content-ID' email_image = email_payload_first[1] html_content_first = email_payload_first[0]._payload[1]._payload # pylint: disable=protected-access # strip enclosing angle brackets from 'logo_image' cache 'Content-ID' image_id = email_image.get('Content-ID', '')[1:-1] self.assertIsNotNone(image_id) self.assertIn(image_id, html_content_first) # Delete the eligibility entries and satisfy the user's eligibility # requirement again to trigger eligibility notification CreditEligibility.objects.all().delete() with self.assertNumQueries(13): api.set_credit_requirement_status( "bob", self.course_key, requirements[1]["namespace"], requirements[1]["name"] ) # Credit eligibility email should be sent self.assertEqual(len(mail.outbox), 2) # Now check that on sending eligibility notification again cached # logo image is used email_payload_second = mail.outbox[1].attachments[0]._payload # pylint: disable=protected-access html_content_second = email_payload_second[0]._payload[1]._payload # pylint: disable=protected-access self.assertIn(image_id, html_content_second) # The user should remain eligible even if the requirement status is later changed api.set_credit_requirement_status( "bob", self.course_key, requirements[0]["namespace"], requirements[0]["name"], status="failed" ) self.assertTrue(api.is_user_eligible_for_credit("bob", self.course_key))
def test_set_credit_requirements_invalid_requirements(self, requirements): self.add_credit_course() with self.assertRaises(InvalidCreditRequirements): set_credit_requirements(self.course_key, requirements)