From 070cbc884c88f23c5f98600cac9b65031e1dc998 Mon Sep 17 00:00:00 2001 From: Sergey Misuk Date: Wed, 22 May 2024 11:59:44 +0300 Subject: [PATCH] Add duplicate search result API --- src/hope_dedup_engine/apps/api/const.py | 3 ++ .../apps/api/migrations/0001_initial.py | 45 +++++++++---------- src/hope_dedup_engine/apps/api/models/auth.py | 5 +-- .../apps/api/models/deduplication.py | 42 ++++++++--------- src/hope_dedup_engine/apps/api/serializers.py | 22 ++++++++- src/hope_dedup_engine/apps/api/urls.py | 11 ++++- src/hope_dedup_engine/apps/api/views.py | 23 ++++++++-- src/hope_dedup_engine/apps/core/storage.py | 4 +- .../apps/faces/utils/duplication_detector.py | 2 +- tests/api/{const.py => api_const.py} | 3 +- tests/api/conftest.py | 3 +- tests/api/test_auth.py | 4 +- tests/api/test_business_logic.py | 13 +++++- tests/api/test_deduplication_set_create.py | 2 +- tests/api/test_deduplication_set_delete.py | 2 +- tests/api/test_deduplication_set_list.py | 3 +- tests/api/test_image_bulk_create.py | 2 +- tests/api/test_image_bulk_delete.py | 2 +- tests/api/test_image_create.py | 2 +- tests/api/test_image_delete.py | 2 +- tests/api/test_image_list.py | 2 +- tests/extras/testutils/factories/api.py | 16 ++++++- tests/faces/{const.py => faces_const.py} | 0 tests/faces/fixtures/duplication_detector.py | 2 +- tests/faces/test_duplication_detector.py | 4 +- tests/faces/test_tasks.py | 2 +- 26 files changed, 136 insertions(+), 85 deletions(-) rename tests/api/{const.py => api_const.py} (82%) rename tests/faces/{const.py => faces_const.py} (100%) diff --git a/src/hope_dedup_engine/apps/api/const.py b/src/hope_dedup_engine/apps/api/const.py index 1533c068..cf066e62 100644 --- a/src/hope_dedup_engine/apps/api/const.py +++ b/src/hope_dedup_engine/apps/api/const.py @@ -11,3 +11,6 @@ BULK = "bulk" BULK_IMAGE = f"{IMAGE}_{BULK}" BULK_IMAGE_LIST = f"{IMAGE_LIST}_{BULK}" + +DUPLICATE = "duplicate" +DUPLICATE_LIST = f"{DUPLICATE}s" diff --git a/src/hope_dedup_engine/apps/api/migrations/0001_initial.py b/src/hope_dedup_engine/apps/api/migrations/0001_initial.py index 563e0da9..a2b8aeed 100644 --- a/src/hope_dedup_engine/apps/api/migrations/0001_initial.py +++ b/src/hope_dedup_engine/apps/api/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.0.6 on 2024-05-20 12:50 +# Generated by Django 5.0.6 on 2024-05-21 07:23 import django.db.models.deletion import uuid @@ -28,10 +28,10 @@ class Migration(migrations.Migration): choices=[(0, "Clean"), (1, "Dirty"), (2, "Processing"), (3, "Error")], default=0 ), ), - ("deleted", models.BooleanField(default=False, verbose_name="deleted")), + ("deleted", models.BooleanField(default=False)), ("error", models.CharField(blank=True, max_length=255, null=True)), - ("created_at", models.DateTimeField(auto_now_add=True, verbose_name="created at")), - ("updated_at", models.DateTimeField(auto_now=True, verbose_name="updated at")), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), ( "created_by", models.ForeignKey( @@ -58,6 +58,20 @@ class Migration(migrations.Migration): ), ], ), + migrations.CreateModel( + name="Duplicate", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("first_reference_pk", models.IntegerField()), + ("second_reference_pk", models.IntegerField()), + ("first_filename", models.CharField(max_length=255)), + ("second_filename", models.CharField(max_length=255)), + ( + "deduplication_set", + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.deduplicationset"), + ), + ], + ), migrations.CreateModel( name="HDEToken", fields=[ @@ -69,7 +83,6 @@ class Migration(migrations.Migration): on_delete=django.db.models.deletion.CASCADE, related_name="auth_tokens", to=settings.AUTH_USER_MODEL, - verbose_name="User", ), ), ], @@ -79,27 +92,13 @@ class Migration(migrations.Migration): "abstract": False, }, ), - migrations.CreateModel( - name="Duplicate", - fields=[ - ("id", models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), - ("filename", models.CharField(max_length=255)), - ( - "deduplication_set", - models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.deduplicationset"), - ), - ], - options={ - "abstract": False, - "unique_together": {("deduplication_set", "filename")}, - }, - ), migrations.CreateModel( name="Image", fields=[ ("id", models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ("reference_pk", models.IntegerField()), ("filename", models.CharField(max_length=255)), - ("created_at", models.DateTimeField(auto_now_add=True, verbose_name="created at")), + ("created_at", models.DateTimeField(auto_now_add=True)), ( "created_by", models.ForeignKey( @@ -115,9 +114,5 @@ class Migration(migrations.Migration): models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.deduplicationset"), ), ], - options={ - "abstract": False, - "unique_together": {("deduplication_set", "filename")}, - }, ), ] diff --git a/src/hope_dedup_engine/apps/api/models/auth.py b/src/hope_dedup_engine/apps/api/models/auth.py index 1ff0efe0..025370bd 100644 --- a/src/hope_dedup_engine/apps/api/models/auth.py +++ b/src/hope_dedup_engine/apps/api/models/auth.py @@ -1,11 +1,8 @@ from django.conf import settings from django.db import models -from django.utils.translation import gettext_lazy as _ from rest_framework.authtoken.models import Token class HDEToken(Token): - user = models.ForeignKey( - settings.AUTH_USER_MODEL, related_name="auth_tokens", on_delete=models.CASCADE, verbose_name=_("User") - ) + user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="auth_tokens", on_delete=models.CASCADE) diff --git a/src/hope_dedup_engine/apps/api/models/deduplication.py b/src/hope_dedup_engine/apps/api/models/deduplication.py index 14ea5e95..faf8a8fe 100644 --- a/src/hope_dedup_engine/apps/api/models/deduplication.py +++ b/src/hope_dedup_engine/apps/api/models/deduplication.py @@ -2,17 +2,16 @@ from django.conf import settings from django.db import models -from django.utils.translation import gettext_lazy as _ from hope_dedup_engine.apps.security.models import ExternalSystem class DeduplicationSet(models.Model): class State(models.IntegerChoices): - CLEAN = 0, _("Clean") # Deduplication set is created or already processed - DIRTY = 1, _("Dirty") # Images are added to deduplication set, but not yet processed - PROCESSING = 2, _("Processing") # Images are being processed - ERROR = 3, _("Error") # Error occurred + CLEAN = 0, "Clean" # Deduplication set is created or already processed + DIRTY = 1, "Dirty" # Images are added to deduplication set, but not yet processed + PROCESSING = 2, "Processing" # Images are being processed + ERROR = 3, "Error" # Error occurred id = models.UUIDField(primary_key=True, default=uuid4) name = models.CharField(max_length=100) @@ -21,38 +20,33 @@ class State(models.IntegerChoices): choices=State.choices, default=State.CLEAN, ) - deleted = models.BooleanField(_("deleted"), null=False, blank=False, default=False) + deleted = models.BooleanField(null=False, blank=False, default=False) external_system = models.ForeignKey(ExternalSystem, on_delete=models.CASCADE) error = models.CharField(max_length=255, null=True, blank=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True, related_name="+" ) - created_at = models.DateTimeField(_("created at"), auto_now_add=True) + created_at = models.DateTimeField(auto_now_add=True) updated_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True, related_name="+" ) - updated_at = models.DateTimeField(_("updated at"), auto_now=True) + updated_at = models.DateTimeField(auto_now=True) -class ImagePath(models.Model): +class Image(models.Model): id = models.UUIDField(primary_key=True, default=uuid4) deduplication_set = models.ForeignKey(DeduplicationSet, on_delete=models.CASCADE) + reference_pk = models.IntegerField() filename = models.CharField(max_length=255) - - class Meta: - abstract = True - unique_together = ("deduplication_set", "filename") - - -class Duplicate(ImagePath): - pass - - -class Image(ImagePath): created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True, related_name="+" ) - created_at = models.DateTimeField(_("created at"), auto_now_add=True) - # - # class Meta: - # unique_together = ("deduplication_set", "filename") + created_at = models.DateTimeField(auto_now_add=True) + + +class Duplicate(models.Model): + deduplication_set = models.ForeignKey(DeduplicationSet, on_delete=models.CASCADE) + first_reference_pk = models.IntegerField() + first_filename = models.CharField(max_length=255) + second_reference_pk = models.IntegerField() + second_filename = models.CharField(max_length=255) diff --git a/src/hope_dedup_engine/apps/api/serializers.py b/src/hope_dedup_engine/apps/api/serializers.py index 02832d4a..ba5ac220 100644 --- a/src/hope_dedup_engine/apps/api/serializers.py +++ b/src/hope_dedup_engine/apps/api/serializers.py @@ -1,7 +1,7 @@ from rest_framework import serializers from hope_dedup_engine.apps.api.models import DeduplicationSet -from hope_dedup_engine.apps.api.models.deduplication import Image +from hope_dedup_engine.apps.api.models.deduplication import Duplicate, Image class DeduplicationSetSerializer(serializers.ModelSerializer): @@ -18,3 +18,23 @@ class Meta: model = Image fields = "__all__" read_only_fields = "created_by", "created_at" + + +class EntrySerializer(serializers.Serializer): + reference_pk = serializers.SerializerMethodField() + filename = serializers.SerializerMethodField() + + def __init__(self, prefix: str, *args, **kwargs) -> None: + self._prefix = prefix + super().__init__(*args, **kwargs) + + def get_reference_pk(self, duplicate: Duplicate) -> int: + return getattr(duplicate, f"{self._prefix}_reference_pk") + + def get_filename(self, duplicate: Duplicate) -> str: + return getattr(duplicate, f"{self._prefix}_filename") + + +class DuplicateSerializer(serializers.Serializer): + first = EntrySerializer(prefix="first", source="*") + second = EntrySerializer(prefix="second", source="*") diff --git a/src/hope_dedup_engine/apps/api/urls.py b/src/hope_dedup_engine/apps/api/urls.py index b50b2d4d..78b1ff4f 100644 --- a/src/hope_dedup_engine/apps/api/urls.py +++ b/src/hope_dedup_engine/apps/api/urls.py @@ -3,8 +3,14 @@ from rest_framework import routers from rest_framework_nested import routers as nested_routers -from hope_dedup_engine.apps.api.const import BULK_IMAGE_LIST, DEDUPLICATION_SET, DEDUPLICATION_SET_LIST, IMAGE_LIST -from hope_dedup_engine.apps.api.views import BulkImageViewSet, DeduplicationSetViewSet, ImageViewSet +from hope_dedup_engine.apps.api.const import ( + BULK_IMAGE_LIST, + DEDUPLICATION_SET, + DEDUPLICATION_SET_LIST, + DUPLICATE_LIST, + IMAGE_LIST, +) +from hope_dedup_engine.apps.api.views import BulkImageViewSet, DeduplicationSetViewSet, DuplicateViewSet, ImageViewSet router = routers.SimpleRouter() router.register(DEDUPLICATION_SET_LIST, DeduplicationSetViewSet, basename=DEDUPLICATION_SET_LIST) @@ -12,5 +18,6 @@ deduplication_sets_router = nested_routers.NestedSimpleRouter(router, DEDUPLICATION_SET_LIST, lookup=DEDUPLICATION_SET) deduplication_sets_router.register(IMAGE_LIST, ImageViewSet, basename=IMAGE_LIST) deduplication_sets_router.register(BULK_IMAGE_LIST, BulkImageViewSet, basename=BULK_IMAGE_LIST) +deduplication_sets_router.register(DUPLICATE_LIST, DuplicateViewSet, basename=DUPLICATE_LIST) urlpatterns = [path("", include(router.urls)), path("", include(deduplication_sets_router.urls))] diff --git a/src/hope_dedup_engine/apps/api/views.py b/src/hope_dedup_engine/apps/api/views.py index 9c3a229e..9afb7f70 100644 --- a/src/hope_dedup_engine/apps/api/views.py +++ b/src/hope_dedup_engine/apps/api/views.py @@ -20,8 +20,8 @@ ) from hope_dedup_engine.apps.api.const import DEDUPLICATION_SET_FILTER, DEDUPLICATION_SET_PARAM from hope_dedup_engine.apps.api.models import DeduplicationSet -from hope_dedup_engine.apps.api.models.deduplication import Image -from hope_dedup_engine.apps.api.serializers import DeduplicationSetSerializer, ImageSerializer +from hope_dedup_engine.apps.api.models.deduplication import Duplicate, Image +from hope_dedup_engine.apps.api.serializers import DeduplicationSetSerializer, DuplicateSerializer, ImageSerializer from hope_dedup_engine.apps.api.utils import delete_model_data, start_processing MESSAGE = "message" @@ -49,15 +49,20 @@ def perform_destroy(self, instance: DeduplicationSet) -> None: instance.save() delete_model_data(instance) + @staticmethod + def _start_processing(deduplication_set: DeduplicationSet) -> None: + Duplicate.objects.filter(deduplication_set=deduplication_set).delete() + start_processing(deduplication_set) + @action(detail=True, methods=(HTTPMethod.POST,)) def process(self, request: Request, pk: UUID | None = None) -> Response: deduplication_set = DeduplicationSet.objects.get(pk=pk) match deduplication_set.state: case DeduplicationSet.State.CLEAN | DeduplicationSet.State.ERROR: - start_processing(deduplication_set) + self._start_processing(deduplication_set) return Response({MESSAGE: RETRYING}) case DeduplicationSet.State.DIRTY: - start_processing(deduplication_set) + self._start_processing(deduplication_set) return Response({MESSAGE: STARTED}) case DeduplicationSet.State.PROCESSING: return Response({MESSAGE: ALREADY_PROCESSING}, status=status.HTTP_400_BAD_REQUEST) @@ -149,3 +154,13 @@ def clear(self, request: Request, deduplication_set_pk: str) -> Response: deduplication_set.updated_by = request.user deduplication_set.save() return Response(status=status.HTTP_204_NO_CONTENT) + + +class DuplicateViewSet(nested_viewsets.NestedViewSetMixin, mixins.ListModelMixin, viewsets.GenericViewSet): + authentication_classes = (HDETokenAuthentication,) + permission_classes = IsAuthenticated, AssignedToExternalSystem, UserAndDeduplicationSetAreOfTheSameSystem + serializer_class = DuplicateSerializer + queryset = Duplicate.objects.all() + parent_lookup_kwargs = { + DEDUPLICATION_SET_PARAM: DEDUPLICATION_SET_FILTER, + } diff --git a/src/hope_dedup_engine/apps/core/storage.py b/src/hope_dedup_engine/apps/core/storage.py index 7faff3a3..438efa24 100644 --- a/src/hope_dedup_engine/apps/core/storage.py +++ b/src/hope_dedup_engine/apps/core/storage.py @@ -12,9 +12,7 @@ def get_available_name(self, name: str, max_length: int | None = None) -> str: class CV2DNNStorage(UniqueStorageMixin, FileSystemStorage): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.location = settings.CV2DNN_PATH + pass class HDEAzureStorage(UniqueStorageMixin, AzureStorage): diff --git a/src/hope_dedup_engine/apps/faces/utils/duplication_detector.py b/src/hope_dedup_engine/apps/faces/utils/duplication_detector.py index 02861efb..0312fb99 100644 --- a/src/hope_dedup_engine/apps/faces/utils/duplication_detector.py +++ b/src/hope_dedup_engine/apps/faces/utils/duplication_detector.py @@ -18,7 +18,7 @@ def __init__(self, filename: str) -> None: self.storages = { "images": HOPEAzureStorage(), - "cv2dnn": CV2DNNStorage(), + "cv2dnn": CV2DNNStorage(settings.CV2DNN_PATH), "encoded": HDEAzureStorage(), } diff --git a/tests/api/const.py b/tests/api/api_const.py similarity index 82% rename from tests/api/const.py rename to tests/api/api_const.py index a85f9896..a191afe2 100644 --- a/tests/api/const.py +++ b/tests/api/api_const.py @@ -1,4 +1,4 @@ -from hope_dedup_engine.apps.api.const import BULK_IMAGE_LIST, DEDUPLICATION_SET_LIST, IMAGE_LIST +from hope_dedup_engine.apps.api.const import BULK_IMAGE_LIST, DEDUPLICATION_SET_LIST, DUPLICATE_LIST, IMAGE_LIST JSON = "json" DEDUPLICATION_SET_LIST_VIEW = f"{DEDUPLICATION_SET_LIST}-list" @@ -8,3 +8,4 @@ IMAGE_DETAIL_VIEW = f"{IMAGE_LIST}-detail" BULK_IMAGE_LIST_VIEW = f"{BULK_IMAGE_LIST}-list" BULK_IMAGE_CLEAR_VIEW = f"{BULK_IMAGE_LIST}-clear" +DUPLICATE_LIST_VIEW = f"{DUPLICATE_LIST}-list" diff --git a/tests/api/conftest.py b/tests/api/conftest.py index 2ae5891f..c9fafa5b 100644 --- a/tests/api/conftest.py +++ b/tests/api/conftest.py @@ -5,7 +5,7 @@ from pytest_factoryboy import LazyFixture, register from pytest_mock import MockerFixture from rest_framework.test import APIClient -from testutils.factories.api import DeduplicationSetFactory, ImageFactory, TokenFactory +from testutils.factories.api import DeduplicationSetFactory, DuplicateFactory, ImageFactory, TokenFactory from testutils.factories.user import ExternalSystemFactory, UserFactory from hope_dedup_engine.apps.api.models import HDEToken @@ -15,6 +15,7 @@ register(UserFactory) register(DeduplicationSetFactory, external_system=LazyFixture("external_system")) register(ImageFactory, deduplication_Set=LazyFixture("deduplication_set")) +register(DuplicateFactory, deduplication_set=LazyFixture("deduplication_set")) @fixture diff --git a/tests/api/test_auth.py b/tests/api/test_auth.py index ea6f42d7..7e1ecc43 100644 --- a/tests/api/test_auth.py +++ b/tests/api/test_auth.py @@ -2,8 +2,7 @@ from typing import Any from uuid import uuid4 -from conftest import get_auth_headers -from const import ( +from api_const import ( BULK_IMAGE_CLEAR_VIEW, BULK_IMAGE_LIST_VIEW, DEDUPLICATION_SET_DETAIL_VIEW, @@ -12,6 +11,7 @@ IMAGE_LIST_VIEW, JSON, ) +from conftest import get_auth_headers from pytest import mark from rest_framework import status from rest_framework.reverse import reverse diff --git a/tests/api/test_business_logic.py b/tests/api/test_business_logic.py index 1833c1f3..7f83ee37 100644 --- a/tests/api/test_business_logic.py +++ b/tests/api/test_business_logic.py @@ -1,6 +1,6 @@ from unittest.mock import MagicMock -from const import ( +from api_const import ( DEDUPLICATION_SET_DETAIL_VIEW, DEDUPLICATION_SET_LIST_VIEW, DEDUPLICATION_SET_PROCESS_VIEW, @@ -15,7 +15,7 @@ from testutils.factories.api import DeduplicationSetFactory, ImageFactory from hope_dedup_engine.apps.api.models import DeduplicationSet -from hope_dedup_engine.apps.api.models.deduplication import Image +from hope_dedup_engine.apps.api.models.deduplication import Duplicate, Image from hope_dedup_engine.apps.api.serializers import DeduplicationSetSerializer, ImageSerializer @@ -40,6 +40,15 @@ def test_deduplication_set_processing_trigger( start_processing.assert_called_once_with(deduplication_set) +def test_duplicates_are_removed_before_processing( + api_client: APIClient, deduplication_set: DeduplicationSet, duplicate: Duplicate +) -> None: + assert Duplicate.objects.count() + response = api_client.post(reverse(DEDUPLICATION_SET_PROCESS_VIEW, (deduplication_set.pk,))) + assert response.status_code == status.HTTP_200_OK + assert not Duplicate.objects.count() + + def test_new_image_makes_deduplication_set_state_dirty( api_client: APIClient, deduplication_set: DeduplicationSet ) -> None: diff --git a/tests/api/test_deduplication_set_create.py b/tests/api/test_deduplication_set_create.py index 10430bf6..1dd2d71e 100644 --- a/tests/api/test_deduplication_set_create.py +++ b/tests/api/test_deduplication_set_create.py @@ -1,6 +1,6 @@ from typing import Any -from const import DEDUPLICATION_SET_LIST_VIEW, JSON +from api_const import DEDUPLICATION_SET_LIST_VIEW, JSON from pytest import mark from rest_framework import status from rest_framework.reverse import reverse diff --git a/tests/api/test_deduplication_set_delete.py b/tests/api/test_deduplication_set_delete.py index 0a0e5bf3..1c81fbab 100644 --- a/tests/api/test_deduplication_set_delete.py +++ b/tests/api/test_deduplication_set_delete.py @@ -1,6 +1,6 @@ from unittest.mock import MagicMock -from const import DEDUPLICATION_SET_DETAIL_VIEW +from api_const import DEDUPLICATION_SET_DETAIL_VIEW from rest_framework import status from rest_framework.reverse import reverse from rest_framework.test import APIClient diff --git a/tests/api/test_deduplication_set_list.py b/tests/api/test_deduplication_set_list.py index 2260e07a..09481bf6 100644 --- a/tests/api/test_deduplication_set_list.py +++ b/tests/api/test_deduplication_set_list.py @@ -1,4 +1,4 @@ -from const import DEDUPLICATION_SET_LIST_VIEW +from api_const import DEDUPLICATION_SET_LIST_VIEW from rest_framework import status from rest_framework.reverse import reverse from rest_framework.test import APIClient @@ -16,7 +16,6 @@ def test_can_list_deduplication_sets(api_client: APIClient, deduplication_set: D def test_cannot_list_deduplication_sets_between_systems( another_system_api_client: APIClient, deduplication_set: DeduplicationSet ) -> None: - assert DeduplicationSet.objects.count() response = another_system_api_client.get(reverse(DEDUPLICATION_SET_LIST_VIEW)) assert response.status_code == status.HTTP_200_OK data = response.json() diff --git a/tests/api/test_image_bulk_create.py b/tests/api/test_image_bulk_create.py index 46b4ca5e..6b91dd8a 100644 --- a/tests/api/test_image_bulk_create.py +++ b/tests/api/test_image_bulk_create.py @@ -1,4 +1,4 @@ -from const import BULK_IMAGE_LIST_VIEW, JSON +from api_const import BULK_IMAGE_LIST_VIEW, JSON from rest_framework import status from rest_framework.reverse import reverse from rest_framework.test import APIClient diff --git a/tests/api/test_image_bulk_delete.py b/tests/api/test_image_bulk_delete.py index dcce98af..a1898483 100644 --- a/tests/api/test_image_bulk_delete.py +++ b/tests/api/test_image_bulk_delete.py @@ -1,4 +1,4 @@ -from const import BULK_IMAGE_CLEAR_VIEW +from api_const import BULK_IMAGE_CLEAR_VIEW from rest_framework import status from rest_framework.reverse import reverse from rest_framework.test import APIClient diff --git a/tests/api/test_image_create.py b/tests/api/test_image_create.py index 89c01ad4..fe87ca16 100644 --- a/tests/api/test_image_create.py +++ b/tests/api/test_image_create.py @@ -1,4 +1,4 @@ -from const import IMAGE_LIST_VIEW, JSON +from api_const import IMAGE_LIST_VIEW, JSON from pytest import mark from rest_framework import status from rest_framework.reverse import reverse diff --git a/tests/api/test_image_delete.py b/tests/api/test_image_delete.py index 99815759..e0745847 100644 --- a/tests/api/test_image_delete.py +++ b/tests/api/test_image_delete.py @@ -1,4 +1,4 @@ -from const import IMAGE_DETAIL_VIEW +from api_const import IMAGE_DETAIL_VIEW from rest_framework import status from rest_framework.reverse import reverse from rest_framework.test import APIClient diff --git a/tests/api/test_image_list.py b/tests/api/test_image_list.py index 3e780f30..e02e5f54 100644 --- a/tests/api/test_image_list.py +++ b/tests/api/test_image_list.py @@ -1,4 +1,4 @@ -from api.const import IMAGE_LIST_VIEW +from api_const import IMAGE_LIST_VIEW from rest_framework import status from rest_framework.reverse import reverse from rest_framework.test import APIClient diff --git a/tests/extras/testutils/factories/api.py b/tests/extras/testutils/factories/api.py index b38b1301..ba2e039e 100644 --- a/tests/extras/testutils/factories/api.py +++ b/tests/extras/testutils/factories/api.py @@ -3,7 +3,7 @@ from testutils.factories import ExternalSystemFactory from hope_dedup_engine.apps.api.models import DeduplicationSet, HDEToken -from hope_dedup_engine.apps.api.models.deduplication import Image +from hope_dedup_engine.apps.api.models.deduplication import Duplicate, Image class TokenFactory(DjangoModelFactory): @@ -22,8 +22,20 @@ class Meta: class ImageFactory(DjangoModelFactory): - filename = fuzzy.FuzzyText() deduplication_set = SubFactory(DeduplicationSetFactory) + filename = fuzzy.FuzzyText() + reference_pk = fuzzy.FuzzyInteger(low=1) class Meta: model = Image + + +class DuplicateFactory(DjangoModelFactory): + deduplication_set = SubFactory(DeduplicationSetFactory) + first_filename = fuzzy.FuzzyText() + first_reference_pk = fuzzy.FuzzyInteger(low=1) + second_filename = fuzzy.FuzzyText() + second_reference_pk = fuzzy.FuzzyInteger(low=1) + + class Meta: + model = Duplicate diff --git a/tests/faces/const.py b/tests/faces/faces_const.py similarity index 100% rename from tests/faces/const.py rename to tests/faces/faces_const.py diff --git a/tests/faces/fixtures/duplication_detector.py b/tests/faces/fixtures/duplication_detector.py index fb357ab2..46a6f39a 100644 --- a/tests/faces/fixtures/duplication_detector.py +++ b/tests/faces/fixtures/duplication_detector.py @@ -8,7 +8,7 @@ from hope_dedup_engine.apps.core.storage import CV2DNNStorage, HDEAzureStorage, HOPEAzureStorage from hope_dedup_engine.apps.faces.utils.duplication_detector import DuplicationDetector -from ..const import FILENAME +from ..faces_const import FILENAME @pytest.fixture(scope="module", autouse=True) diff --git a/tests/faces/test_duplication_detector.py b/tests/faces/test_duplication_detector.py index a0d5df5f..48c49d29 100644 --- a/tests/faces/test_duplication_detector.py +++ b/tests/faces/test_duplication_detector.py @@ -6,7 +6,7 @@ import cv2 import numpy as np import pytest -from const import FILENAME, FILENAMES +from faces_const import FILENAME, FILENAMES from hope_dedup_engine.apps.faces.utils.duplication_detector import DuplicationDetector @@ -29,7 +29,7 @@ def test_duplication_detector_initialization(dd): def test_missing_files_in_storage(dd, mock_cv2dnn_storage): with patch( - "hope_dedup_engine.apps.faces.utils.duplication_detector.CV2DNNStorage", new=lambda: mock_cv2dnn_storage + "hope_dedup_engine.apps.faces.utils.duplication_detector.CV2DNNStorage", new=lambda _: mock_cv2dnn_storage ): mock_cv2dnn_storage.exists.return_value = False with pytest.raises(FileNotFoundError): diff --git a/tests/faces/test_tasks.py b/tests/faces/test_tasks.py index 2dd833fa..0851809b 100644 --- a/tests/faces/test_tasks.py +++ b/tests/faces/test_tasks.py @@ -1,6 +1,6 @@ from unittest.mock import patch -from const import FILENAME, FILENAMES +from faces_const import FILENAME, FILENAMES from hope_dedup_engine.apps.faces.celery_tasks import deduplicate from hope_dedup_engine.apps.faces.models import TaskModel