mirror of
https://github.com/leaders-of-digital-9-task/backend.git
synced 2024-11-21 17:06:33 +03:00
added polygon and circle
This commit is contained in:
parent
4f15a29b07
commit
4ec02a8729
|
@ -1,4 +1,11 @@
|
|||
from dicom.api.views import ListCreateDicomApi, RetrieveUpdateDeleteDicomApi
|
||||
from dicom.api.views import (
|
||||
CreateCircleApi,
|
||||
CreatePolygonApi,
|
||||
ListCreateDicomApi,
|
||||
RetrieveUpdateDeleteCircleApi,
|
||||
RetrieveUpdateDeleteDicomApi,
|
||||
RetrieveUpdateDeletePolygonApi,
|
||||
)
|
||||
from django.urls import include, path
|
||||
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
|
||||
from users.api.views import RegisterView
|
||||
|
@ -24,6 +31,33 @@ urlpatterns = [
|
|||
RetrieveUpdateDeleteDicomApi.as_view(),
|
||||
name="get_update_delete_dicom",
|
||||
),
|
||||
path(
|
||||
"<str:slug>/polygon",
|
||||
CreatePolygonApi.as_view(),
|
||||
name="create_polygon",
|
||||
),
|
||||
path(
|
||||
"<str:slug>/circle",
|
||||
CreateCircleApi.as_view(),
|
||||
name="create_circle",
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
path(
|
||||
"shapes/",
|
||||
include(
|
||||
[
|
||||
path(
|
||||
"polygon/<int:id>",
|
||||
RetrieveUpdateDeletePolygonApi.as_view(),
|
||||
name="get_update_delete_polygon",
|
||||
),
|
||||
path(
|
||||
"circle/<int:id>",
|
||||
RetrieveUpdateDeleteCircleApi.as_view(),
|
||||
name="get_update_delete_circle",
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
|
|
@ -309,8 +309,6 @@ REST_FRAMEWORK = {
|
|||
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
|
||||
}
|
||||
|
||||
# django-cors-headers - https://github.com/adamchainz/django-cors-headers#setup
|
||||
CORS_URLS_REGEX = r"^/api/.*$"
|
||||
# By Default swagger ui is available only to admin user(s). You can change permission classes to change that
|
||||
# See more configuration options at https://drf-spectacular.readthedocs.io/en/latest/settings.html#settings
|
||||
SPECTACULAR_SETTINGS = {
|
||||
|
|
|
@ -1,5 +1,22 @@
|
|||
from dicom.models import Dicom
|
||||
from dicom.models import Circle, Coordinate, Dicom, Polygon
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework import serializers
|
||||
from rest_framework.generics import get_object_or_404
|
||||
|
||||
|
||||
def create_coordinate(coordinates, obj):
|
||||
for coordinate in coordinates:
|
||||
Coordinate.objects.create(
|
||||
x=coordinate["x"],
|
||||
y=coordinate["y"],
|
||||
shape=obj,
|
||||
)
|
||||
|
||||
|
||||
class CoordinateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Coordinate
|
||||
fields = ["x", "y"]
|
||||
|
||||
|
||||
class ListDicomSerializer(serializers.ModelSerializer):
|
||||
|
@ -16,9 +33,82 @@ class ListDicomSerializer(serializers.ModelSerializer):
|
|||
return Dicom.objects.create(**validated_data, user=self.context["request"].user)
|
||||
|
||||
|
||||
class BaseShapeSerializer(serializers.Serializer):
|
||||
type = serializers.ChoiceField(choices=["circle", "polygon"])
|
||||
image_number = serializers.IntegerField()
|
||||
coordinates = CoordinateSerializer(many=True)
|
||||
|
||||
|
||||
class DicomSerializer(serializers.ModelSerializer):
|
||||
file = serializers.FileField()
|
||||
shapes = serializers.SerializerMethodField("get_dicom_shapes")
|
||||
|
||||
@extend_schema_field(field=BaseShapeSerializer)
|
||||
def get_dicom_shapes(self, obj):
|
||||
return [x.serialize_self() for x in obj.shapes.all()]
|
||||
|
||||
class Meta:
|
||||
model = Dicom
|
||||
fields = ["file", "uploaded"]
|
||||
fields = ["file", "uploaded", "shapes"]
|
||||
|
||||
|
||||
class PolygonSerializer(serializers.ModelSerializer):
|
||||
coordinates = CoordinateSerializer(many=True)
|
||||
|
||||
class Meta:
|
||||
model = Polygon
|
||||
fields = ["id", "image_number", "coordinates"]
|
||||
extra_kwargs = {"id": {"read_only": True}}
|
||||
|
||||
def create(self, validated_data):
|
||||
if "coordinates" not in validated_data:
|
||||
raise serializers.ValidationError
|
||||
dicom = get_object_or_404(
|
||||
Dicom, slug=self.context["request"].parser_context["kwargs"]["slug"]
|
||||
)
|
||||
polygon = Polygon.objects.create(
|
||||
dicom=dicom, image_number=validated_data["image_number"]
|
||||
)
|
||||
|
||||
create_coordinate(validated_data["coordinates"], polygon)
|
||||
return polygon
|
||||
|
||||
def update(self, obj: Circle, validated_data):
|
||||
Coordinate.objects.filter(shape=obj).delete()
|
||||
create_coordinate(validated_data["coordinates"], obj)
|
||||
return obj
|
||||
|
||||
|
||||
class CircleSerializer(serializers.ModelSerializer):
|
||||
coordinates = CoordinateSerializer(many=True)
|
||||
|
||||
class Meta:
|
||||
model = Circle
|
||||
fields = ["id", "image_number", "radius", "coordinates"]
|
||||
extra_kwargs = {"id": {"read_only": True}}
|
||||
|
||||
def create(self, validated_data):
|
||||
if (
|
||||
"coordinates" not in validated_data
|
||||
and len(validated_data["coordinates"]) > 1
|
||||
):
|
||||
raise serializers.ValidationError
|
||||
dicom = get_object_or_404(
|
||||
Dicom, slug=self.context["request"].parser_context["kwargs"]["slug"]
|
||||
)
|
||||
circle = Circle.objects.create(
|
||||
dicom=dicom,
|
||||
image_number=validated_data["image_number"],
|
||||
radius=validated_data["radius"],
|
||||
)
|
||||
|
||||
create_coordinate(validated_data["coordinates"], circle)
|
||||
return circle
|
||||
|
||||
def update(self, obj: Circle, validated_data):
|
||||
Coordinate.objects.filter(shape=obj).delete()
|
||||
create_coordinate(validated_data["coordinates"], obj)
|
||||
if "radius" in validated_data:
|
||||
obj.radius = validated_data["radius"]
|
||||
obj.save()
|
||||
return obj
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework import generics
|
||||
from rest_framework.generics import get_object_or_404
|
||||
from rest_framework.parsers import FormParser, MultiPartParser
|
||||
|
||||
from ..models import Dicom
|
||||
from .serializers import DicomSerializer, ListDicomSerializer
|
||||
from ..models import Circle, Dicom, Polygon
|
||||
from .serializers import (
|
||||
CircleSerializer,
|
||||
DicomSerializer,
|
||||
ListDicomSerializer,
|
||||
PolygonSerializer,
|
||||
)
|
||||
|
||||
|
||||
class ListCreateDicomApi(generics.ListCreateAPIView):
|
||||
|
@ -34,3 +40,43 @@ class RetrieveUpdateDeleteDicomApi(generics.RetrieveUpdateDestroyAPIView):
|
|||
parser_classes = [MultiPartParser, FormParser]
|
||||
|
||||
lookup_field = "slug"
|
||||
|
||||
|
||||
class CreatePolygonApi(generics.CreateAPIView):
|
||||
serializer_class = PolygonSerializer
|
||||
|
||||
|
||||
class CreateCircleApi(generics.CreateAPIView):
|
||||
serializer_class = CircleSerializer
|
||||
|
||||
|
||||
class RetrieveUpdateDeletePolygonApi(generics.RetrieveUpdateDestroyAPIView):
|
||||
serializer_class = PolygonSerializer
|
||||
|
||||
def get_object(self):
|
||||
return get_object_or_404(
|
||||
Polygon, id=self.request.parser_context["kwargs"]["id"]
|
||||
)
|
||||
|
||||
@extend_schema(description="Note: coordinated are dropped on update")
|
||||
def put(self, request, *args, **kwargs):
|
||||
return self.update(request, *args, **kwargs)
|
||||
|
||||
@extend_schema(description="Note: coordinated are dropped on update")
|
||||
def patch(self, request, *args, **kwargs):
|
||||
return self.partial_update(request, *args, **kwargs)
|
||||
|
||||
|
||||
class RetrieveUpdateDeleteCircleApi(generics.RetrieveUpdateDestroyAPIView):
|
||||
serializer_class = CircleSerializer
|
||||
|
||||
def get_object(self):
|
||||
return get_object_or_404(Circle, id=self.request.parser_context["kwargs"]["id"])
|
||||
|
||||
@extend_schema(description="Note: coordinated are dropped on update")
|
||||
def patch(self, request, *args, **kwargs):
|
||||
return self.partial_update(request, *args, **kwargs)
|
||||
|
||||
@extend_schema(description="Note: coordinated are dropped on update")
|
||||
def put(self, request, *args, **kwargs):
|
||||
return self.update(request, *args, **kwargs)
|
||||
|
|
3
image_markuper/dicom/models/__init__.py
Normal file
3
image_markuper/dicom/models/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
# flake8: noqa
|
||||
from .base import Dicom
|
||||
from .blocks import BaseShape, Circle, Coordinate, Polygon
|
60
image_markuper/dicom/models/blocks.py
Normal file
60
image_markuper/dicom/models/blocks.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
from dicom.models import Dicom
|
||||
from django.db import models
|
||||
from polymorphic.models import PolymorphicModel
|
||||
|
||||
|
||||
class BaseShape(PolymorphicModel):
|
||||
dicom = models.ForeignKey(Dicom, related_name="shapes", on_delete=models.CASCADE)
|
||||
image_number = models.IntegerField()
|
||||
|
||||
def serialize_self(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def coordinates(self) -> [(int, int)]:
|
||||
return self.shape_coordinates.all().values("x", "y")
|
||||
|
||||
def __str__(self):
|
||||
return self.dicom.file.name
|
||||
|
||||
|
||||
class Coordinate(models.Model):
|
||||
x = models.IntegerField()
|
||||
y = models.IntegerField()
|
||||
|
||||
shape = models.ForeignKey(
|
||||
to=BaseShape,
|
||||
null=False,
|
||||
blank=False,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="shape_coordinates",
|
||||
)
|
||||
|
||||
|
||||
class Circle(BaseShape):
|
||||
radius = models.IntegerField()
|
||||
|
||||
def serialize_self(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"type": "circle",
|
||||
"image_number": self.image_number,
|
||||
"radius": self.radius,
|
||||
"coordinates": self.coordinates,
|
||||
}
|
||||
|
||||
def __str__(self):
|
||||
return f"circle on {self.dicom.file.name}"
|
||||
|
||||
|
||||
class Polygon(BaseShape):
|
||||
def serialize_self(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"type": "polygon",
|
||||
"image_number": self.image_number,
|
||||
"coordinates": self.coordinates,
|
||||
}
|
||||
|
||||
def __str__(self):
|
||||
return f"polygon on {self.dicom.file.name}"
|
Loading…
Reference in New Issue
Block a user