feat(websocket): submissions backend
This commit is contained in:
@@ -4,3 +4,6 @@ from django.apps import AppConfig
|
||||
class SubmissionsConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "submissions"
|
||||
|
||||
def ready(self):
|
||||
import submissions.signals # noqa
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import json
|
||||
from channels.generic.websocket import AsyncWebsocketConsumer
|
||||
from channels.db import database_sync_to_async
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
|
||||
from submissions.models import Submission, SubmissionFile, ImageCrop
|
||||
from submissions.schemas import SubmissionOut, SubmissionFileOut, ImageCropOut
|
||||
|
||||
|
||||
class SubmissionsConsumer(AsyncWebsocketConsumer):
|
||||
async def connect(self):
|
||||
self.user = self.scope["user"]
|
||||
|
||||
if isinstance(self.user, AnonymousUser):
|
||||
await self.close()
|
||||
return
|
||||
|
||||
self.user_group_name = f"submissions_user_{self.user.id}"
|
||||
await self.channel_layer.group_add(self.user_group_name, self.channel_name)
|
||||
await self.accept()
|
||||
|
||||
async def disconnect(self, close_code):
|
||||
await self.channel_layer.group_discard(self.user_group_name, self.channel_name)
|
||||
|
||||
async def submission_updated(self, event):
|
||||
submission_data = event["submission"]
|
||||
await self.send(text_data=json.dumps(submission_data))
|
||||
|
||||
async def submission_file_updated(self, event):
|
||||
file_data = event["file"]
|
||||
await self.send(text_data=json.dumps(file_data))
|
||||
|
||||
async def submission_crop_updated(self, event):
|
||||
crop_data = event["crop"]
|
||||
await self.send(text_data=json.dumps(crop_data))
|
||||
@@ -0,0 +1,6 @@
|
||||
from django.urls import re_path
|
||||
from submissions.consumers import SubmissionsConsumer
|
||||
|
||||
websocket_urlpatterns = [
|
||||
re_path(r"ws/submissions/$", SubmissionsConsumer.as_asgi()),
|
||||
]
|
||||
@@ -1,6 +1,7 @@
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
from ninja import Schema
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class ImageCropIn(Schema):
|
||||
@@ -11,7 +12,9 @@ class ImageCropIn(Schema):
|
||||
label: str | None = None
|
||||
|
||||
|
||||
class ImageCropOut(Schema):
|
||||
class ImageCropOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
x1: int
|
||||
y1: int
|
||||
@@ -23,7 +26,9 @@ class ImageCropOut(Schema):
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class SubmissionFileOut(Schema):
|
||||
class SubmissionFileOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
original_filename: str
|
||||
content_type: str
|
||||
@@ -34,7 +39,9 @@ class SubmissionFileOut(Schema):
|
||||
crops: list[ImageCropOut] = []
|
||||
|
||||
|
||||
class SubmissionOut(Schema):
|
||||
class SubmissionOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
game_id: int | None = None
|
||||
notes: str | None = None
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import json
|
||||
from django.db.models.signals import 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
|
||||
from submissions.schemas import SubmissionOut, SubmissionFileOut, ImageCropOut
|
||||
|
||||
|
||||
channel_layer = get_channel_layer()
|
||||
|
||||
|
||||
@receiver(post_save, sender=Submission)
|
||||
def broadcast_submission_update(sender, instance, created, **kwargs):
|
||||
if not instance.user:
|
||||
return
|
||||
|
||||
user_group_name = f"submissions_user_{instance.user.id}"
|
||||
schema = SubmissionOut.model_validate(instance)
|
||||
submission_data = schema.model_dump(mode="json")
|
||||
|
||||
async_to_sync(channel_layer.group_send)(
|
||||
user_group_name,
|
||||
{
|
||||
"type": "submission.updated",
|
||||
"submission": submission_data,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@receiver(post_save, sender=SubmissionFile)
|
||||
def broadcast_submission_file_update(sender, instance, created, **kwargs):
|
||||
submission = instance.submission
|
||||
if not submission.user:
|
||||
return
|
||||
|
||||
user_group_name = f"submissions_user_{submission.user.id}"
|
||||
schema = SubmissionFileOut.model_validate(instance)
|
||||
file_data = schema.model_dump(mode="json")
|
||||
|
||||
async_to_sync(channel_layer.group_send)(
|
||||
user_group_name,
|
||||
{
|
||||
"type": "submission.file.updated",
|
||||
"file": file_data,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@receiver(post_save, sender=ImageCrop)
|
||||
def broadcast_image_crop_update(sender, instance, created, **kwargs):
|
||||
submission = instance.submission_file.submission
|
||||
if not submission.user:
|
||||
return
|
||||
|
||||
user_group_name = f"submissions_user_{submission.user.id}"
|
||||
schema = ImageCropOut.model_validate(instance)
|
||||
crop_data = schema.model_dump(mode="json")
|
||||
|
||||
async_to_sync(channel_layer.group_send)(
|
||||
user_group_name,
|
||||
{
|
||||
"type": "submission.crop.updated",
|
||||
"crop": crop_data,
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user