Merge pull request #19883 from edx/schen/revert_softwaresecure_prod

Revert "LEARNER-6945 Populate software secure photo verification"
This commit is contained in:
Farhanah Sheets
2019-02-26 15:34:18 -05:00
committed by GitHub
5 changed files with 1 additions and 270 deletions

View File

@@ -1,104 +0,0 @@
"""
Django admin command to populate expiry_date for approved verifications in SoftwareSecurePhotoVerification
"""
import logging
import time
from datetime import timedelta
from django.conf import settings
from django.core.management.base import BaseCommand
from django.db.models import F
from util.query import use_read_replica_if_available
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
logger = logging.getLogger(__name__)
class Command(BaseCommand):
"""
This command sets the expiry_date for users for which the verification is approved
The task is performed in batches with maximum number of rows to process given in argument `batch_size`
and a sleep time between each batch given by `sleep_time`
Default values:
`batch_size` = 1000 rows
`sleep_time` = 10 seconds
Example usage:
$ ./manage.py lms populate_expiry_date --batch_size=1000 --sleep_time=5
OR
$ ./manage.py lms populate_expiry_date
"""
help = 'Populate expiry_date for approved verifications'
def add_arguments(self, parser):
parser.add_argument(
'--batch_size',
action='store',
dest='batch_size',
type=int,
default=1000,
help='Maximum number of database rows to process. '
'This helps avoid locking the database while updating large amount of data.')
parser.add_argument(
'--sleep_time',
action='store',
dest='sleep_time',
type=int,
default=10,
help='Sleep time in seconds between update of batches')
def handle(self, *args, **options):
"""
Handler for the command
It filters approved Software Secure Photo Verification and then for each distinct user it finds the most
recent approved verification and set its expiry_date
"""
batch_size = options['batch_size']
sleep_time = options['sleep_time']
query = SoftwareSecurePhotoVerification.objects.filter(status='approved').order_by()
sspv = use_read_replica_if_available(query)
if not sspv.count():
logger.info("No approved entries found in SoftwareSecurePhotoVerification")
return
distinct_user_ids = set()
update_verification_ids = []
update_verification_count = 0
for verification in sspv:
if verification.user_id not in distinct_user_ids:
distinct_user_ids.add(verification.user_id)
recent_verification = self.find_recent_verification(sspv, verification.user_id)
if not recent_verification.expiry_date:
update_verification_ids.append(recent_verification.pk)
update_verification_count += 1
if update_verification_count == batch_size:
self.bulk_update(update_verification_ids)
update_verification_count = 0
update_verification_ids = []
time.sleep(sleep_time)
if update_verification_ids:
self.bulk_update(update_verification_ids)
def bulk_update(self, verification_ids):
"""
It updates the expiry_date for all the verification whose ids lie in verification_ids
"""
recent_verification_qs = SoftwareSecurePhotoVerification.objects.filter(pk__in=verification_ids)
recent_verification_qs.update(expiry_date=F('updated_at') + timedelta(
days=settings.VERIFY_STUDENT["DAYS_GOOD_FOR"]))
def find_recent_verification(self, model, user_id):
"""
Returns the most recent approved verification for a user
"""
return model.filter(user_id=user_id).latest('updated_at')

View File

