diff --git a/src/hope_dedup_engine/apps/api/admin.py b/src/hope_dedup_engine/apps/api/admin.py deleted file mode 100644 index 7753cfe2..00000000 --- a/src/hope_dedup_engine/apps/api/admin.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.contrib import admin - -from hope_dedup_engine.apps.api.models import ( - DeduplicationSet, - Duplicate, - HDEToken, - Image, -) - -admin.site.register(DeduplicationSet) -admin.site.register(Duplicate) -admin.site.register(HDEToken) -admin.site.register(Image) diff --git a/src/hope_dedup_engine/apps/api/admin/__init__.py b/src/hope_dedup_engine/apps/api/admin/__init__.py new file mode 100644 index 00000000..f008d63d --- /dev/null +++ b/src/hope_dedup_engine/apps/api/admin/__init__.py @@ -0,0 +1,4 @@ +from .deduplicationset import DeduplicationSetAdmin # noqa +from .duplicate import DuplicateAdmin # noqa +from .hdetoken import HDETokenAdmin # noqa +from .image import ImageAdmin # noqa diff --git a/src/hope_dedup_engine/apps/api/admin/deduplicationset.py b/src/hope_dedup_engine/apps/api/admin/deduplicationset.py new file mode 100644 index 00000000..2c933f67 --- /dev/null +++ b/src/hope_dedup_engine/apps/api/admin/deduplicationset.py @@ -0,0 +1,45 @@ +from django.contrib.admin import ModelAdmin, register + +from adminfilters.dates import DateRangeFilter +from adminfilters.filters import ChoicesFieldComboFilter, DjangoLookupFilter +from adminfilters.mixin import AdminFiltersMixin + +from hope_dedup_engine.apps.api.models import DeduplicationSet + + +@register(DeduplicationSet) +class DeduplicationSetAdmin(AdminFiltersMixin, ModelAdmin): + list_display = ( + "id", + "short_name", + "reference_pk", + "state", + "created_at", + "updated_at", + "deleted", + ) + readonly_fields = ( + "id", + "state", + "external_system", + "created_at", + "created_by", + "updated_at", + "updated_by", + "deleted", + ) + search_fields = ("name",) + list_filter = ( + ("state", ChoicesFieldComboFilter), + ("created_at", DateRangeFilter), + ("updated_at", DateRangeFilter), + DjangoLookupFilter, + ) + + def short_name(self, obj): + if obj.name: + return (obj.name[:50] + "...") if len(obj.name) > 50 else obj.name + return "-" + + def has_add_permission(self, request): + return False diff --git a/src/hope_dedup_engine/apps/api/admin/duplicate.py b/src/hope_dedup_engine/apps/api/admin/duplicate.py new file mode 100644 index 00000000..c6cfb641 --- /dev/null +++ b/src/hope_dedup_engine/apps/api/admin/duplicate.py @@ -0,0 +1,35 @@ +from django.contrib.admin import ModelAdmin, register + +from adminfilters.filters import ( + DjangoLookupFilter, + NumberFilter, + RelatedFieldComboFilter, +) +from adminfilters.mixin import AdminFiltersMixin + +from hope_dedup_engine.apps.api.models import Duplicate + + +@register(Duplicate) +class DuplicateAdmin(AdminFiltersMixin, ModelAdmin): + list_display = ( + "id", + "deduplication_set", + "score", + "first_reference_pk", + "second_reference_pk", + ) + list_filter = ( + ("deduplication_set", RelatedFieldComboFilter), + ("score", NumberFilter), + DjangoLookupFilter, + ) + + def has_add_permission(self, request): + return False + + def has_delete_permission(self, request, obj=None): + return False + + def has_change_permission(self, request, obj=None): + return False diff --git a/src/hope_dedup_engine/apps/api/admin/hdetoken.py b/src/hope_dedup_engine/apps/api/admin/hdetoken.py new file mode 100644 index 00000000..d299bd4c --- /dev/null +++ b/src/hope_dedup_engine/apps/api/admin/hdetoken.py @@ -0,0 +1,8 @@ +from django.contrib.admin import ModelAdmin, register + +from hope_dedup_engine.apps.api.models import HDEToken + + +@register(HDEToken) +class HDETokenAdmin(ModelAdmin): + pass diff --git a/src/hope_dedup_engine/apps/api/admin/image.py b/src/hope_dedup_engine/apps/api/admin/image.py new file mode 100644 index 00000000..cc9b1afa --- /dev/null +++ b/src/hope_dedup_engine/apps/api/admin/image.py @@ -0,0 +1,32 @@ +from django.contrib.admin import ModelAdmin, register + +from adminfilters.dates import DateRangeFilter +from adminfilters.filters import DjangoLookupFilter, RelatedFieldComboFilter +from adminfilters.mixin import AdminFiltersMixin + +from hope_dedup_engine.apps.api.models import Image + + +@register(Image) +class ImageAdmin(AdminFiltersMixin, ModelAdmin): + list_display = ( + "id", + "filename", + "deduplication_set", + "created_at", + ) + + list_filter = ( + ("deduplication_set", RelatedFieldComboFilter), + ("created_at", DateRangeFilter), + DjangoLookupFilter, + ) + + def has_add_permission(self, request): + return False + + def has_delete_permission(self, request, obj=None): + return False + + def has_change_permission(self, request, obj=None): + return False diff --git a/src/hope_dedup_engine/apps/api/deduplication/process.py b/src/hope_dedup_engine/apps/api/deduplication/process.py index bef9e030..5b171334 100644 --- a/src/hope_dedup_engine/apps/api/deduplication/process.py +++ b/src/hope_dedup_engine/apps/api/deduplication/process.py @@ -78,9 +78,8 @@ def find_duplicates(deduplication_set_id: str, serialized_lock: str) -> None: if lock_enabled: lock.release() - except Exception as e: + except Exception: deduplication_set.state = DeduplicationSet.State.ERROR - deduplication_set.error = str(e) deduplication_set.save() raise finally: diff --git a/src/hope_dedup_engine/apps/api/migrations/0003_remove_deduplicationset_error_and_more.py b/src/hope_dedup_engine/apps/api/migrations/0003_remove_deduplicationset_error_and_more.py new file mode 100644 index 00000000..6b466f03 --- /dev/null +++ b/src/hope_dedup_engine/apps/api/migrations/0003_remove_deduplicationset_error_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 5.0.7 on 2024-09-20 08:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0002_remove_duplicate_first_filename_and_more"), + ] + + operations = [ + migrations.RemoveField( + model_name="deduplicationset", + name="error", + ), + migrations.AddField( + model_name="deduplicationset", + name="description", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="deduplicationset", + name="name", + field=models.CharField( + blank=True, db_index=True, max_length=128, null=True, unique=True + ), + ), + ] diff --git a/src/hope_dedup_engine/apps/api/migrations/0003_remove_deduplicationset_name.py b/src/hope_dedup_engine/apps/api/migrations/0003_remove_deduplicationset_name.py deleted file mode 100644 index 72c01f68..00000000 --- a/src/hope_dedup_engine/apps/api/migrations/0003_remove_deduplicationset_name.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 5.0.6 on 2024-08-07 14:14 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("api", "0002_remove_duplicate_first_filename_and_more"), - ] - - operations = [ - migrations.RemoveField( - model_name="deduplicationset", - name="name", - ), - ] diff --git a/src/hope_dedup_engine/apps/api/models/deduplication.py b/src/hope_dedup_engine/apps/api/models/deduplication.py index eea9f626..1597cb05 100644 --- a/src/hope_dedup_engine/apps/api/models/deduplication.py +++ b/src/hope_dedup_engine/apps/api/models/deduplication.py @@ -1,4 +1,4 @@ -from typing import Any, override +from typing import Any, Final, override from uuid import uuid4 from django.conf import settings @@ -6,7 +6,10 @@ from hope_dedup_engine.apps.security.models import ExternalSystem -REFERENCE_PK_LENGTH = 100 +REFERENCE_PK_LENGTH: Final[int] = 100 +DELIMITER: Final[str] = "|" +EMPTY_PLACEHOLDER: Final[str] = "-" +MAX_STR_DISPLAY_LENGTH: Final[int] = 25 class DeduplicationSet(models.Model): @@ -24,6 +27,10 @@ class State(models.IntegerChoices): ERROR = 3, "Error" # Error occurred id = models.UUIDField(primary_key=True, default=uuid4) + name = models.CharField( + max_length=128, unique=True, null=True, blank=True, db_index=True + ) + description = models.TextField(null=True, blank=True) reference_pk = models.CharField(max_length=REFERENCE_PK_LENGTH) # source_id state = models.IntegerField( choices=State.choices, @@ -31,7 +38,6 @@ class State(models.IntegerChoices): ) 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, @@ -50,6 +56,18 @@ class State(models.IntegerChoices): updated_at = models.DateTimeField(auto_now=True) notification_url = models.CharField(max_length=255, null=True, blank=True) + def __str__(self) -> str: + def shorten(text: str, trim: int = MAX_STR_DISPLAY_LENGTH) -> str: + return ( + (text[:trim] + "...") + if text and len(text) > trim + else text or EMPTY_PLACEHOLDER + ) + + if not self.name and not self.description: + return f"ID: {self.pk}" + return f"{shorten(self.name)} {DELIMITER} {shorten(self.description)}" + class Image(models.Model): """