103 lines
3.3 KiB
Python
103 lines
3.3 KiB
Python
from django.db import transaction
|
|
from django.db.models.signals import post_delete, post_save
|
|
from django.dispatch import receiver
|
|
from asgiref.sync import async_to_sync
|
|
from channels.layers import get_channel_layer
|
|
|
|
from submissions.models import Submission, SubmissionFile, ImageCrop, Scope
|
|
from submissions.schemas import SubmissionOut, SubmissionFileOut, ImageCropOut
|
|
|
|
|
|
# Staff see (and OCR/crop) every player's submissions, so they get every update
|
|
STAFF_GROUP_NAME = "submissions_staff"
|
|
|
|
|
|
def _broadcast_submission_event(submission: Submission, message: dict):
|
|
channel_layer = get_channel_layer()
|
|
groups = [STAFF_GROUP_NAME]
|
|
if submission.user_id:
|
|
groups.append(f"submissions_user_{submission.user_id}")
|
|
|
|
for group_name in groups:
|
|
async_to_sync(channel_layer.group_send)(group_name, message)
|
|
|
|
|
|
@receiver(post_save, sender=Submission)
|
|
def broadcast_submission_update(sender, instance, created, **kwargs):
|
|
_broadcast_submission_event(
|
|
instance,
|
|
{
|
|
"type": "submission.updated",
|
|
"submission": SubmissionOut.from_orm(instance).dict(),
|
|
},
|
|
)
|
|
|
|
|
|
@receiver(post_save, sender=SubmissionFile)
|
|
def broadcast_submission_file_update(sender, instance, created, **kwargs):
|
|
_broadcast_submission_event(
|
|
instance.submission,
|
|
{
|
|
"type": "submission.file.updated",
|
|
"file": SubmissionFileOut.from_orm(instance).dict(),
|
|
},
|
|
)
|
|
|
|
|
|
@receiver(post_save, sender=ImageCrop)
|
|
def broadcast_image_crop_update(sender, instance, created, **kwargs):
|
|
_broadcast_submission_event(
|
|
instance.submission_file.submission,
|
|
{
|
|
"type": "submission.crop.updated",
|
|
"crop": ImageCropOut.from_orm(instance).dict(),
|
|
},
|
|
)
|
|
|
|
|
|
@receiver(post_delete, sender=ImageCrop)
|
|
def broadcast_image_crop_delete(sender, instance, **kwargs):
|
|
_broadcast_submission_event(
|
|
instance.submission_file.submission,
|
|
{
|
|
"type": "submission.crop.deleted",
|
|
"crop": {
|
|
"id": str(instance.id),
|
|
"submission_file_id": str(instance.submission_file_id),
|
|
},
|
|
},
|
|
)
|
|
|
|
|
|
def broadcast_scope_updates(changes: dict[Scope, set[int]], /):
|
|
"""Tell users their scope changed (closed/opened, (un)assigned), once the
|
|
current transaction commits, so the frontend can move them to the right
|
|
match without a refresh.
|
|
|
|
Args:
|
|
changes: Changed scopes, each with every user id that was or now is
|
|
assigned to it.
|
|
"""
|
|
|
|
def send():
|
|
channel_layer = get_channel_layer()
|
|
for scope, user_ids in changes.items():
|
|
assigned_ids = set(scope.assigned_users.values_list("id", flat=True))
|
|
for user_id in user_ids:
|
|
async_to_sync(channel_layer.group_send)(
|
|
f"submissions_user_{user_id}",
|
|
{
|
|
"type": "scope.updated",
|
|
"scope": {
|
|
"id": str(scope.id),
|
|
"is_active": scope.is_active,
|
|
"assigned": user_id in assigned_ids,
|
|
},
|
|
},
|
|
)
|
|
|
|
if changes:
|
|
# robust: a websocket hiccup only gets logged, it mustn't fail the
|
|
# (already committed) sync or admin save
|
|
transaction.on_commit(send, robust=True)
|