Merge pull request #29155 from edx/mroytman/MST-1130-management-command-cleanup
feat: Add Management Command to Re-Emit SoftwareSecurePhotoVerification post_save Signal
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
Tests for django admin command `trigger_softwaresecurephotoverifications_post_save_signal`
|
||||
in the verify_student module
|
||||
"""
|
||||
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management import call_command
|
||||
|
||||
from freezegun import freeze_time
|
||||
from unittest.mock import call, patch, ANY
|
||||
|
||||
from common.test.utils import MockS3BotoMixin
|
||||
from lms.djangoapps.verify_student.tests import TestVerificationBase
|
||||
from lms.djangoapps.verify_student.tests.test_models import (
|
||||
FAKE_SETTINGS,
|
||||
mock_software_secure_post,
|
||||
)
|
||||
|
||||
|
||||
# Lots of patching to stub in our own settings, and HTTP posting
|
||||
@patch.dict(settings.VERIFY_STUDENT, FAKE_SETTINGS)
|
||||
@patch.dict(settings.FEATURES, {'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING': True})
|
||||
@patch('lms.djangoapps.verify_student.models.requests.post', new=mock_software_secure_post)
|
||||
class TestTriggerSoftwareSecurePhotoVerificationsPostSaveSignal(MockS3BotoMixin, TestVerificationBase):
|
||||
"""
|
||||
Tests for django admin command `trigger_softwaresecurephotoverifications_post_save_signal`
|
||||
in the verify_student module
|
||||
"""
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
with freeze_time("2021-10-30 12:00:00"):
|
||||
self._create_attempts(4)
|
||||
with freeze_time("2021-11-01 03:00:00"):
|
||||
self._create_attempts(4)
|
||||
|
||||
def _create_attempts(self, num_attempts):
|
||||
for _ in range(num_attempts):
|
||||
self.create_and_submit_attempt_for_user()
|
||||
|
||||
@patch('lms.djangoapps.verify_student.signals.idv_update_signal.send')
|
||||
def test_command(self, send_idv_update_mock):
|
||||
call_command('trigger_softwaresecurephotoverifications_post_save_signal', start_date_time='2021-10-31 06:00:00')
|
||||
|
||||
# The UserFactory instantiates first_name and last_name using a Sequence, which provide integers to
|
||||
# generate unique values. A Sequence maintains its state across test runs, so the value of a given Sequence,
|
||||
# and therefore, the value of photo_id_name and full_name, depends on the order that tests using the UserFactory
|
||||
# run, as the Sequence is not reset after each test suite. This makes asserting on the exact value
|
||||
# of photo_id_name and full_name, derived from first_name and last_name, difficult.
|
||||
# Resetting the Sequence with UserFactory.reset_sequence causes failures in other tests that rely on
|
||||
# these values. In any case, we primarily care about the signal being called with the correct sender,
|
||||
# attempt_id, and user_id, as the signal is tested elsewhere.
|
||||
expected_calls = [
|
||||
call(
|
||||
sender='idv_update', attempt_id=5, user_id=5, status='must_retry',
|
||||
photo_id_name=ANY, full_name=ANY
|
||||
),
|
||||
call(
|
||||
sender='idv_update', attempt_id=6, user_id=6, status='must_retry',
|
||||
photo_id_name=ANY, full_name=ANY
|
||||
),
|
||||
call(
|
||||
sender='idv_update', attempt_id=7, user_id=7, status='must_retry',
|
||||
photo_id_name=ANY, full_name=ANY
|
||||
),
|
||||
call(
|
||||
sender='idv_update', attempt_id=8, user_id=8, status='must_retry',
|
||||
photo_id_name=ANY, full_name=ANY
|
||||
),
|
||||
]
|
||||
print(send_idv_update_mock.mock_calls)
|
||||
self.assertEqual(send_idv_update_mock.call_count, 4)
|
||||
send_idv_update_mock.assert_has_calls(expected_calls, any_order=True)
|
||||
@@ -0,0 +1,68 @@
|
||||
"""
|
||||
Django admin command to save SoftwareSecurePhotoVerifications given an iterable of
|
||||
verification_ids, thereby re-emitting the post_save signal.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import time
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""
|
||||
Command to save SoftwareSecurePhotoVerification model instances based on a provided interable of verifiction_ids.
|
||||
This re-emits the post_save signal.
|
||||
"""
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--start_date_time',
|
||||
action='store',
|
||||
dest='start_date_time',
|
||||
type=str,
|
||||
help='First date time for SoftwareSecurePhotoVerifications to resave; '
|
||||
'should be formatted as 2020-12-02 00:00:00.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--batch_size',
|
||||
action='store',
|
||||
dest='batch_size',
|
||||
type=int,
|
||||
default=300,
|
||||
help='Maximum number of SoftwareSecurePhotoVerifications to process. '
|
||||
'This helps avoid overloading 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):
|
||||
start_date = datetime.datetime.strptime(options['start_date_time'], '%Y-%m-%d %H:%M:%S')
|
||||
batch_size = options['batch_size']
|
||||
sleep_time = options['sleep_time']
|
||||
|
||||
count = 0
|
||||
verifications_after_start = SoftwareSecurePhotoVerification.objects.filter(created_at__gte=start_date)
|
||||
|
||||
for verification in verifications_after_start:
|
||||
logger.info(
|
||||
'Saving SoftwareSecurePhotoVerification with id=%(id)s',
|
||||
{'id': verification.id}
|
||||
)
|
||||
verification.save()
|
||||
count += 1
|
||||
|
||||
if count == batch_size:
|
||||
time.sleep(sleep_time)
|
||||
count = 0
|
||||
Reference in New Issue
Block a user