def test_credit_requirements_eligible(self):
        # Mark the user as eligible for all requirements
        credit_api.set_credit_requirement_status(self.user.username,
                                                 self.course.id,
                                                 "grade",
                                                 "grade",
                                                 status="satisfied",
                                                 reason={"final_grade": 0.95})

        credit_api.set_credit_requirement_status(self.user.username,
                                                 self.course.id,
                                                 "reverification",
                                                 "midterm",
                                                 status="satisfied",
                                                 reason={})

        # Check the progress page display
        response = self._get_progress_page()
        self.assertContains(response, self.MIN_GRADE_REQ_DISPLAY)
        self.assertContains(response, self.VERIFICATION_REQ_DISPLAY)
        self.assertContains(
            response,
            "{}, you have met the requirements for credit in this course.".
            format(self.USER_FULL_NAME))
        self.assertContains(
            response,
            "Verified on {date}".format(date=self._now_formatted_date()))
        self.assertContains(response, "95%")
Ejemplo n.º 2
0
    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_credit_requirements_eligible(self):
        # Mark the user as eligible for all requirements
        credit_api.set_credit_requirement_status(
            self.user.username, self.course.id,
            "grade", "grade",
            status="satisfied",
            reason={"final_grade": 0.95}
        )

        credit_api.set_credit_requirement_status(
            self.user.username, self.course.id,
            "reverification", "midterm",
            status="satisfied", reason={}
        )

        # Check the progress page display
        response = self._get_progress_page()
        self.assertContains(response, self.MIN_GRADE_REQ_DISPLAY)
        self.assertContains(response, self.VERIFICATION_REQ_DISPLAY)
        self.assertContains(
            response,
            "{}, you have met the requirements for credit in this course.".format(self.USER_FULL_NAME)
        )
        self.assertContains(response, "Verified on {date}".format(date=self._now_formatted_date()))
        self.assertContains(response, "95%")
    def test_credit_requirements_eligible(self):
        """
        Mark the user as eligible for all requirements. Requirements are only displayed
        for credit and verified enrollments.
        """
        credit_api.set_credit_requirement_status(
            self.user, self.course.id,
            "grade", "grade",
            status="satisfied",
            reason={"final_grade": 0.95}
        )

        credit_api.set_credit_requirement_status(
            self.user, self.course.id,
            "reverification", "midterm",
            status="satisfied", reason={}
        )

        # Check the progress page display
        response = self._get_progress_page()
        self.assertContains(response, self.MIN_GRADE_REQ_DISPLAY)
        self.assertContains(response, self.VERIFICATION_REQ_DISPLAY)
        self.assertContains(
            response,
            "{}, you have met the requirements for credit in this course.".format(self.USER_FULL_NAME)
        )
        self.assertContains(response, "Completed by {date}")

        credit_requirements = credit_api.get_credit_requirement_status(self.course.id, self.user.username)
        for requirement in credit_requirements:
            self.assertContains(response, requirement['status_date'].strftime('%Y-%m-%d %H:%M'))
        self.assertNotContains(response, "95%")
    def test_credit_requirements_eligible(self):
        """
        Mark the user as eligible for all requirements. Requirements are only displayed
        for credit and verified enrollments.
        """
        credit_api.set_credit_requirement_status(
            self.user, self.course.id,
            "grade", "grade",
            status="satisfied",
            reason={"final_grade": 0.95}
        )

        credit_api.set_credit_requirement_status(
            self.user, self.course.id,
            "reverification", "midterm",
            status="satisfied", reason={}
        )

        # Check the progress page display
        response = self._get_progress_page()
        self.assertContains(response, self.MIN_GRADE_REQ_DISPLAY)
        self.assertContains(response, self.VERIFICATION_REQ_DISPLAY)
        self.assertContains(
            response,
            f"{self.USER_FULL_NAME}, you have met the requirements for credit in this course."
        )
        self.assertContains(response, "Completed by {date}")

        credit_requirements = credit_api.get_credit_requirement_status(self.course.id, self.user.username)
        for requirement in credit_requirements:
            self.assertContains(response, requirement['status_date'].strftime('%Y-%m-%d %H:%M'))
        self.assertNotContains(response, "95%")
Ejemplo n.º 6
0
    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)
Ejemplo n.º 7
0
    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")
Ejemplo n.º 8
0
    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"])
Ejemplo n.º 9
0
 def _make_eligible(self):
     """Make the user eligible for credit in the course. """
     credit_api.set_credit_requirement_status(
         self.user,
         self.course.id,  # pylint: disable=no-member
         "grade",
         "grade",
         status="satisfied",
         reason={"final_grade": 0.95})
