mirror of
https://github.com/leaders-of-digital-9-task/backend.git
synced 2024-11-22 01:16: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 django.urls import include, path
|
||||||
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
|
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
|
||||||
from users.api.views import RegisterView
|
from users.api.views import RegisterView
|
||||||
|
@ -24,6 +31,33 @@ urlpatterns = [
|
||||||
RetrieveUpdateDeleteDicomApi.as_view(),
|
RetrieveUpdateDeleteDicomApi.as_view(),
|
||||||
name="get_update_delete_dicom",
|
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",
|
"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
|
# 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
|
# See more configuration options at https://drf-spectacular.readthedocs.io/en/latest/settings.html#settings
|
||||||
SPECTACULAR_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 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):
|
class ListDicomSerializer(serializers.ModelSerializer):
|
||||||
|
@ -16,9 +33,82 @@ class ListDicomSerializer(serializers.ModelSerializer):
|
||||||
return Dicom.objects.create(**validated_data, user=self.context["request"].user)
|
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):
|
class DicomSerializer(serializers.ModelSerializer):
|
||||||
file = serializers.FileField()
|
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:
|
class Meta:
|
||||||
model = Dicom
|
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 drf_spectacular.utils import extend_schema
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
|
from rest_framework.generics import get_object_or_404
|
||||||
from rest_framework.parsers import FormParser, MultiPartParser
|
from rest_framework.parsers import FormParser, MultiPartParser
|
||||||
|
|
||||||
from ..models import Dicom
|
from ..models import Circle, Dicom, Polygon
|
||||||
from .serializers import DicomSerializer, ListDicomSerializer
|
from .serializers import (
|
||||||
|
CircleSerializer,
|
||||||
|
DicomSerializer,
|
||||||
|
ListDicomSerializer,
|
||||||
|
PolygonSerializer,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ListCreateDicomApi(generics.ListCreateAPIView):
|
class ListCreateDicomApi(generics.ListCreateAPIView):
|
||||||
|
@ -34,3 +40,43 @@ class RetrieveUpdateDeleteDicomApi(generics.RetrieveUpdateDestroyAPIView):
|
||||||
parser_classes = [MultiPartParser, FormParser]
|
parser_classes = [MultiPartParser, FormParser]
|
||||||
|
|
||||||
lookup_field = "slug"
|
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