diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py
index b3ce021568..024a12909f 100644
--- a/lms/djangoapps/courseware/views.py
+++ b/lms/djangoapps/courseware/views.py
@@ -1045,6 +1045,9 @@ def _credit_course_requirements(course_key, student):
if not (settings.FEATURES.get("ENABLE_CREDIT_ELIGIBILITY", False) and is_credit_course(course_key)):
return None
+ # Credit requirement statuses for which user does not remain eligible to get credit.
+ non_eligible_statuses = ['failed', 'declined']
+
# Retrieve the status of the user for each eligibility requirement in the course.
# For each requirement, the user's status is either "satisfied", "failed", or None.
# In this context, `None` means that we don't know the user's status, either because
@@ -1068,7 +1071,7 @@ def _credit_course_requirements(course_key, student):
# If the user has *failed* any requirements (for example, if a photo verification is denied),
# then the user is NOT eligible for credit.
- elif any(requirement['status'] == 'failed' for requirement in requirement_statuses):
+ elif any(requirement['status'] in non_eligible_statuses for requirement in requirement_statuses):
eligibility_status = "not_eligible"
# Otherwise, the user may be eligible for credit, but the user has not
diff --git a/lms/djangoapps/verify_student/services.py b/lms/djangoapps/verify_student/services.py
index cdf0a38df4..0269d8ed3c 100644
--- a/lms/djangoapps/verify_student/services.py
+++ b/lms/djangoapps/verify_student/services.py
@@ -95,6 +95,7 @@ class ReverificationService(object):
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
@@ -102,6 +103,24 @@ class ReverificationService(object):
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 get_attempts(self, user_id, course_id, related_assessment_location):
"""Get re-verification attempts against a user for a given 'checkpoint'
diff --git a/lms/djangoapps/verify_student/tests/test_services.py b/lms/djangoapps/verify_student/tests/test_services.py
index 85568c3d9f..5c5a8b9dc1 100644
--- a/lms/djangoapps/verify_student/tests/test_services.py
+++ b/lms/djangoapps/verify_student/tests/test_services.py
@@ -4,6 +4,8 @@ Tests of re-verification service.
import ddt
+from opaque_keys.edx.keys import CourseKey
+
from course_modes.models import CourseMode
from course_modes.tests.factories import CourseModeFactory
from student.models import CourseEnrollment
@@ -11,6 +13,8 @@ from student.tests.factories import UserFactory
from verify_student.models import VerificationCheckpoint, VerificationStatus, SkippedReverification
from verify_student.services import ReverificationService
+from openedx.core.djangoapps.credit.api import get_credit_requirement_status, set_credit_requirements
+from openedx.core.djangoapps.credit.models import CreditCourse
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
@@ -25,20 +29,22 @@ class TestReverificationService(ModuleStoreTestCase):
super(TestReverificationService, self).setUp()
self.user = UserFactory.create(username="rusty", password="test")
- course = CourseFactory.create(org='Robot', number='999', display_name='Test Course')
- self.course_key = course.id
+ self.course = CourseFactory.create(org='Robot', number='999', display_name='Test Course')
+ self.course_id = self.course.id
CourseModeFactory(
mode_slug="verified",
- course_id=self.course_key,
+ course_id=self.course_id,
min_price=100,
)
- self.item = ItemFactory.create(parent=course, category='chapter', display_name='Test Section')
+ self.course_key = CourseKey.from_string(unicode(self.course_id))
+
+ self.item = ItemFactory.create(parent=self.course, category='chapter', display_name='Test Section')
self.final_checkpoint_location = u'i4x://{org}/{course}/edx-reverification-block/final_uuid'.format(
- org=self.course_key.org, course=self.course_key.course
+ org=self.course_id.org, course=self.course_id.course
)
# Enroll in a verified mode
- self.enrollment = CourseEnrollment.enroll(self.user, self.course_key, mode=CourseMode.VERIFIED)
+ self.enrollment = CourseEnrollment.enroll(self.user, self.course_id, mode=CourseMode.VERIFIED)
@ddt.data('final', 'midterm')
def test_start_verification(self, checkpoint_name):
@@ -50,16 +56,16 @@ class TestReverificationService(ModuleStoreTestCase):
"""
reverification_service = ReverificationService()
checkpoint_location = u'i4x://{org}/{course}/edx-reverification-block/{checkpoint}'.format(
- org=self.course_key.org, course=self.course_key.course, checkpoint=checkpoint_name
+ org=self.course_id.org, course=self.course_id.course, checkpoint=checkpoint_name
)
expected_url = (
'/verify_student/reverify'
'/{course_key}'
'/{checkpoint_location}/'
- ).format(course_key=unicode(self.course_key), checkpoint_location=checkpoint_location)
+ ).format(course_key=unicode(self.course_id), checkpoint_location=checkpoint_location)
self.assertEqual(
- reverification_service.start_verification(unicode(self.course_key), checkpoint_location),
+ reverification_service.start_verification(unicode(self.course_id), checkpoint_location),
expected_url
)
@@ -69,22 +75,22 @@ class TestReverificationService(ModuleStoreTestCase):
"""
reverification_service = ReverificationService()
self.assertIsNone(
- reverification_service.get_status(self.user.id, unicode(self.course_key), self.final_checkpoint_location)
+ reverification_service.get_status(self.user.id, unicode(self.course_id), self.final_checkpoint_location)
)
checkpoint_obj = VerificationCheckpoint.objects.create(
- course_id=unicode(self.course_key),
+ course_id=unicode(self.course_id),
checkpoint_location=self.final_checkpoint_location
)
VerificationStatus.objects.create(checkpoint=checkpoint_obj, user=self.user, status='submitted')
self.assertEqual(
- reverification_service.get_status(self.user.id, unicode(self.course_key), self.final_checkpoint_location),
+ reverification_service.get_status(self.user.id, unicode(self.course_id), self.final_checkpoint_location),
'submitted'
)
VerificationStatus.objects.create(checkpoint=checkpoint_obj, user=self.user, status='approved')
self.assertEqual(
- reverification_service.get_status(self.user.id, unicode(self.course_key), self.final_checkpoint_location),
+ reverification_service.get_status(self.user.id, unicode(self.course_id), self.final_checkpoint_location),
'approved'
)
@@ -94,36 +100,68 @@ class TestReverificationService(ModuleStoreTestCase):
"""
reverification_service = ReverificationService()
VerificationCheckpoint.objects.create(
- course_id=unicode(self.course_key),
+ course_id=unicode(self.course_id),
checkpoint_location=self.final_checkpoint_location
)
- reverification_service.skip_verification(self.user.id, unicode(self.course_key), self.final_checkpoint_location)
+ reverification_service.skip_verification(self.user.id, unicode(self.course_id), self.final_checkpoint_location)
self.assertEqual(
- SkippedReverification.objects.filter(user=self.user, course_id=self.course_key).count(),
+ SkippedReverification.objects.filter(user=self.user, course_id=self.course_id).count(),
1
)
# now test that a user can have only one entry for a skipped
# reverification for a course
- reverification_service.skip_verification(self.user.id, unicode(self.course_key), self.final_checkpoint_location)
+ reverification_service.skip_verification(self.user.id, unicode(self.course_id), self.final_checkpoint_location)
self.assertEqual(
- SkippedReverification.objects.filter(user=self.user, course_id=self.course_key).count(),
+ SkippedReverification.objects.filter(user=self.user, course_id=self.course_id).count(),
1
)
# testing service for skipped attempt.
self.assertEqual(
- reverification_service.get_status(self.user.id, unicode(self.course_key), self.final_checkpoint_location),
+ reverification_service.get_status(self.user.id, unicode(self.course_id), self.final_checkpoint_location),
'skipped'
)
+ 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_get_attempts(self):
"""Check verification attempts count against a user for a given
'checkpoint' and 'course_id'.
"""
reverification_service = ReverificationService()
- course_id = unicode(self.course_key)
+ course_id = unicode(self.course_id)
self.assertEqual(
reverification_service.get_attempts(self.user.id, course_id, self.final_checkpoint_location),
0
@@ -147,5 +185,5 @@ class TestReverificationService(ModuleStoreTestCase):
# Should be marked as "skipped" (opted out)
service = ReverificationService()
- status = service.get_status(self.user.id, unicode(self.course_key), self.final_checkpoint_location)
+ status = service.get_status(self.user.id, unicode(self.course_id), self.final_checkpoint_location)
self.assertEqual(status, service.NON_VERIFIED_TRACK)
diff --git a/lms/templates/courseware/progress.html b/lms/templates/courseware/progress.html
index 9483e8efea..8666bd941f 100644
--- a/lms/templates/courseware/progress.html
+++ b/lms/templates/courseware/progress.html
@@ -132,10 +132,13 @@ from django.utils.http import urlquote_plus
%if requirement['status'] == 'submitted':
${_("Verification Submitted")}
%elif requirement['status'] == 'failed':
-
+
${_("Verification Failed" )}
+ %elif requirement['status'] == 'declined':
+
+ ${_("Verification Declined" )}
%elif requirement['status'] == 'satisfied':
-
+
% if requirement['namespace'] == 'grade' and 'final_grade' in requirement['reason']:
${int(requirement['reason']['final_grade'] * 100)}%
% else:
@@ -149,7 +152,7 @@ from django.utils.http import urlquote_plus
%endfor
-