Ejemplo n.º 10
0
 def _make_eligible(self):
     """Make the user eligible for credit in the course. """
     credit_api.set_credit_requirement_status(
         self.USERNAME,
         self.course.id,  # pylint: disable=no-member
         "grade",
         "grade",
         status="satisfied",
         reason={"final_grade": 0.95},
     )
Ejemplo n.º 11
0
    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"])
Ejemplo n.º 12
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)
Ejemplo n.º 13
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)
Ejemplo n.º 14
0
    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_credit_requirements_not_eligible(self):
        # Mark the user as having failed both requirements
        credit_api.set_credit_requirement_status(
            self.user.username, self.course.id,
            "reverification", "midterm",
            status="failed", reason={}
        )

        # Check the progress page display
        response = self._get_progress_page()
        self.assertContains(response, self.MIN_GRADE_REQ_DISPLAY)
        self.assertContains(response, self.VERIFICATION_REQ_DISPLAY)
        self.assertContains(
            response,
            "{}, you are no longer eligible for credit in this course.".format(self.USER_FULL_NAME)
        )
        self.assertContains(response, "Verification Failed")
Ejemplo n.º 16
0
    def skip_verification(self, user_id, course_id,
                          related_assessment_location):
        """Add skipped verification attempt entry for a user against a given
        'checkpoint'.

        Args:
            user_id(str): User Id string
            course_id(str): A string of course_id
            related_assessment_location(str): Location of Reverification XBlock

        Returns:
            None
        """
        course_key = CourseKey.from_string(course_id)
        checkpoint = VerificationCheckpoint.objects.get(
            course_id=course_key,
            checkpoint_location=related_assessment_location)
        user = User.objects.get(id=user_id)

        # user can skip a reverification attempt only if that user has not already
        # skipped an attempt
        try:
            SkippedReverification.add_skipped_reverification_attempt(
                checkpoint, user_id, course_key)
        except IntegrityError:
            log.exception(
                "Skipped attempt already exists for user %s: with course %s:",
                user_id, unicode(course_id))
            return

        try:
            # Avoid circular import
            from openedx.core.djangoapps.credit.api import set_credit_requirement_status

            # As a user skips the reverification it declines to fulfill the requirement so
            # requirement sets to declined.
            set_credit_requirement_status(user,
                                          course_key,
                                          'reverification',
                                          checkpoint.checkpoint_location,
                                          status='declined')

        except Exception as err:  # pylint: disable=broad-except
            log.error(
                "Unable to add credit requirement status for user with id %d: %s",
                user_id, err)
Ejemplo n.º 17
0
    def test_credit_requirements_not_eligible(self):
        # Mark the user as having failed both requirements
        credit_api.set_credit_requirement_status(
            self.user.username, self.course.id,
            "reverification", "midterm",
            status="failed", reason={}
        )

        # Check the progress page display
        response = self._get_progress_page()
        self.assertContains(response, self.MIN_GRADE_REQ_DISPLAY)
        self.assertContains(response, self.VERIFICATION_REQ_DISPLAY)
        self.assertContains(
            response,
            "{}, you are no longer eligible for credit in this course.".format(self.USER_FULL_NAME)
        )
        self.assertContains(response, "Verification Failed")
Ejemplo n.º 18
0
def listen_for_grade_calculation(sender, username, grade_summary, course_key,
                                 deadline, **kwargs):  # pylint: disable=unused-argument
    """Receive 'MIN_GRADE_REQUIREMENT_STATUS' signal and update minimum grade
    requirement status.

    Args:
        sender: None
        username(string): user name
        grade_summary(dict): Dict containing output from the course grader
        course_key(CourseKey): The key for the course
        deadline(datetime): Course end date or None

    Kwargs:
        kwargs : None

    """
    # This needs to be imported here to avoid a circular dependency
    # that can cause syncdb to fail.
    from openedx.core.djangoapps.credit import api

    course_id = CourseKey.from_string(unicode(course_key))
    is_credit = api.is_credit_course(course_id)
    if is_credit:
        requirements = api.get_credit_requirements(course_id,
                                                   namespace='grade')
        if requirements:
            criteria = requirements[0].get('criteria')
            if criteria:
                min_grade = criteria.get('min_grade')
                if grade_summary['percent'] >= min_grade:
                    reason_dict = {'final_grade': grade_summary['percent']}
                    api.set_credit_requirement_status(username,
                                                      course_id,
                                                      'grade',
                                                      'grade',
                                                      status="satisfied",
                                                      reason=reason_dict)
                elif deadline and deadline < timezone.now():
                    api.set_credit_requirement_status(username,
                                                      course_id,
                                                      'grade',
                                                      'grade',
                                                      status="failed",
                                                      reason={})
