Skip to content

Commit 6c9064b

Browse files
committed
fix check manual restart approval
1 parent 3f36856 commit 6c9064b

7 files changed

Lines changed: 117 additions & 24 deletions

File tree

osf/management/commands/process_manual_restart_approvals.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from django.utils import timezone
55
from osf.models import Registration
66
from osf.models.admin_log_entry import AdminLogEntry, MANUAL_ARCHIVE_RESTART
7-
from website import settings
87
from scripts.approve_registrations import approve_past_pendings
98

109
logger = logging.getLogger(__name__)
@@ -134,9 +133,9 @@ def should_auto_approve(self, registration):
134133
if approval.is_rejected:
135134
return 'approval was rejected'
136135

137-
time_since_initiation = timezone.now() - approval.initiation_date
138-
if time_since_initiation < settings.REGISTRATION_APPROVAL_TIME:
139-
remaining = settings.REGISTRATION_APPROVAL_TIME - time_since_initiation
136+
auto_approve_at = approval.auto_approve_at
137+
if timezone.now() < auto_approve_at:
138+
remaining = auto_approve_at - timezone.now()
140139
return f'not ready yet ({remaining} remaining)'
141140

142141
if registration.is_stuck_registration:

osf/models/registrations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ def is_collection(self):
235235

236236
@property
237237
def archive_job(self):
238-
return self.archive_jobs.first() if self.archive_jobs.count() else None
238+
return self.archive_jobs.first()
239239

240240
@property
241241
def sanction(self):

osf/models/sanctions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,10 @@ class RegistrationApproval(SanctionCallbackMixin, EmailApprovableSanction):
833833

834834
initiated_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.CASCADE)
835835