@@ -1,124 +0,0 @@
"""
Tests for django admin command `populate_expiry_date` in the verify_student module
"""
from datetime import timedelta
import boto
from django.conf import settings
from django.core.management import call_command
from django.test import TestCase
from mock import patch
from student.tests.factories import UserFactory
from testfixtures import LogCapture
from common.test.utils import MockS3Mixin
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
from lms.djangoapps.verify_student.tests.test_models import (
FAKE_SETTINGS,
mock_software_secure_post
)
LOGGER_NAME = 'lms.djangoapps.verify_student.management.commands.populate_expiry_date'
@patch.dict(settings.VERIFY_STUDENT, FAKE_SETTINGS)
@patch('lms.djangoapps.verify_student.models.requests.post', new=mock_software_secure_post)
class TestPopulateExpiryDate(MockS3Mixin, TestCase):
""" Tests for django admin command `populate_expiry_date` in the verify_student module """
def setUp(self):
""" Initial set up for tests """
super(TestPopulateExpiryDate, self).setUp()
connection = boto.connect_s3()
connection.create_bucket(FAKE_SETTINGS['SOFTWARE_SECURE']['S3_BUCKET'])
def create_and_submit(self, user):
""" Helper method that lets us create new SoftwareSecurePhotoVerifications """
attempt = SoftwareSecurePhotoVerification(user=user)
attempt.upload_face_image("Fake Data")
attempt.upload_photo_id_image("More Fake Data")
attempt.mark_ready()
attempt.submit()
return attempt
def test_expiry_date_already_present(self):
"""
Test that the expiry_date for most recent approved verification is updated only when the
expiry_date is not already present
"""
user = UserFactory.create()
verification = self.create_and_submit(user)
verification.status = 'approved'
verification.expiry_date = verification.updated_at + timedelta(days=10)
verification.save()
expiry_date = verification.expiry_date
call_command('populate_expiry_date')
# Check that the expiry_date for approved verification is not changed when it is already present
verification_expiry_date = SoftwareSecurePhotoVerification.objects.get(pk=verification.pk).expiry_date
self.assertEqual(verification_expiry_date, expiry_date)
def test_recent_approved_verification(self):
"""
Test that the expiry_date for most recent approved verification is updated
A user can have multiple approved Software Secure Photo Verification over the year
Only the most recent is considered for course verification
"""
user = UserFactory.create()
outdated_verification = self.create_and_submit(user)
outdated_verification.status = 'approved'
outdated_verification.save()
recent_verification = self.create_and_submit(user)
recent_verification.status = 'approved'
recent_verification.save()
call_command('populate_expiry_date')
# Check that expiry_date for only one verification is set
assert len(SoftwareSecurePhotoVerification.objects.filter(expiry_date__isnull=False)) == 1
# Check that the expiry_date date set for verification is not for the outdated approved verification
expiry_date = SoftwareSecurePhotoVerification.objects.get(pk=outdated_verification.pk).expiry_date
self.assertIsNone(expiry_date)
def test_approved_verification_expiry_date(self):
"""
Tests that the command correctly updates expiry_date
Criteria :
Verification for which status is approved and expiry_date is null
"""
# Create verification with status : submitted
user = UserFactory.create()
self.create_and_submit(user)
# Create verification with status : approved
approved_verification = self.create_and_submit(user)
approved_verification.status = 'approved'
approved_verification.save()
expected_date = approved_verification.updated_at + timedelta(
days=settings.VERIFY_STUDENT["DAYS_GOOD_FOR"])
call_command('populate_expiry_date')
# Check to make sure we have one verification with expiry_date set and one with null
assert len(SoftwareSecurePhotoVerification.objects.filter(expiry_date__isnull=True)) == 1
assert len(SoftwareSecurePhotoVerification.objects.filter(expiry_date__isnull=False)) == 1
# Confirm that expiry_date set for approved verification is correct
approved_verification = SoftwareSecurePhotoVerification.objects.get(pk=approved_verification.pk)
self.assertEqual(approved_verification.expiry_date, expected_date)
def test_no_approved_verification_found(self):
"""
Test that if no approved verifications are found the management command terminates gracefully
"""
with LogCapture(LOGGER_NAME) as logger:
call_command('populate_expiry_date')
logger.check(
(LOGGER_NAME, 'INFO', "No approved entries found in SoftwareSecurePhotoVerification")
)

View File

@@ -561,23 +561,6 @@ class SoftwareSecurePhotoVerification(PhotoVerification):
expiry_date = models.DateTimeField(null=True, blank=True, db_index=True)
expiry_email_date = models.DateTimeField(null=True, blank=True, db_index=True)
@status_before_must_be("must_retry", "submitted", "approved", "denied")
def approve(self, user_id=None, service=""):
"""
Approve the verification attempt for user
Valid attempt statuses when calling this method:
`submitted`, `approved`, `denied`
After method completes:
status is set to `approved`
expiry_date is set to one year from now
"""
self.expiry_date = datetime.now(pytz.UTC) + timedelta(
days=settings.VERIFY_STUDENT["DAYS_GOOD_FOR"]
)
super(SoftwareSecurePhotoVerification, self).approve(user_id, service)
@classmethod
def get_initial_verification(cls, user, earliest_allowed_date=None):
"""Get initial verification for a user with the 'photo_id_key'.

View File

@@ -1764,17 +1764,6 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase):
"""
Test for verification passed.
"""
expiry_date = datetime.now(pytz.UTC) + timedelta(
days=settings.VERIFY_STUDENT["DAYS_GOOD_FOR"]
)
verification = SoftwareSecurePhotoVerification.objects.create(user=self.user)
verification.mark_ready()
verification.submit()
verification.approve()
verification.expiry_date = datetime.now(pytz.UTC)
verification.expiry_email_date = datetime.now(pytz.UTC)
verification.save()
data = {
"EdX-ID": self.receipt_id,
"Result": "PASS",
@@ -1789,11 +1778,7 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase):
HTTP_DATE='testdate'
)
attempt = SoftwareSecurePhotoVerification.objects.get(receipt_id=self.receipt_id)
old_verification = SoftwareSecurePhotoVerification.objects.get(pk=verification.pk)
self.assertEqual(attempt.status, u'approved')
self.assertEqual(attempt.expiry_date.date(), expiry_date.date())
self.assertIsNone(old_verification.expiry_date)
self.assertIsNone(old_verification.expiry_email_date)
self.assertEquals(response.content, 'OK!')
self.assertEqual(len(mail.outbox), 1)

View File

@@ -1158,16 +1158,7 @@ def results_callback(request):
'platform_name': settings.PLATFORM_NAME,
}
if result == "PASS":
# If this verification is not an outdated version then make expiry date of previous approved verification NULL
# Setting expiry date to NULL is important so that it does not get filtered in the management command
# that sends email when verification expires : verify_student/send_verification_expiry_email
if attempt.status != 'approved':
log.info(u'Making expiry date of previous approved verification NULL for {}'.format(attempt.user_id))
verification = SoftwareSecurePhotoVerification.objects.filter(status='approved',
user_id=attempt.user_id).latest('updated_at')
SoftwareSecurePhotoVerification.objects.filter(pk=verification.pk).update(expiry_date=None,
expiry_email_date=None)
log.debug(u'Approving verification for {}'.format(receipt_id))
log.debug(u"Approving verification for %s", receipt_id)
attempt.approve()
status = u"approved"
expiry_date = datetime.date.today() + datetime.timedelta(