From 8d5c5e7589587dba14e2eb37b6cb5ba8236d2baa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Odini?= Date: Thu, 13 Jun 2024 10:35:35 +0200 Subject: [PATCH] =?UTF-8?q?refactor(P=C3=A9rim=C3=A8tres):=20Bouger=20le?= =?UTF-8?q?=20filtre=20autocomplete=20de=20l'API=20vers=20le=20mod=C3=A8le?= =?UTF-8?q?=20(#1264)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lemarche/api/perimeters/filters.py | 25 +-------------- lemarche/perimeters/models.py | 30 ++++++++++++++++++ lemarche/perimeters/tests.py | 50 ++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 24 deletions(-) diff --git a/lemarche/api/perimeters/filters.py b/lemarche/api/perimeters/filters.py index b98105795..1fe473abc 100644 --- a/lemarche/api/perimeters/filters.py +++ b/lemarche/api/perimeters/filters.py @@ -1,7 +1,5 @@ import django_filters from django.conf import settings -from django.contrib.postgres.search import TrigramSimilarity -from django.db.models import Q from lemarche.perimeters.models import Perimeter @@ -30,28 +28,7 @@ class Meta: fields = ["q", "results"] def name_or_post_code_autocomplete_search(self, queryset, name, value): - if not value: - return queryset - # department code or city post_code - if value.isnumeric(): - # city post_code - if len(value) == 5: - queryset = queryset.filter(post_codes__contains=[value]) - # if we wanted to allow search on insee_code as well - # return queryset.filter(Q(insee_code=value) | Q(post_codes__contains=[value])) - # department code or beginning of city post_code - elif len(value) == 2: - queryset = queryset.filter(Q(insee_code=value) | Q(post_codes__0__startswith=value)) - # city post_code - else: - queryset = queryset.filter(post_codes__0__startswith=value) - return queryset.order_by("insee_code") - # normal name filtering - return ( - queryset.annotate(similarity=TrigramSimilarity("name", value)) - .filter(similarity__gt=0.1) - .order_by("-similarity") - ) + return queryset.name_or_post_code_autocomplete_search(value) def max_number_of_results(self, queryset, name, value): if not value: diff --git a/lemarche/perimeters/models.py b/lemarche/perimeters/models.py index 2d5805a12..21db9687a 100644 --- a/lemarche/perimeters/models.py +++ b/lemarche/perimeters/models.py @@ -3,7 +3,9 @@ from django.contrib.gis.db import models as gis_models from django.contrib.postgres.indexes import GinIndex +from django.contrib.postgres.search import TrigramSimilarity from django.db import models +from django.db.models import Q from django.template.defaultfilters import slugify from django.utils import timezone @@ -18,6 +20,34 @@ def cities(self): def regions(self): return self.filter(kind="REGION") + def name_search(self, value): + return ( + self.annotate(similarity=TrigramSimilarity("name", value)) + .filter(similarity__gt=0.1) + .order_by("-similarity") + ) + + def post_code_search(self, value): + # city post_code + if len(value) == 5: + qs = self.filter(post_codes__contains=[value]) + # if we wanted to allow search on insee_code as well + # return queryset.filter(Q(insee_code=value) | Q(post_codes__contains=[value])) + # department code or beginning of city post_code + elif len(value) == 2: + qs = self.filter(Q(insee_code=value) | Q(post_codes__0__startswith=value)) + # city post_code + else: + qs = self.filter(post_codes__0__startswith=value) + return qs.order_by("insee_code") + + def name_or_post_code_autocomplete_search(self, value): + if not value: + return self + if value.isnumeric(): + return self.post_code_search(value) + return self.name_search(value) + class Perimeter(models.Model): KIND_CITY = "CITY" diff --git a/lemarche/perimeters/tests.py b/lemarche/perimeters/tests.py index d36c2d312..04be593b4 100644 --- a/lemarche/perimeters/tests.py +++ b/lemarche/perimeters/tests.py @@ -38,3 +38,53 @@ def test_name_display_property(self): self.assertEqual(str(self.perimeter_city), "Grenoble (38)") self.assertEqual(str(self.perimeter_department), "Isère") self.assertEqual(str(self.perimeter_region), "Auvergne-Rhône-Alpes") + + +class PerimeterQuerysetTest(TestCase): + @classmethod + def setUpTestData(cls): + cls.perimeter_city = PerimeterFactory( + name="Grenoble", + kind=Perimeter.KIND_CITY, + insee_code="38185", + department_code="38", + region_code="84", + post_codes=["38000", "38100", "38700"], + # coords=Point(5.7301, 45.1825), + ) + cls.perimeter_department = PerimeterFactory( + name="Isère", kind=Perimeter.KIND_DEPARTMENT, insee_code="38", region_code="84" + ) + cls.perimeter_department_2 = PerimeterFactory( + name="Guadeloupe", kind=Perimeter.KIND_DEPARTMENT, insee_code="971", region_code="01" + ) + cls.perimeter_region = PerimeterFactory( + name="Auvergne-Rhône-Alpes", kind=Perimeter.KIND_REGION, insee_code="R84" + ) + cls.perimeter_region_2 = PerimeterFactory(name="Guadeloupe", kind=Perimeter.KIND_REGION, insee_code="R01") + + def test_cities(self): + self.assertEqual(Perimeter.objects.count(), 5) + qs = Perimeter.objects.cities() + self.assertEqual(qs.count(), 1) + self.assertEqual(qs.first(), self.perimeter_city) + + def test_regions(self): + self.assertEqual(Perimeter.objects.count(), 5) + qs = Perimeter.objects.regions() + self.assertEqual(qs.count(), 2) + self.assertEqual(qs.first(), self.perimeter_region) + + def test_name_search(self): + qs = Perimeter.objects.name_search("Grenoble") + self.assertEqual(qs.count(), 1) + self.assertEqual(qs.first(), self.perimeter_city) + qs = Perimeter.objects.name_search("guadelou") + self.assertEqual(qs.count(), 2) + + def test_post_code_search(self): + qs = Perimeter.objects.post_code_search("38000") + self.assertEqual(qs.count(), 1) + self.assertEqual(qs.first(), self.perimeter_city) + qs = Perimeter.objects.post_code_search("38") + self.assertEqual(qs.count(), 2)