-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add management command to delete exam attempts (#1243)
* feat: add management command to delete exam attempts * fix: quality
- Loading branch information
Showing
5 changed files
with
152 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
""" | ||
Django management command to delete attempts. This command should only be used | ||
to remove attempts that have not been started or completed, as it will not | ||
reset problem state or grade overrides. | ||
""" | ||
import logging | ||
import time | ||
|
||
from django.core.management.base import BaseCommand | ||
|
||
from edx_proctoring.models import ProctoredExamStudentAttempt | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
class Command(BaseCommand): | ||
""" | ||
Django Management command to delete attempts. | ||
""" | ||
|
||
def add_arguments(self, parser): | ||
parser.add_argument( | ||
'-p', | ||
'--file_path', | ||
metavar='file_path', | ||
dest='file_path', | ||
required=True, | ||
help='Path to file.' | ||
) | ||
parser.add_argument( | ||
'--batch_size', | ||
action='store', | ||
dest='batch_size', | ||
type=int, | ||
default=300, | ||
help='Maximum number of attempt_ids 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): | ||
""" | ||
Management command entry point, simply call into the signal firing | ||
""" | ||
batch_size = options['batch_size'] | ||
sleep_time = options['sleep_time'] | ||
file_path = options['file_path'] | ||
|
||
with open(file_path, 'r') as file: | ||
ids_to_delete = file.readlines() | ||
|
||
total_deleted = 0 | ||
|
||
for i in range(0, len(ids_to_delete), batch_size): | ||
batch_to_delete = ids_to_delete[i:i + batch_size] | ||
|
||
delete_queryset = ProctoredExamStudentAttempt.objects.filter( | ||
id__in=batch_to_delete | ||
) | ||
deleted_count, _ = delete_queryset.delete() | ||
|
||
total_deleted += deleted_count | ||
|
||
log.info(f'{deleted_count} attempts deleted.') | ||
time.sleep(sleep_time) | ||
|
||
log.info(f'Job completed. {total_deleted} attempts deleted.') |
72 changes: 72 additions & 0 deletions
72
edx_proctoring/management/commands/tests/test_reset_attempts.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
""" | ||
Tests for the reset_attempts management command | ||
""" | ||
from tempfile import NamedTemporaryFile | ||
|
||
import ddt | ||
|
||
from django.core.management import call_command | ||
|
||
from edx_proctoring.api import create_exam | ||
from edx_proctoring.models import ProctoredExamStudentAttempt | ||
from edx_proctoring.statuses import ProctoredExamStudentAttemptStatus | ||
from edx_proctoring.tests.utils import LoggedInTestCase | ||
|
||
|
||
@ddt.ddt | ||
class ResetAttemptsTests(LoggedInTestCase): | ||
""" | ||
Coverage of the reset_attempts.py file | ||
""" | ||
|
||
def setUp(self): | ||
""" | ||
Build up test data | ||
""" | ||
super().setUp() | ||
self.exam_id = create_exam( | ||
course_id='a/b/c', | ||
content_id='bar', | ||
exam_name='Test Exam', | ||
time_limit_mins=90 | ||
) | ||
|
||
self.num_attempts = 10 | ||
|
||
user_list = self.create_batch_users(self.num_attempts) | ||
for user in user_list: | ||
ProctoredExamStudentAttempt.objects.create( | ||
proctored_exam_id=self.exam_id, | ||
user_id=user.id, | ||
external_id='foo', | ||
status=ProctoredExamStudentAttemptStatus.created, | ||
allowed_time_limit_mins=10, | ||
taking_as_proctored=True, | ||
is_sample_attempt=False | ||
) | ||
|
||
@ddt.data( | ||
5, | ||
7, | ||
10, | ||
) | ||
def test_run_command(self, num_to_delete): | ||
""" | ||
Run the management command | ||
""" | ||
ids = list(ProctoredExamStudentAttempt.objects.all().values_list('id', flat=True))[:num_to_delete] | ||
|
||
with NamedTemporaryFile() as file: | ||
with open(file.name, 'w') as writing_file: | ||
for num in ids: | ||
writing_file.write(str(num) + '\n') | ||
|
||
call_command( | ||
'reset_attempts', | ||
batch_size=2, | ||
sleep_time=0, | ||
file_path=file.name, | ||
) | ||
|
||
attempts = ProctoredExamStudentAttempt.objects.all() | ||
self.assertEqual(len(attempts), self.num_attempts - num_to_delete) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters