added archive and list of files upload

This commit is contained in:
Alexander Karpov 2022-10-29 22:46:19 +03:00
parent e3aa26a7df
commit a40bc6ad5b
9 changed files with 79 additions and 17 deletions

View File

@ -5,6 +5,7 @@ from dicom.api.views import (
RetrieveUpdateDeleteCircleApi, RetrieveUpdateDeleteCircleApi,
RetrieveUpdateDeleteDicomApi, RetrieveUpdateDeleteDicomApi,
RetrieveUpdateDeletePolygonApi, RetrieveUpdateDeletePolygonApi,
SmartFileUploadApi,
) )
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
@ -26,6 +27,7 @@ urlpatterns = [
include( include(
[ [
path("", ListCreateDicomApi.as_view(), name="list_create_dicom"), path("", ListCreateDicomApi.as_view(), name="list_create_dicom"),
path("upload", SmartFileUploadApi.as_view(), name="upload_dicom_api"),
path( path(
"<str:slug>", "<str:slug>",
RetrieveUpdateDeleteDicomApi.as_view(), RetrieveUpdateDeleteDicomApi.as_view(),

View File

@ -142,6 +142,10 @@ MIDDLEWARE = [
"django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware",
] ]
FILE_UPLOAD_HANDLERS = [
"django.core.files.uploadhandler.TemporaryFileUploadHandler",
]
# STATIC # STATIC
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#static-root # https://docs.djangoproject.com/en/dev/ref/settings/#static-root

View File

@ -112,3 +112,7 @@ class CircleSerializer(serializers.ModelSerializer):
obj.radius = validated_data["radius"] obj.radius = validated_data["radius"]
obj.save() obj.save()
return obj return obj
class SmartFileUploadSerializer(serializers.ModelSerializer):
file = serializers.FileField()

View File

@ -1,9 +1,13 @@
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
from rest_framework import generics from rest_framework import generics, status
from rest_framework.exceptions import ValidationError
from rest_framework.generics import get_object_or_404 from rest_framework.generics import get_object_or_404
from rest_framework.parsers import FormParser, MultiPartParser from rest_framework.parsers import FormParser, MultiPartParser
from rest_framework.response import Response
from rest_framework.views import APIView
from ..models import Circle, Dicom, Polygon from ..models import Circle, Dicom, Polygon
from ..services import process_files
from .serializers import ( from .serializers import (
CircleSerializer, CircleSerializer,
DicomSerializer, DicomSerializer,
@ -19,18 +23,6 @@ class ListCreateDicomApi(generics.ListCreateAPIView):
def get_queryset(self): def get_queryset(self):
return Dicom.objects.filter(user=self.request.user) return Dicom.objects.filter(user=self.request.user)
@extend_schema(
operation_id="upload_file",
request={
"multipart/form-data": {
"type": "object",
"properties": {"file": {"type": "string", "format": "binary"}},
}
},
)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class RetrieveUpdateDeleteDicomApi(generics.RetrieveUpdateDestroyAPIView): class RetrieveUpdateDeleteDicomApi(generics.RetrieveUpdateDestroyAPIView):
def get_queryset(self): def get_queryset(self):
@ -80,3 +72,16 @@ class RetrieveUpdateDeleteCircleApi(generics.RetrieveUpdateDestroyAPIView):
@extend_schema(description="Note: coordinated are dropped on update") @extend_schema(description="Note: coordinated are dropped on update")
def put(self, request, *args, **kwargs): def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs) return self.update(request, *args, **kwargs)
class SmartFileUploadApi(APIView):
parser_classes = [MultiPartParser, FormParser]
def post(self, request):
if "file" not in request.data:
raise ValidationError("no files")
d_list = process_files(request.FILES.getlist("file"), request.user)
return Response(
DicomSerializer(d_list.files.all(), many=True).data,
status=status.HTTP_201_CREATED,
)

View File

@ -1,3 +1,3 @@
# flake8: noqa # flake8: noqa
from .base import Dicom from .base import Dicom, ListOfDicom
from .blocks import BaseShape, Circle, Coordinate, Polygon from .blocks import BaseShape, Circle, Coordinate, Polygon

View File

@ -6,6 +6,10 @@ from utils.files import media_upload_path
User = get_user_model() User = get_user_model()
class ListOfDicom(models.Model):
pass
class Dicom(models.Model): class Dicom(models.Model):
class PathologyType(models.IntegerChoices): class PathologyType(models.IntegerChoices):
no_pathology = 0, "Без патологий" no_pathology = 0, "Без патологий"
@ -17,7 +21,10 @@ class Dicom(models.Model):
file = models.FileField(upload_to=media_upload_path) file = models.FileField(upload_to=media_upload_path)
uploaded = models.DateTimeField(auto_now_add=True) uploaded = models.DateTimeField(auto_now_add=True)
pathology_type = models.IntegerField(choices=PathologyType.choices) pathology_type = models.IntegerField(choices=PathologyType.choices, default=0)
list = models.ForeignKey(
ListOfDicom, related_name="files", null=True, on_delete=models.SET_NULL
)
def __str__(self): def __str__(self):
return self.file.name return self.file.name

View File

@ -19,8 +19,8 @@ class BaseShape(PolymorphicModel):
class Coordinate(models.Model): class Coordinate(models.Model):
x = models.IntegerField() x = models.FloatField()
y = models.IntegerField() y = models.FloatField()
shape = models.ForeignKey( shape = models.ForeignKey(
to=BaseShape, to=BaseShape,

View File

@ -0,0 +1,39 @@
import glob
import os
import shutil
import zipfile
from pathlib import Path
import magic
from dicom.models import Dicom, ListOfDicom
from django.core.files import File
from django.core.files.uploadedfile import InMemoryUploadedFile, TemporaryUploadedFile
from utils.generators import generate_charset
def process_files(files: list[TemporaryUploadedFile | InMemoryUploadedFile], user):
d_list = ListOfDicom.objects.create()
for file in files:
content_type = magic.from_file(file.temporary_file_path())
if content_type == "DICOM medical imaging data":
Dicom.objects.create(file=file, list=d_list, user=user)
elif "Zip" in content_type:
dit_path = f"/tmp/{generate_charset(10)}"
os.mkdir(dit_path)
with zipfile.ZipFile(file.temporary_file_path(), "r") as zip_ref:
zip_ref.extractall(dit_path)
files = glob.glob(dit_path + "/**/*", recursive=True)
for file_in_d in files:
if not os.path.isdir(file_in_d):
content_type = magic.from_file(file_in_d)
if content_type == "DICOM medical imaging data":
path = Path(file_in_d)
with path.open(mode="rb") as f:
Dicom.objects.create(
file=File(f, name=file_in_d.split("/")[-1]),
list=d_list,
user=user,
)
shutil.rmtree(dit_path)
return d_list

View File

@ -1,4 +1,5 @@
pytz==2022.5 # https://github.com/stub42/pytz pytz==2022.5 # https://github.com/stub42/pytz
python-magic==0.4.27
python-slugify==6.1.2 # https://github.com/un33k/python-slugify python-slugify==6.1.2 # https://github.com/un33k/python-slugify
Pillow==9.2.0 # https://github.com/python-pillow/Pillow Pillow==9.2.0 # https://github.com/python-pillow/Pillow
argon2-cffi==21.3.0 # https://github.com/hynek/argon2_cffi argon2-cffi==21.3.0 # https://github.com/hynek/argon2_cffi