Skip to content

Commit

Permalink
Move some post-moderation stuff to an async task
Browse files Browse the repository at this point in the history
This will make the approval of sound ticckets significantly faster and make the task easier for moderators.
  • Loading branch information
ffont committed Feb 13, 2024
1 parent 6a49644 commit 02fcc6d
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 38 deletions.
63 changes: 59 additions & 4 deletions general/tasks.py
Expand Up @@ -18,21 +18,18 @@
# See AUTHORS file.
#
import datetime
import hashlib
import json
import logging
import os
import time
import sentry_sdk


from celery import shared_task
from django.apps import apps
from django.conf import settings
from django.contrib.auth.models import User

from tickets import TICKET_STATUS_CLOSED
from tickets.models import Ticket
from tickets.models import Ticket, TicketComment
from utils.audioprocessing.freesound_audio_processing import set_timeout_alarm, check_if_free_space, \
FreesoundAudioProcessor, WorkerException, cancel_timeout_alarm, FreesoundAudioProcessorBeforeDescription
from utils.cache import invalidate_user_template_caches, invalidate_all_moderators_header_cache
Expand All @@ -41,6 +38,7 @@
workers_logger = logging.getLogger("workers")

WHITELIST_USER_TASK_NAME = 'whitelist_user'
POST_MODERATION_ASSIGNED_TICKETS_TASK_NAME = "post_moderation_assigned_tickets"
DELETE_USER_TASK_NAME = 'delete_user'
VALIDATE_BULK_DESCRIBE_CSV_TASK_NAME = "validate_bulk_describe_csv"
BULK_DESCRIBE_TASK_NAME = "bulk_describe"
Expand Down Expand Up @@ -114,6 +112,63 @@ def whitelist_user(ticket_ids=None, user_id=None):
'work_time': round(time.time() - start_time)}))


@shared_task(name=POST_MODERATION_ASSIGNED_TICKETS_TASK_NAME, queue=settings.CELERY_ASYNC_TASKS_QUEUE_NAME)
def post_moderation_assigned_tickets(ticket_ids=[], notification=None, msg=None, moderator_only=False, users_to_update=None, packs_to_update=None):
# Carry out post-processing tasks for the approved sounds like invlaidating caches, sending packs to process, etc...
# We do that in an async task to avoid moderation requests taking too long when approving sounds
workers_logger.info("Start post moderation assigned tickets (%s)" % json.dumps({
'task_name': POST_MODERATION_ASSIGNED_TICKETS_TASK_NAME,
'n_tickets': len(ticket_ids)}))
start_time = time.time()
tickets = Ticket.objects.filter(id__in=ticket_ids)

collect_users_and_packs = False
if not users_to_update and not packs_to_update:
collect_users_and_packs = True
users_to_update = set()
packs_to_update = set()

for ticket in tickets:
if collect_users_and_packs:
# Collect list of users and packls to update
# We only fill here users_to_update and packs_to_update if action is not
# "Delete". See comment in "Delete" action case some lines above
users_to_update.add(ticket.sound.user_id)
if ticket.sound.pack:
packs_to_update.add(ticket.sound.pack_id)

# Invalidate caches of related objects
invalidate_user_template_caches(ticket.sender.id)
invalidate_all_moderators_header_cache()

# Add new comments to the ticket
if msg is not None:
tc = TicketComment(sender=ticket.assignee,
text=msg,
ticket=ticket,
moderator_only=moderator_only)
tc.save()

# Send notification email to users
if notification is not None:
ticket.send_notification_emails(notification, Ticket.USER_ONLY)

# Update number of sounds for each user
Profile = apps.get_model('accounts.Profile')
for profile in Profile.objects.filter(user_id__in=list(users_to_update)):
profile.update_num_sounds()

# Process packs
Pack = apps.get_model('sounds.Pack')
for pack in Pack.objects.filter(id__in=list(packs_to_update)):
pack.process()

workers_logger.info("Finished post moderation assigned tickets (%s)" % json.dumps(
{'task_name': POST_MODERATION_ASSIGNED_TICKETS_TASK_NAME,
'n_tickets': len(ticket_ids),
'work_time': round(time.time() - start_time)}))


@shared_task(name=DELETE_USER_TASK_NAME, queue=settings.CELERY_ASYNC_TASKS_QUEUE_NAME)
def delete_user(user_id, deletion_action, deletion_reason):
try:
Expand Down
47 changes: 13 additions & 34 deletions tickets/views.py
Expand Up @@ -20,7 +20,6 @@


import datetime
import json

from django.conf import settings
from django.contrib import messages
Expand All @@ -33,7 +32,7 @@
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.shortcuts import render
from general.tasks import whitelist_user as whitelist_user_task
from general.tasks import whitelist_user as whitelist_user_task, post_moderation_assigned_tickets as post_moderation_assigned_tickets_task

from .models import Ticket, TicketComment, UserAnnotation
from sounds.models import Sound
Expand Down Expand Up @@ -556,9 +555,9 @@ def moderation_assigned(request, user_id):
# and sounds_to_update before we delete the sounds and they dissapear
# from the ticket (thus losing reference)
for ticket in tickets:
users_to_update.add(ticket.sound.user.profile)
users_to_update.add(ticket.sound.user_id)
if ticket.sound.pack:
packs_to_update.add(ticket.sound.pack)
packs_to_update.add(ticket.sound.pack_id)
Sound.objects.filter(ticket__in=tickets).delete()
# After we delete sounds that these tickets are associated with,
# we refresh the ticket list so that sound_id is null and this does
Expand All @@ -578,36 +577,16 @@ def moderation_assigned(request, user_id):
page in a few seconds to see the updated list of pending
tickets""" % ", ".join(users))

for ticket in tickets:
if action != "Delete":
# We only fill here users_to_update and packs_to_update if action is not
# "Delete". See comment in "Delete" action case some lines above
users_to_update.add(ticket.sound.user.profile)
if ticket.sound.pack:
packs_to_update.add(ticket.sound.pack)
invalidate_user_template_caches(ticket.sender.id)
invalidate_all_moderators_header_cache()
moderator_only = msg_form.cleaned_data.get("moderator_only", False)

if msg:
tc = TicketComment(sender=ticket.assignee,
text=msg,
ticket=ticket,
moderator_only=moderator_only)
tc.save()

# Send emails
if notification:
ticket.send_notification_emails(notification, Ticket.USER_ONLY)

# Update number of sounds for each user
for profile in users_to_update:
profile.update_num_sounds()

# Process packs
for pack in packs_to_update:
pack.process()

# Tirgger some async tasks to update user and pack counts, clear caches, send email notifications, etc.
post_moderation_assigned_tickets_task.delay(
ticket_ids=ticket_ids,
notification=notification,
msg=msg,
moderator_only=msg_form.cleaned_data.get("moderator_only", False),
users_to_update=list(users_to_update),
packs_to_update=list(packs_to_update)
)

messages.add_message(request, messages.INFO, f"{len(tickets)} ticket(s) successfully updated")
else:
clear_forms = False
Expand Down

0 comments on commit 02fcc6d

Please sign in to comment.