836+
@property
837+
def auto_approve_at(self):
838+
return self.initiation_date + osf_settings.REGISTRATION_APPROVAL_TIME
839+
836840
@staticmethod
837841
def find_approval_backlog():
838842
"""

osf_tests/test_archiver.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,41 @@ def test_archive(self, mock_chain, mock_enqueue):
451451
]
452452
)
453453

454+
@mock.patch('website.archiver.tasks.delayed_manual_restart_approval.delay')
455+
@mock.patch('website.archiver.tasks.was_manually_restarted', return_value=True)
456+
@mock.patch('osf.management.commands.force_archive.archive')
457+
@mock.patch('osf.management.commands.force_archive.verify')
458+
def test_force_archive_schedules_manual_restart_approval_check(
459+
self, mock_verify, mock_archive, mock_was_manually_restarted, mock_delayed_check
460+
):
461+
result = force_archive(
462+
registration_id=self.dst._id,
463+
permissible_addons=['osfstorage'],
464+
)
465+
466+
assert result == f'Registration {self.dst._id} archive completed'
467+
mock_verify.assert_called_once()
468+
mock_archive.assert_called_once()
469+
mock_was_manually_restarted.assert_called_once()
470+
mock_delayed_check.assert_called_once_with(self.dst._id, delay_minutes=5)
471+
472+
@mock.patch('website.archiver.tasks.delayed_manual_restart_approval.delay')
473+
@mock.patch('website.archiver.tasks.was_manually_restarted', return_value=False)
474+
@mock.patch('osf.management.commands.force_archive.archive')
475+
@mock.patch('osf.management.commands.force_archive.verify')
476+
def test_force_archive_does_not_schedule_when_not_manually_restarted(
477+
self, mock_verify, mock_archive, mock_was_manually_restarted, mock_delayed_check
478+
):
479+
force_archive(
480+
registration_id=self.dst._id,
481+
permissible_addons=['osfstorage'],
482+
)
483+
484+
mock_verify.assert_called_once()
485+
mock_archive.assert_called_once()
486+
mock_was_manually_restarted.assert_called_once()
487+
mock_delayed_check.assert_not_called()
488+
454489
def test_stat_addon(self):
455490
with mock.patch.object(BaseStorageAddon, '_get_file_tree') as mock_file_tree:
456491
mock_file_tree.return_value = FILE_TREE

scripts/check_manual_restart_approval.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,34 @@
11
import logging
2+
from framework import sentry
23
from framework.celery_tasks import app as celery_app
34
from django.core.management import call_command
5+
from django.utils import timezone
46
from osf.models import Registration
7+
from scripts.approve_registrations import approve_past_pendings
58

69
logger = logging.getLogger(__name__)
710

811

912
@celery_app.task(name='scripts.check_manual_restart_approval')
1013
def check_manual_restart_approval(registration_id):
1114
try:
12-
try:
13-
registration = Registration.objects.get(_id=registration_id)
14-
except Registration.DoesNotExist:
15+
registration = Registration.load(registration_id)
16+
if not registration:
1517
logger.error(f"Registration {registration_id} not found")
1618
return f"Registration {registration_id} not found"
1719

1820
if registration.is_public or registration.is_registration_approved:
1921
return f"Registration {registration_id} already approved/public"
2022

23+
approval = registration.registration_approval
24+
if not approval:
25+
logger.info(f"Registration {registration_id} has no registration approval object")
26+
return f"Registration {registration_id} has no registration approval object"
27+
28+
if approval.is_rejected:
29+
logger.info(f"Registration {registration_id} approval was rejected")
30+
return f"Registration {registration_id} approval was rejected"
31+
2132
if registration.archiving:
2233
logger.info(f"Registration {registration_id} still archiving, retrying in 10 minutes")
2334
check_manual_restart_approval.apply_async(
@@ -26,20 +37,18 @@ def check_manual_restart_approval(registration_id):
2637
)
2738
return f"Registration {registration_id} still archiving, scheduled retry"
2839

29-
logger.info(f"Processing manual restart approval for registration {registration_id}")
40+
if timezone.now() < approval.auto_approve_at:
41+
logger.info(f"Registration {registration_id} not ready for auto-approval yet")
42+
return f"Registration {registration_id} not ready for auto-approval yet"
3043

31-
call_command(
32-
'process_manual_restart_approvals',
33-
registration_id=registration_id,
34-
dry_run=False,
35-
hours_back=24,
36-
verbosity=1
37-
)
44+
logger.info(f"Processing manual restart approval for registration {registration_id}")
45+
approve_past_pendings([approval], dry_run=False)
3846

3947
return f"Processed manual restart approval check for registration {registration_id}"
4048

4149
except Exception as e:
4250
logger.error(f"Error processing manual restart approval for {registration_id}: {e}")
51+
sentry.log_exception(e)
4352
raise
4453

4554

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from datetime import timedelta
2+
from unittest import mock
3+
4+
from django.utils import timezone
5+
6+
from osf_tests.factories import RegistrationFactory, UserFactory
7+
from scripts.check_manual_restart_approval import check_manual_restart_approval
8+
from tests.base import OsfTestCase
9+
from website import settings
10+
11+
12+
class TestCheckManualRestartApproval(OsfTestCase):
13+
14+
def setUp(self):
15+
super().setUp()
16+
self.user = UserFactory()
17+
self.registration = RegistrationFactory(creator=self.user, archive=False)
18+
self.registration.require_approval(self.user)
19+
20+
@mock.patch('scripts.check_manual_restart_approval.approve_past_pendings')
21+
def test_skips_if_approval_window_not_elapsed(self, mock_approve):
22+
self.registration.registration_approval.initiation_date = timezone.now() - timedelta(hours=47)
23+
self.registration.registration_approval.save()
24+
25+
result = check_manual_restart_approval(self.registration._id)
26+
27+
assert 'not ready for auto-approval' in result
28+
mock_approve.assert_not_called()
29+
30+
@mock.patch('scripts.check_manual_restart_approval.approve_past_pendings')
31+
def test_approves_when_approval_window_elapsed(self, mock_approve):
32+
self.registration.registration_approval.initiation_date = (
33+
timezone.now() - settings.REGISTRATION_APPROVAL_TIME - timedelta(minutes=1)
34+
)
35+
self.registration.registration_approval.save()
36+
37+
result = check_manual_restart_approval(self.registration._id)
38+
39+
assert 'Processed manual restart approval check' in result
40+
mock_approve.assert_called_once_with([self.registration.registration_approval], dry_run=False)
41+
42+
@mock.patch('scripts.check_manual_restart_approval.Registration.load', return_value=None)
43+
def test_returns_not_found_when_registration_missing(self, mock_load):
44+
result = check_manual_restart_approval('abc12')
45+
46+
assert result == 'Registration abc12 not found'
47+
mock_load.assert_called_once_with('abc12')

website/archiver/tasks.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -387,21 +387,15 @@ def archive_success(self, dst_pk, job_pk):
387387
job.save()
388388
dst.sanction.ask(dst.get_active_contributors_recursive(unique_users=True))
389389

390-
if was_manually_restarted(dst):
391-
logger.info(f'Registration {dst._id} was manually restarted, scheduling approval check')
392-
delayed_manual_restart_approval.delay(dst._id, delay_minutes=5)
393-
394390
dst.update_search()
395391

396392

397393
def was_manually_restarted(registration):
398-
recent_logs = AdminLogEntry.objects.filter(
394+
return AdminLogEntry.objects.filter(
399395
object_id=registration.pk,
400396
action_flag=MANUAL_ARCHIVE_RESTART,
401397
action_time__gte=timezone.now() - timedelta(hours=48)
402-
)
403-
404-
return recent_logs.exists()
398+
).exists()
405399

406400

407401
@celery_app.task(bind=True)
@@ -424,6 +418,11 @@ def force_archive(self, registration_id, permissible_addons, allow_unconfigured=
424418
skip_collisions=skip_collisions,
425419
delete_collisions=delete_collisions,
426420
)
421+
# The force-archive path bypasses `archive_success`; schedule manual restart follow-up
422+
# in the force_archive so restarted registrations still get auto-approval checks
423+
if was_manually_restarted(registration):
424+
logger.info(f'Registration {registration._id} was manually restarted, scheduling approval check')
425+
delayed_manual_restart_approval.delay(registration._id, delay_minutes=5)
427426
return f'Registration {registration_id} archive completed'
428427

429428
except Exception as exc:

0 commit comments

Comments
 (0)