Ejemplo n.º 19
0
    def skip_verification(self, user_id, course_id, related_assessment_location):
        """Add skipped verification attempt entry for a user against a given
        'checkpoint'.

        Args:
            user_id(str): User Id string
            course_id(str): A string of course_id
            related_assessment_location(str): Location of Reverification XBlock

        Returns:
            None
        """
        course_key = CourseKey.from_string(course_id)
        checkpoint = VerificationCheckpoint.objects.get(
            course_id=course_key,
            checkpoint_location=related_assessment_location
        )
        user = User.objects.get(id=user_id)

        # user can skip a reverification attempt only if that user has not already
        # skipped an attempt
        try:
            SkippedReverification.add_skipped_reverification_attempt(checkpoint, user_id, course_key)
        except IntegrityError:
            log.exception("Skipped attempt already exists for user %s: with course %s:", user_id, unicode(course_id))
            return

        try:
            # Avoid circular import
            from openedx.core.djangoapps.credit.api import set_credit_requirement_status

            # As a user skips the reverification it declines to fulfill the requirement so
            # requirement sets to declined.
            set_credit_requirement_status(
                user.username,
                course_key,
                'reverification',
                checkpoint.checkpoint_location,
                status='declined'
            )

        except Exception as err:  # pylint: disable=broad-except
            log.error("Unable to add credit requirement status for user with id %d: %s", user_id, err)
    def test_credit_requirements_not_eligible(self):
        """
        Mark the user as having failed both requirements. Requirements are only displayed
        for credit and verified enrollments.
        """
        credit_api.set_credit_requirement_status(
            self.user, self.course.id,
            "reverification", "midterm",
            status="failed", reason={}
        )

        # Check the progress page display
        response = self._get_progress_page()
        self.assertContains(response, self.MIN_GRADE_REQ_DISPLAY)
        self.assertContains(response, self.VERIFICATION_REQ_DISPLAY)
        self.assertContains(
            response,
            f"{self.USER_FULL_NAME}, you are no longer eligible for credit in this course."
        )
        self.assertContains(response, "Verification Failed")
Ejemplo n.º 21
0
    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")
Ejemplo n.º 22
0
def listen_for_grade_calculation(sender, username, grade_summary, course_key, deadline, **kwargs):  # pylint: disable=unused-argument
    """Receive 'MIN_GRADE_REQUIREMENT_STATUS' signal and update minimum grade
    requirement status.

    Args:
        sender: None
        username(string): user name
        grade_summary(dict): Dict containing output from the course grader
        course_key(CourseKey): The key for the course
        deadline(datetime): Course end date or None

    Kwargs:
        kwargs : None

    """
    # This needs to be imported here to avoid a circular dependency
    # that can cause syncdb to fail.
    from openedx.core.djangoapps.credit import api

    course_id = CourseKey.from_string(unicode(course_key))
    is_credit = api.is_credit_course(course_id)
    if is_credit:
        requirements = api.get_credit_requirements(course_id, namespace='grade')
        if requirements:
            criteria = requirements[0].get('criteria')
            if criteria:
                min_grade = criteria.get('min_grade')
                if grade_summary['percent'] >= min_grade:
                    reason_dict = {'final_grade': grade_summary['percent']}
                    api.set_credit_requirement_status(
                        username, course_id, 'grade', 'grade', status="satisfied", reason=reason_dict
                    )
                elif deadline and deadline < timezone.now():
                    api.set_credit_requirement_status(
                        username, course_id, 'grade', 'grade', status="failed", reason={}
                    )
Ejemplo n.º 23
0
def _set_user_requirement_status(attempt, namespace, status, reason=None):
    """Sets the status of a credit requirement for the user,
    based on a verification checkpoint.
    """
    checkpoint = None
    try:
        checkpoint = VerificationCheckpoint.objects.get(photo_verification=attempt)
    except VerificationCheckpoint.DoesNotExist:
        log.error("Unable to find checkpoint for user with id %d", attempt.user.id)

    if checkpoint is not None:
        try:
            set_credit_requirement_status(
                attempt.user.username,
                checkpoint.course_id,
                namespace,
                checkpoint.checkpoint_location,
                status=status,
                reason=reason,
            )
        except Exception:  # pylint: disable=broad-except
            # Catch exception if unable to add credit requirement
            # status for user
            log.error("Unable to add Credit requirement status for user with id %d", attempt.user.id)
