feat(websocket): frontend websocket
This commit is contained in:
@@ -1,4 +1,17 @@
|
||||
from django.apps import AppConfig
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
|
||||
from channels_redis.serializers import JSONSerializer, registry
|
||||
|
||||
import json
|
||||
|
||||
|
||||
class CJSONSerializer(JSONSerializer):
|
||||
"""Override `JSONSerializer` to use `DjangoJSONEncoder`."""
|
||||
|
||||
def as_bytes(self, message, *args, **kwargs): # noqa: ANN001 ANN002 ANN003 ANN201
|
||||
message = json.dumps(message, *args, **kwargs, cls=DjangoJSONEncoder)
|
||||
return message.encode("utf-8")
|
||||
|
||||
|
||||
class SubmissionsConfig(AppConfig):
|
||||
@@ -6,4 +19,6 @@ class SubmissionsConfig(AppConfig):
|
||||
name = "submissions"
|
||||
|
||||
def ready(self):
|
||||
registry.register_serializer("cjson", CJSONSerializer)
|
||||
|
||||
import submissions.signals # noqa
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
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):
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
from ninja import Schema
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class ImageCropIn(Schema):
|
||||
@@ -12,9 +11,7 @@ class ImageCropIn(Schema):
|
||||
label: str | None = None
|
||||
|
||||
|
||||
class ImageCropOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
class ImageCropOut(Schema):
|
||||
id: UUID
|
||||
x1: int
|
||||
y1: int
|
||||
@@ -26,9 +23,7 @@ class ImageCropOut(BaseModel):
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class SubmissionFileOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
class SubmissionFileOut(Schema):
|
||||
id: UUID
|
||||
original_filename: str
|
||||
content_type: str
|
||||
@@ -39,9 +34,7 @@ class SubmissionFileOut(BaseModel):
|
||||
crops: list[ImageCropOut] = []
|
||||
|
||||
|
||||
class SubmissionOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
class SubmissionOut(Schema):
|
||||
id: UUID
|
||||
game_id: int | None = None
|
||||
notes: str | None = None
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import json
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from asgiref.sync import async_to_sync
|
||||
@@ -17,15 +16,14 @@ def broadcast_submission_update(sender, instance, created, **kwargs):
|
||||
return
|
||||
|
||||
user_group_name = f"submissions_user_{instance.user.id}"
|
||||
schema = SubmissionOut.model_validate(instance)
|
||||
submission_data = schema.model_dump(mode="json")
|
||||
submission_data = SubmissionOut.from_orm(instance).dict()
|
||||
|
||||
async_to_sync(channel_layer.group_send)(
|
||||
user_group_name,
|
||||
{
|
||||
"type": "submission.updated",
|
||||
"submission": submission_data,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -36,15 +34,14 @@ def broadcast_submission_file_update(sender, instance, created, **kwargs):
|
||||
return
|
||||
|
||||
user_group_name = f"submissions_user_{submission.user.id}"
|
||||
schema = SubmissionFileOut.model_validate(instance)
|
||||
file_data = schema.model_dump(mode="json")
|
||||
file_data = SubmissionFileOut.from_orm(instance).dict()
|
||||
|
||||
async_to_sync(channel_layer.group_send)(
|
||||
user_group_name,
|
||||
{
|
||||
"type": "submission.file.updated",
|
||||
"file": file_data,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -55,13 +52,12 @@ def broadcast_image_crop_update(sender, instance, created, **kwargs):
|
||||
return
|
||||
|
||||
user_group_name = f"submissions_user_{submission.user.id}"
|
||||
schema = ImageCropOut.model_validate(instance)
|
||||
crop_data = schema.model_dump(mode="json")
|
||||
crop_data = ImageCropOut.from_orm(instance).dict()
|
||||
|
||||
async_to_sync(channel_layer.group_send)(
|
||||
user_group_name,
|
||||
{
|
||||
"type": "submission.crop.updated",
|
||||
"crop": crop_data,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -5,25 +5,28 @@ from PIL import Image
|
||||
import pytesseract
|
||||
|
||||
|
||||
def _process_ocr_file(file) -> bool:
|
||||
if file.ocr_status != OCRStatus.TO_PROCESS:
|
||||
def _process_ocr_file(obj, file, crop: ImageCrop | None = None) -> bool:
|
||||
if obj.ocr_status != OCRStatus.TO_PROCESS:
|
||||
return False
|
||||
|
||||
file.ocr_status = OCRStatus.PROCESSING
|
||||
file.save(update_fields=["ocr_status"])
|
||||
obj.ocr_status = OCRStatus.PROCESSING
|
||||
obj.save(update_fields=["ocr_status"])
|
||||
|
||||
try:
|
||||
file.ocr_raw_data = pytesseract.image_to_string(Image.open(file.file))
|
||||
image = Image.open(file)
|
||||
|
||||
except Exception:
|
||||
file.ocr_status = OCRStatus.ERROR
|
||||
file.save(update_fields=["ocr_status"])
|
||||
if crop:
|
||||
image = image.crop((crop.x1, crop.y1, crop.x2, crop.y2))
|
||||
|
||||
return False
|
||||
obj.ocr_raw_data = pytesseract.image_to_string(image)
|
||||
obj.ocr_status = OCRStatus.PROCESSED
|
||||
|
||||
except Exception as e:
|
||||
obj.ocr_status = OCRStatus.ERROR
|
||||
raise e
|
||||
|
||||
finally:
|
||||
file.ocr_status = OCRStatus.PROCESSED
|
||||
file.save(update_fields=["ocr_status", "ocr_raw_data"])
|
||||
obj.save(update_fields=["ocr_status", "ocr_raw_data"])
|
||||
|
||||
return True
|
||||
|
||||
@@ -42,7 +45,7 @@ def process_submission_file(file_id: str):
|
||||
if not file:
|
||||
return
|
||||
|
||||
_process_ocr_file(file)
|
||||
_process_ocr_file(file, file.file)
|
||||
|
||||
process_crops.enqueue(file_id)
|
||||
|
||||
@@ -65,4 +68,4 @@ def process_crop(crop_id: str):
|
||||
if not crop:
|
||||
return
|
||||
|
||||
_process_ocr_file(crop)
|
||||
_process_ocr_file(crop, crop.submission_file.file, crop=crop)
|
||||
|
||||
Reference in New Issue
Block a user