diff --git a/lms/djangoapps/verify_student/admin.py b/lms/djangoapps/verify_student/admin.py index b13b775ad4..56e5bd3a90 100644 --- a/lms/djangoapps/verify_student/admin.py +++ b/lms/djangoapps/verify_student/admin.py @@ -1,5 +1,31 @@ from ratelimitbackend import admin -from verify_student.models import SoftwareSecurePhotoVerification, InCourseReverificationConfiguration +from verify_student.models import ( + SoftwareSecurePhotoVerification, + InCourseReverificationConfiguration, + VerificationStatus +) -admin.site.register(SoftwareSecurePhotoVerification) + +class SoftwareSecurePhotoVerificationAdmin(admin.ModelAdmin): + """ + Admin for the SoftwareSecurePhotoVerification table. + """ + list_display = ('id', 'user', 'status', 'receipt_id', 'submitted_at', 'updated_at') + search_fields = ( + 'receipt_id', + ) + + +class VerificationStatusAdmin(admin.ModelAdmin): + """ + Admin for the VerificationStatus table. + """ + list_display = ('id', 'user', 'status', 'checkpoint', 'location_id') + search_fields = ( + 'checkpoint', + ) + + +admin.site.register(SoftwareSecurePhotoVerification, SoftwareSecurePhotoVerificationAdmin) admin.site.register(InCourseReverificationConfiguration) +admin.site.register(VerificationStatus, VerificationStatusAdmin) diff --git a/lms/djangoapps/verify_student/models.py b/lms/djangoapps/verify_student/models.py index 66ea5d9e8a..b1353a097e 100644 --- a/lms/djangoapps/verify_student/models.py +++ b/lms/djangoapps/verify_student/models.py @@ -1028,7 +1028,7 @@ class VerificationStatus(models.Model): get_latest_by = "timestamp" @classmethod - def add_verification_status(cls, checkpoint, user, status, location_id=''): + def add_verification_status(cls, checkpoint, user, status, location_id=None): """ Create new verification status object Arguments: @@ -1058,10 +1058,34 @@ class VerificationStatus(models.Model): try: location_id = cls.objects.filter(checkpoint=checkpoint).latest().location_id except cls.DoesNotExist: - location_id = '' + location_id = None cls.objects.create(checkpoint=checkpoint, user=user, status=status, location_id=location_id) + @classmethod + def get_user_attempts(cls, user_id, course_key, related_assessment, location_id): + """ + Get re-verification attempts against a user for a given 'checkpoint' + and 'course_id'. + + Arguments: + user_id(str): User Id string + course_key(str): A CourseKey of a course + related_assessment(str): Verification checkpoint name + location_id(str): Location of Reverification XBlock in courseware + + Returns: + count of re-verification attempts + """ + + return cls.objects.filter( + user_id=user_id, + checkpoint__course_id=course_key, + checkpoint__checkpoint_name=related_assessment, + location_id=location_id, + status="submitted" + ).count() + class InCourseReverificationConfiguration(ConfigurationModel): """Configure in-course re-verification. diff --git a/lms/djangoapps/verify_student/services.py b/lms/djangoapps/verify_student/services.py index 8d3c1d73fc..5e8e989a7f 100644 --- a/lms/djangoapps/verify_student/services.py +++ b/lms/djangoapps/verify_student/services.py @@ -1,28 +1,33 @@ """ -Implement the Reverification XBlock "reverification" server +Implementation of "reverification" service to communicate with Reverification XBlock """ import logging -from opaque_keys.edx.keys import CourseKey + from django.core.exceptions import ObjectDoesNotExist from django.core.urlresolvers import reverse -from verify_student.models import VerificationCheckpoint, VerificationStatus, SkippedReverification from django.db import IntegrityError +from opaque_keys.edx.keys import CourseKey +from verify_student.models import VerificationCheckpoint, VerificationStatus, SkippedReverification + + log = logging.getLogger(__name__) class ReverificationService(object): - """ Service to implement the Reverification XBlock "reverification" service - + """ + Reverification XBlock service """ def get_status(self, user_id, course_id, related_assessment): - """ Check if the user has any verification attempt or has skipped the verification + """ + Get verification attempt status against a user for a given 'checkpoint' + and 'course_id'. Args: user_id(str): User Id string - course_id(str): A string of course_id + course_id(str): A string of course id related_assessment(str): Verification checkpoint name Returns: @@ -44,10 +49,11 @@ class ReverificationService(object): return None def start_verification(self, course_id, related_assessment, item_id): - """ Get or create the verification checkpoint and return the re-verification link + """ + Create re-verification link against a verification checkpoint. Args: - course_id(str): A string of course_id + course_id(str): A string of course id related_assessment(str): Verification checkpoint name Returns: @@ -66,12 +72,13 @@ class ReverificationService(object): return re_verification_link def skip_verification(self, checkpoint_name, user_id, course_id): - """Create the add verification attempt + """ + Add skipped verification attempt entry against a given 'checkpoint' Args: - course_id(str): A string of course_id - user_id(str): User Id string checkpoint_name(str): Verification checkpoint name + user_id(str): User Id string + course_id(str): A string of course_id Returns: None @@ -84,3 +91,20 @@ 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)) + + def get_attempts(self, user_id, course_id, related_assessment, location_id): + """ + Get re-verification attempts against a user for a given 'checkpoint' + and 'course_id'. + + Args: + user_id(str): User Id string + course_id(str): A string of course id + related_assessment(str): Verification checkpoint name + location_id(str): Location of Reverification XBlock in courseware + + Returns: + Number of re-verification attempts of a user + """ + course_key = CourseKey.from_string(course_id) + return VerificationStatus.get_user_attempts(user_id, course_key, related_assessment, location_id) diff --git a/lms/djangoapps/verify_student/tests/test_services.py b/lms/djangoapps/verify_student/tests/test_services.py index 29dd4f069f..6a89a3efbd 100644 --- a/lms/djangoapps/verify_student/tests/test_services.py +++ b/lms/djangoapps/verify_student/tests/test_services.py @@ -1,24 +1,26 @@ -# encoding: utf-8 """ -Tests of reverify service. +Tests of re-verification service. """ + import ddt + +from course_modes.tests.factories import CourseModeFactory +from student.tests.factories import UserFactory +from verify_student.models import VerificationCheckpoint, VerificationStatus, SkippedReverification +from verify_student.services import ReverificationService + from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory -from student.tests.factories import UserFactory -from course_modes.tests.factories import CourseModeFactory -from verify_student.services import ReverificationService -from verify_student.models import VerificationCheckpoint, VerificationStatus, SkippedReverification @ddt.ddt -class TestReverifyService(ModuleStoreTestCase): +class TestReverificationService(ModuleStoreTestCase): """ - Tests for the re-verification service + Tests for the re-verification service. """ def setUp(self): - super(TestReverifyService, self).setUp() + super(TestReverificationService, self).setUp() self.user = UserFactory.create(username="rusty", password="test") course = CourseFactory.create(org='Robot', number='999', display_name='Test Course') @@ -27,18 +29,18 @@ class TestReverifyService(ModuleStoreTestCase): mode_slug="verified", course_id=self.course_key, min_price=100, - suggested_prices='' ) self.item = ItemFactory.create(parent=course, category='chapter', display_name='Test Section') @ddt.data("final_term", "mid_term") def test_start_verification(self, checkpoint_name): - """Testing start verification service. If checkpoint exists for specific course then returns the checkpoint - otherwise created that checkpoint. """ - - rev = ReverificationService() - rev.start_verification(unicode(self.course_key), checkpoint_name, self.item.location) + Test the 'start_verification' service method. If checkpoint exists for + a specific course then return the checkpoint otherwise create that + checkpoint. + """ + reverification_service = ReverificationService() + reverification_service.start_verification(unicode(self.course_key), checkpoint_name, self.item.location) expected_url = ( '/verify_student/reverify' '/{course_key}' @@ -47,36 +49,71 @@ class TestReverifyService(ModuleStoreTestCase): ).format(course_key=unicode(self.course_key), checkpoint_name=checkpoint_name, usage_id=self.item.location) self.assertEqual( - expected_url, rev.start_verification(unicode(self.course_key), checkpoint_name, self.item.location) + expected_url, + reverification_service.start_verification(unicode(self.course_key), checkpoint_name, self.item.location) ) def test_get_status(self): - """ Check if the user has any verification attempt for the checkpoint and course_id """ - + """ + Test the verification statuses of a user for a given 'checkpoint' and + 'course_id'. + """ checkpoint_name = 'final_term' - rev = ReverificationService() - self.assertIsNone(rev.get_status(self.user.id, unicode(self.course_key), checkpoint_name)) + reverification_service = ReverificationService() + self.assertIsNone(reverification_service.get_status(self.user.id, unicode(self.course_key), checkpoint_name)) + checkpoint_obj = VerificationCheckpoint.objects.create( - course_id=unicode(self.course_key), checkpoint_name=checkpoint_name + course_id=unicode(self.course_key), + checkpoint_name=checkpoint_name ) VerificationStatus.objects.create(checkpoint=checkpoint_obj, user=self.user, status='submitted') - self.assertEqual(rev.get_status(self.user.id, unicode(self.course_key), checkpoint_name), 'submitted') - - def test_skip_verification(self): - """ Adding the test skip verification attempt for the user """ - - checkpoint_name = 'final_term' - rev = ReverificationService() - - VerificationCheckpoint.objects.create( - course_id=unicode(self.course_key), checkpoint_name=checkpoint_name + self.assertEqual( + reverification_service.get_status(self.user.id, unicode(self.course_key), checkpoint_name), + 'submitted' ) - rev.skip_verification(checkpoint_name, self.user.id, unicode(self.course_key)) + VerificationStatus.objects.create(checkpoint=checkpoint_obj, user=self.user, status='submitted') + self.assertEqual( + reverification_service.get_status(self.user.id, unicode(self.course_key), checkpoint_name), + 'submitted' + ) + def test_skip_verification(self): + """ + Adding the test skip verification attempt for the user + """ + checkpoint_name = 'final_term' + reverification_service = ReverificationService() + VerificationCheckpoint.objects.create( + course_id=unicode(self.course_key), + checkpoint_name=checkpoint_name + ) + + reverification_service.skip_verification(checkpoint_name, self.user.id, unicode(self.course_key)) self.assertEqual(1, SkippedReverification.objects.filter(user=self.user, course_id=self.course_key).count()) - rev.skip_verification(checkpoint_name, self.user.id, unicode(self.course_key)) - + reverification_service.skip_verification(checkpoint_name, self.user.id, unicode(self.course_key)) self.assertEqual(1, SkippedReverification.objects.filter(user=self.user, course_id=self.course_key).count()) + + def test_get_attempts(self): + """ + Check verification attempts count against a user for a given + 'checkpoint' and 'course_id'. + """ + checkpoint_name = 'final_term' + reverification_service = ReverificationService() + course_id = unicode(self.course_key) + self.assertEqual( + reverification_service.get_attempts(self.user.id, course_id, checkpoint_name, location_id=None), + 0 + ) + + # now create a checkpoint and add user's entry against it then test + # that the 'get_attempts' service method returns count accordingly + checkpoint_obj = VerificationCheckpoint.objects.create(course_id=course_id, checkpoint_name=checkpoint_name) + VerificationStatus.objects.create(checkpoint=checkpoint_obj, user=self.user, status='submitted') + self.assertEqual( + reverification_service.get_attempts(self.user.id, course_id, checkpoint_name, location_id=None), + 1 + )