From 8c53cb67454be88b9a78b885dc890a7965a603da Mon Sep 17 00:00:00 2001 From: Alexandr Karpov Date: Sun, 30 Oct 2022 16:40:46 +0300 Subject: [PATCH] added layer operations --- config/api_router.py | 20 ++++++---- image_markuper/dicom/api/serializers.py | 8 +++- image_markuper/dicom/api/views.py | 53 +++++++++++++++++++++++-- image_markuper/dicom/models/blocks.py | 20 +++++++++- 4 files changed, 87 insertions(+), 14 deletions(-) diff --git a/config/api_router.py b/config/api_router.py index 958acd5..04de9c4 100644 --- a/config/api_router.py +++ b/config/api_router.py @@ -1,10 +1,11 @@ from dicom.api.views import ( CreateCircleApi, - CreateroiApi, + CreateRoiApi, ListCreateDicomApi, + ListUpdateDicomImageNumberApi, RetrieveUpdateDeleteCircleApi, RetrieveUpdateDeleteDicomApi, - RetrieveUpdateDeleteroiApi, + RetrieveUpdateDeleteRoiApi, SmartFileUploadApi, ) from django.urls import include, path @@ -26,7 +27,7 @@ urlpatterns = [ "dicom/", include( [ - path("", ListCreateDicomApi.as_view(), name="list_create_dicom"), + path("", ListCreateDicomApi.as_view(), name="dicom_list_create"), path("upload", SmartFileUploadApi.as_view(), name="upload_dicom_api"), path( "", @@ -34,8 +35,8 @@ urlpatterns = [ name="get_update_delete_dicom", ), path( - "/Roi", - CreateroiApi.as_view(), + "/roi", + CreateRoiApi.as_view(), name="create_roi", ), path( @@ -43,6 +44,11 @@ urlpatterns = [ CreateCircleApi.as_view(), name="create_circle", ), + path( + "/", + ListUpdateDicomImageNumberApi.as_view(), + name="update_dicom_layer", + ), ] ), ), @@ -51,8 +57,8 @@ urlpatterns = [ include( [ path( - "Roi/", - RetrieveUpdateDeleteroiApi.as_view(), + "roi/", + RetrieveUpdateDeleteRoiApi.as_view(), name="get_update_delete_roi", ), path( diff --git a/image_markuper/dicom/api/serializers.py b/image_markuper/dicom/api/serializers.py index d104649..89927fb 100644 --- a/image_markuper/dicom/api/serializers.py +++ b/image_markuper/dicom/api/serializers.py @@ -34,11 +34,17 @@ class ListDicomSerializer(serializers.ModelSerializer): class BaseShapeSerializer(serializers.Serializer): - type = serializers.ChoiceField(choices=["circle", "Roi"]) + type = serializers.ChoiceField(choices=["circle", "roi"]) image_number = serializers.IntegerField() coordinates = CoordinateSerializer(many=True) +class BaseShapeLayerSerializer(serializers.Serializer): + type = serializers.ChoiceField(choices=["circle", "roi"]) + radius = serializers.FloatField(required=False) + coordinates = CoordinateSerializer(many=True) + + class DicomSerializer(serializers.ModelSerializer): file = serializers.FileField() shapes = serializers.SerializerMethodField("get_dicom_shapes") diff --git a/image_markuper/dicom/api/views.py b/image_markuper/dicom/api/views.py index a8e4db6..e952571 100644 --- a/image_markuper/dicom/api/views.py +++ b/image_markuper/dicom/api/views.py @@ -8,11 +8,14 @@ from rest_framework.response import Response from ..models import Circle, Dicom, Roi from ..services import process_files from .serializers import ( + BaseShapeLayerSerializer, + BaseShapeSerializer, CircleSerializer, DicomSerializer, ListDicomSerializer, RoiSerializer, SmartFileUploadSerializer, + create_coordinate, ) @@ -34,7 +37,7 @@ class RetrieveUpdateDeleteDicomApi(generics.RetrieveUpdateDestroyAPIView): lookup_field = "slug" -class CreateroiApi(generics.CreateAPIView): +class CreateRoiApi(generics.CreateAPIView): serializer_class = RoiSerializer @@ -42,7 +45,7 @@ class CreateCircleApi(generics.CreateAPIView): serializer_class = CircleSerializer -class RetrieveUpdateDeleteroiApi(generics.RetrieveUpdateDestroyAPIView): +class RetrieveUpdateDeleteRoiApi(generics.RetrieveUpdateDestroyAPIView): serializer_class = RoiSerializer def get_object(self): @@ -87,5 +90,47 @@ class SmartFileUploadApi(GenericAPIView): ) -class UpdateDicomLayerApi(GenericAPIView): - serializer_class = SmartFileUploadSerializer +class ListUpdateDicomImageNumberApi(GenericAPIView): + serializer_class = BaseShapeSerializer(many=True) + + @extend_schema( + request=None, + responses={200: BaseShapeSerializer(many=True)}, + operation_id="get_dicom_layer", + ) + def get(self, request, slug, layer): + shapes = [ + x.serialize_self_without_layer() + for x in get_object_or_404(Dicom, slug=slug).shapes.filter( + image_number=layer + ) + ] + return Response(shapes, status=status.HTTP_200_OK) + + @extend_schema( + request=BaseShapeLayerSerializer(many=True), + responses={201: DicomSerializer}, + operation_id="update_dicom_layer", + ) + def put(self, request, slug, layer): + dicom = get_object_or_404(Dicom, slug=slug) + dicom.shapes.filter(image_number=layer).delete() + serializer = BaseShapeLayerSerializer(data=request.data, many=True) + serializer.is_valid(raise_exception=True) + for shape in serializer.data: + if shape["type"] == "circle": + obj = Circle.objects.create( + dicom=dicom, image_number=layer, radius=shape["radius"] + ) + if len(shape["coordinates"]) > 1: + raise ValidationError + elif shape["type"] == "roi": + obj = Roi.objects.create(dicom=dicom, image_number=layer) + create_coordinate(shape["coordinates"], obj) + shapes = [ + x.serialize_self_without_layer() + for x in get_object_or_404(Dicom, slug=slug).shapes.filter( + image_number=layer + ) + ] + return Response(shapes, status=status.HTTP_200_OK) diff --git a/image_markuper/dicom/models/blocks.py b/image_markuper/dicom/models/blocks.py index d51abac..4246455 100644 --- a/image_markuper/dicom/models/blocks.py +++ b/image_markuper/dicom/models/blocks.py @@ -32,7 +32,7 @@ class Coordinate(models.Model): class Circle(BaseShape): - radius = models.IntegerField() + radius = models.FloatField() def serialize_self(self): return { @@ -43,6 +43,14 @@ class Circle(BaseShape): "coordinates": self.coordinates, } + def serialize_self_without_layer(self): + return { + "id": self.id, + "type": "circle", + "radius": self.radius, + "coordinates": self.coordinates, + } + def __str__(self): return f"circle on {self.dicom.file.name}" @@ -51,10 +59,18 @@ class Roi(BaseShape): def serialize_self(self): return { "id": self.id, - "type": "Roi", + "type": "roi", "image_number": self.image_number, "coordinates": self.coordinates, } + def serialize_self_without_layer(self): + return { + "id": self.id, + "type": "roi", + "radius": self.radius, + "coordinates": self.coordinates, + } + def __str__(self): return f"Roi on {self.dicom.file.name}"