Skip to content

Commit f67d8e6

Browse files
authored
make confirm ham a celery task (#11666)
1 parent 2e0046d commit f67d8e6

7 files changed

Lines changed: 107 additions & 10 deletions

File tree

admin/users/views.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
from admin.nodes.views import NodeAddSystemTag, NodeRemoveSystemTag
5151
from admin.base.views import GuidView
5252
from api.users.services import send_password_reset_email
53-
from api.users.tasks import merge_users
53+
from api.users.tasks import merge_users, confirm_user_ham
5454
from website.settings import DOMAIN, OSF_SUPPORT_EMAIL
5555
from django.urls import reverse_lazy
5656

@@ -264,14 +264,19 @@ class UserConfirmHamView(UserMixin, View):
264264

265265
def post(self, request, *args, **kwargs):
266266
user = self.get_object()
267-
user.is_registered = True # back in the day spam users were de-registered
268-
user.confirm_ham(save=True)
267+
268+
ham_task = confirm_user_ham.delay(user._id, initiator_guid=request.user._id)
269269
update_admin_log(
270270
user_id=request.user.id,
271271
object_id=user._id,
272272
object_repr='User',
273-
message=f'Confirmed Ham: {user._id} when user {user._id} marked as spam',
274-
action_flag=CONFIRM_SPAM
273+
message=f'Queued Confirm HAM task for user {user._id} (task id: {ham_task.id}).',
274+
action_flag=CONFIRM_HAM
275+
)
276+
277+
messages.success(
278+
request,
279+
f'Confirm HAM started in the background for user {user._id}. Task id: {ham_task.id}.'
275280
)
276281
return redirect(self.get_success_url())
277282

admin_tests/users/test_views.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,18 +218,22 @@ class TestHamUserRestore(AdminTestCase):
218218
def setUp(self):
219219
self.user = UserFactory()
220220
self.request = RequestFactory().post('/fake_path')
221+
patch_messages(self.request)
221222
self.view = views.UserConfirmHamView
222223
self.view = setup_log_view(self.view, self.request, guid=self.user._id)
223224

224-
def test_enable_user(self):
225+
@mock.patch('api.users.tasks.confirm_user_ham.delay')
226+
def test_enable_user(self, mock_confirm_user_ham_delay):
225227
self.user.is_disabled = True
226228
self.user.save()
227229
assert self.user.is_disabled
228230
self.view().post(self.request)
229231
self.user.reload()
230232

231-
assert not self.user.is_disabled
232-
assert self.user.spam_status == SpamStatus.HAM
233+
mock_confirm_user_ham_delay.assert_called_with(
234+
self.user._id,
235+
initiator_guid=mock.ANY,
236+
)
233237

234238

235239
class TestDisableSpamUser(AdminTestCase):

api/users/tasks.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from framework import sentry
44
from framework.celery_tasks import app as celery_app
55

6+
from osf.models import OSFUser
7+
from osf.models.notification_type import NotificationTypeEnum
8+
69
logger = logging.getLogger(__name__)
710

811

@@ -32,3 +35,37 @@ def merge_users(merger_guid: str, mergee_guid: str):
3235
except Exception as exc:
3336
logger.exception(f'Unexpected error during background user merge: merger={merger_guid}, mergee={mergee_guid}')
3437
sentry.log_exception(exc)
38+
39+
40+
@celery_app.task(bind=True, name='api.users.tasks.confirm_user_ham')
41+
def confirm_user_ham(self, user_guid: str, initiator_guid: str | None = None):
42+
initiator_user = OSFUser.load(initiator_guid) if initiator_guid else None
43+
failed_ham = []
44+
try:
45+
user = OSFUser.objects.get(guids___id=user_guid)
46+
except OSFUser.DoesNotExist as exc:
47+
sentry.log_exception(exc)
48+
return str(exc)
49+
else:
50+
try:
51+
user.is_registered = True # back in the day spam users were de-registered
52+
failed_ham = user.confirm_ham(save=True, train_spam_services=False)
53+
except Exception as exc:
54+
sentry.log_exception(exc)
55+
56+
try:
57+
if initiator_user:
58+
NotificationTypeEnum.USER_CONFIRM_HAM_REPORT.instance.emit(
59+
user=initiator_user,
60+
message_frequency='instantly',
61+
event_context={
62+
'user_guid': user._id,
63+
'failed_ham': ', '.join(failed_ham),
64+
},
65+
save=False,
66+
)
67+
except Exception as exc:
68+
logger.exception('Failed to send HAM confirmation report email')
69+
sentry.log_exception(exc)
70+
71+
return True

notifications.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,13 @@ notification_types:
318318
template: 'website/templates/tou_notif.html.mako'
319319
tests: []
320320

321+
- name: user_confirm_ham_report
322+
subject: 'Confirm HAM report for {user_guid}'
323+
__docs__: Summary report sent to the admin who triggered Confirm HAM.
324+
object_content_type_model_name: osfuser
325+
template: 'website/templates/user_confirm_ham_report.html.mako'
326+
tests: []
327+
321328
- name: desk_registration_bulk_upload_product_owner
322329
subject: 'Registry Could Not Bulk Upload Registrations'
323330
object_content_type_model_name: osfuser

osf/models/notification_type.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class NotificationTypeEnum(str, Enum):
7878
USER_SPAM_FILES_DETECTED = 'user_spam_files_detected'
7979
USER_CROSSREF_DOI_PENDING = 'user_crossref_doi_pending'
8080
USER_TERMS_OF_USE_UPDATED = 'user_terms_of_use_updated' # added as a placeholder
81+
USER_CONFIRM_HAM_REPORT = 'user_confirm_ham_report'
8182

8283
# Node notifications
8384
NODE_FILE_UPDATED = 'node_file_updated'

osf/models/user.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,11 +1478,36 @@ def confirm_ham(self, save=False, train_spam_services=False):
14781478
self.reactivate_account()
14791479
super().confirm_ham(save=save, train_spam_services=train_spam_services)
14801480

1481+
failed_ham_ids = []
1482+
14811483
# Don't train on resources merely associated with spam user
14821484
for node in self.nodes.filter():
1483-
node.confirm_ham(save=save, train_spam_services=train_spam_services)
1485+
try:
1486+
node.confirm_ham(save=save, train_spam_services=train_spam_services)
1487+
except Exception as exc:
1488+
sentry.log_exception(exc)
1489+
failed_ham_ids.append(node._id)
1490+
continue
1491+
1492+
if not node.is_ham or getattr(node, 'is_deleted', False):
1493+
failed_ham_ids.append(node._id)
1494+
14841495
for preprint in self.preprints.filter():
1485-
preprint.confirm_ham(save=save, train_spam_services=train_spam_services)
1496+
try:
1497+
preprint.confirm_ham(save=save, train_spam_services=train_spam_services)
1498+
except Exception as exc:
1499+
sentry.log_exception(exc)
1500+
failed_ham_ids.append(preprint._id)
1501+
continue
1502+
1503+
if (
1504+
not preprint.is_ham
1505+
or getattr(preprint, 'is_deleted', False)
1506+
or getattr(preprint, 'deleted', None) is not None
1507+
):
1508+
failed_ham_ids.append(preprint._id)
1509+
1510+
return failed_ham_ids
14861511

14871512
@property
14881513
def is_assumed_ham(self):
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<%inherit file="notify_base.mako" />
2+
3+
<%def name="content()">
4+
<tr>
5+
<td style="border-collapse: collapse;">
6+
<h3 style="padding: 0; margin: 30px 0 10px 0; font-weight: 400;">
7+
Confirm HAM finished for user ${user_guid}
8+
</h3>
9+
</td>
10+
</tr>
11+
<tr>
12+
<td style="border-collapse: collapse;">
13+
% if failed_ham:
14+
<p style="margin: 0;">Failed nodes/preprints: ${failed_ham}</p>
15+
% endif
16+
</td>
17+
</tr>
18+
</%def>

0 commit comments

Comments
 (0)