Ejemplo n.º 24
0
    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))
Ejemplo n.º 25
0
    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))
Ejemplo n.º 26
0
    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))
Ejemplo n.º 27
0
    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")
Ejemplo n.º 28
0
    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")
Ejemplo n.º 29
0
def listen_for_grade_calculation(sender, user, grade_summary, course_key,
                                 deadline, **kwargs):  # pylint: disable=unused-argument
    """Receive 'MIN_GRADE_REQUIREMENT_STATUS' signal and update minimum grade requirement status.

    Args:
        sender: None
        user(User): User Model object
        grade_summary(dict): Dict containing output from the course grader
        course_key(CourseKey): The key for the course
        deadline(datetime): Course end date or None

    Kwargs:
        kwargs : None

    """
    # This needs to be imported here to avoid a circular dependency
    # that can cause syncdb to fail.
    from openedx.core.djangoapps.credit import api
    course_id = CourseKey.from_string(unicode(course_key))
    is_credit = api.is_credit_course(course_id)
    if is_credit:
        requirements = api.get_credit_requirements(course_id,
                                                   namespace='grade')
        if requirements:
            criteria = requirements[0].get('criteria')
            if criteria:
                min_grade = criteria.get('min_grade')
                passing_grade = grade_summary['percent'] >= min_grade
                now = timezone.now()
                status = None
                reason = None

                if (deadline and now < deadline) or not deadline:
                    # Student completed coursework on-time

                    if passing_grade:
                        # Student received a passing grade
                        status = 'satisfied'
                        reason = {'final_grade': grade_summary['percent']}
                else:
                    # Submission after deadline

                    if passing_grade:
                        # Grade was good, but submission arrived too late
                        status = 'failed'
                        reason = {'current_date': now, 'deadline': deadline}
                    else:
                        # Student failed to receive minimum grade
                        status = 'failed'
                        reason = {
                            'final_grade': grade_summary['percent'],
                            'minimum_grade': min_grade
                        }

                # We do not record a status if the user has not yet earned the minimum grade, but still has
                # time to do so.
                if status and reason:
                    api.set_credit_requirement_status(user,
                                                      course_id,
                                                      'grade',
                                                      'grade',
                                                      status=status,
                                                      reason=reason)
Ejemplo n.º 30
0
    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))
Ejemplo n.º 31
0
    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))
Ejemplo n.º 32
0
def listen_for_grade_calculation(sender, user, course_grade, course_key, deadline, **kwargs):  # pylint: disable=unused-argument
    """Receive 'MIN_GRADE_REQUIREMENT_STATUS' signal and update minimum grade requirement status.

    Args:
        sender: None
        user(User): User Model object
        course_grade(CourseGrade): CourseGrade object
        course_key(CourseKey): The key for the course
        deadline(datetime): Course end date or None

    Kwargs:
        kwargs : None

    """
    # This needs to be imported here to avoid a circular dependency
    # that can cause syncdb to fail.
    from openedx.core.djangoapps.credit import api
    course_id = CourseKey.from_string(unicode(course_key))
    is_credit = api.is_credit_course(course_id)
    if is_credit:
        requirements = api.get_credit_requirements(course_id, namespace='grade')
        if requirements:
            criteria = requirements[0].get('criteria')
            if criteria:
                min_grade = criteria.get('min_grade')
                passing_grade = course_grade.percent >= min_grade
                now = timezone.now()
                status = None
                reason = None

                if (deadline and now < deadline) or not deadline:
                    # Student completed coursework on-time

                    if passing_grade:
                        # Student received a passing grade
                        status = 'satisfied'
                        reason = {'final_grade': course_grade.percent}
                else:
                    # Submission after deadline

                    if passing_grade:
                        # Grade was good, but submission arrived too late
                        status = 'failed'
                        reason = {
                            'current_date': now,
                            'deadline': deadline
                        }
                    else:
                        # Student failed to receive minimum grade
                        status = 'failed'
                        reason = {
                            'final_grade': course_grade.percent,
                            'minimum_grade': min_grade
                        }

                # We do not record a status if the user has not yet earned the minimum grade, but still has
                # time to do so.
                if status and reason:
                    api.set_credit_requirement_status(
                        user, course_id, 'grade', 'grade', status=status, reason=reason
                    )