Skip to content

Commit

Permalink
feat(Activités des structures): Adaptation de la recherche des Siaes (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastienReuiller authored Nov 20, 2024
1 parent f737249 commit b3a57e8
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 65 deletions.
29 changes: 16 additions & 13 deletions lemarche/www/siaes/forms.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from django import forms
from django.contrib.gis.db.models.functions import Distance
from django.db.models import BooleanField, Case, Q, Value, When
from django.db.models import BooleanField, Case, OuterRef, Q, Subquery, Value, When

from lemarche.favorites.models import FavoriteList
from lemarche.labels.models import Label
from lemarche.networks.models import Network
from lemarche.perimeters.models import Perimeter
from lemarche.sectors.models import Sector
from lemarche.siaes import constants as siae_constants
from lemarche.siaes.models import Siae, SiaeClientReference, SiaeGroup
from lemarche.siaes.models import Siae, SiaeActivity, SiaeClientReference, SiaeGroup
from lemarche.tenders.models import Tender
from lemarche.utils.apis import api_elasticsearch
from lemarche.utils.fields import GroupedModelMultipleChoiceField
Expand Down Expand Up @@ -230,21 +230,24 @@ def filter_queryset(self, qs=None): # noqa C901
if not hasattr(self, "cleaned_data"):
self.full_clean()

sectors = self.cleaned_data.get("sectors", None)
if sectors:
qs = qs.filter_sectors(sectors)

perimeters = self.cleaned_data.get("perimeters", None)
if perimeters:
qs = qs.geo_range_in_perimeter_list(perimeters)

kinds = self.cleaned_data.get("kind", None)
if kinds:
qs = qs.filter(kind__in=kinds)

presta_types = self.cleaned_data.get("presta_type", None)
if presta_types:
qs = qs.filter(presta_type__overlap=presta_types)
# Create a very nice subquery to filter SiaeActivity by presta_type, sector and perimeter
siae_activity_subquery = SiaeActivity.objects.filter(siae=OuterRef("pk")).values("pk")

if sectors := self.cleaned_data.get("sectors", None):
siae_activity_subquery = siae_activity_subquery.filter_sectors(sectors)

if perimeters := self.cleaned_data.get("perimeters", None):
siae_activity_subquery = siae_activity_subquery.geo_range_in_perimeter_list(perimeters)

if presta_types := self.cleaned_data.get("presta_type", None):
siae_activity_subquery = siae_activity_subquery.filter(presta_type__overlap=presta_types)

if sectors or perimeters or presta_types:
qs = qs.filter(Q(activities__in=Subquery(siae_activity_subquery)))

territory = self.cleaned_data.get("territory", None)
if territory:
Expand Down
144 changes: 92 additions & 52 deletions lemarche/www/siaes/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from lemarche.perimeters.models import Perimeter
from lemarche.sectors.factories import SectorFactory
from lemarche.siaes import constants as siae_constants
from lemarche.siaes.factories import SiaeClientReferenceFactory, SiaeFactory, SiaeOfferFactory
from lemarche.siaes.factories import SiaeActivityFactory, SiaeClientReferenceFactory, SiaeFactory, SiaeOfferFactory
from lemarche.siaes.models import Siae
from lemarche.users.factories import UserFactory
from lemarche.www.siaes.forms import SiaeFilterForm
Expand Down Expand Up @@ -108,8 +108,12 @@ def test_search_kind_multiple_should_filter(self):
class SiaePrestaTypeSearchFilterTest(TestCase):
@classmethod
def setUpTestData(cls):
SiaeFactory(presta_type=[siae_constants.PRESTA_DISP])
SiaeFactory(presta_type=[siae_constants.PRESTA_DISP, siae_constants.PRESTA_BUILD])
siae_1 = SiaeFactory()
SiaeActivityFactory(siae=siae_1, presta_type=[siae_constants.PRESTA_DISP])

siae_2 = SiaeFactory()
SiaeActivityFactory(siae=siae_2, presta_type=[siae_constants.PRESTA_DISP, siae_constants.PRESTA_BUILD])

cls.user = UserFactory()

def setUp(self):
Expand Down Expand Up @@ -190,9 +194,22 @@ def setUpTestData(cls):
cls.sector_1 = SectorFactory()
cls.sector_2 = SectorFactory()
cls.sector_3 = SectorFactory()
siae_with_one_sector.sectors.add(cls.sector_1)
siae_with_two_sectors.sectors.add(cls.sector_1, cls.sector_2)
siae_with_other_sector.sectors.add(cls.sector_3)
siae_with_one_sector_activity = SiaeActivityFactory(siae=siae_with_one_sector, sector_group=cls.sector_1.group)
siae_with_one_sector_activity.sectors.add(cls.sector_1)

siae_with_two_sectors_activity_1 = SiaeActivityFactory(
siae=siae_with_two_sectors, sector_group=cls.sector_1.group
)
siae_with_two_sectors_activity_1.sectors.add(cls.sector_1)
siae_with_two_sectors_activity_2 = SiaeActivityFactory(
siae=siae_with_two_sectors, sector_group=cls.sector_2.group
)
siae_with_two_sectors_activity_2.sectors.add(cls.sector_2)

siae_with_other_sector_activity = SiaeActivityFactory(
siae=siae_with_other_sector, sector_group=cls.sector_3.group
)
siae_with_other_sector_activity.sectors.add(cls.sector_3)

def test_search_sector_empty(self):
url = reverse("siae:search_results")
Expand Down Expand Up @@ -315,106 +332,119 @@ def setUpTestData(cls):
coords=Point(47.9911, -4.1126),
)
# create the Siaes
SiaeFactory(
siae_1 = SiaeFactory(
city=cls.grenoble_perimeter.name,
department=cls.grenoble_perimeter.department_code,
region=cls.auvergne_rhone_alpes_perimeter.name,
post_code=cls.grenoble_perimeter.post_codes[0],
geo_range=siae_constants.GEO_RANGE_COUNTRY,
)
SiaeFactory(
SiaeActivityFactory(siae=siae_1, with_country_perimeter=True)

siae_2 = SiaeFactory(
city=cls.grenoble_perimeter.name,
department=cls.grenoble_perimeter.department_code,
region=cls.auvergne_rhone_alpes_perimeter.name,
post_code=cls.grenoble_perimeter.post_codes[0],
geo_range=siae_constants.GEO_RANGE_REGION,
)
SiaeFactory(
siae_2_activity = SiaeActivityFactory(siae=siae_2, with_zones_perimeter=True)
siae_2_activity.locations.add(cls.auvergne_rhone_alpes_perimeter)

siae_3 = SiaeFactory(
city=cls.grenoble_perimeter.name,
department=cls.grenoble_perimeter.department_code,
region=cls.auvergne_rhone_alpes_perimeter.name,
post_code=cls.grenoble_perimeter.post_codes[1],
geo_range=siae_constants.GEO_RANGE_DEPARTMENT,
)
SiaeFactory(
siae_3_activity = SiaeActivityFactory(siae=siae_3, with_zones_perimeter=True)
siae_3_activity.locations.add(cls.isere_perimeter)

siae_4 = SiaeFactory(
city=cls.grenoble_perimeter.name,
department=cls.grenoble_perimeter.department_code,
region=cls.auvergne_rhone_alpes_perimeter.name,
post_code=cls.grenoble_perimeter.post_codes[2],
geo_range=siae_constants.GEO_RANGE_CUSTOM,
geo_range_custom_distance=10,
coords=Point(5.7301, 45.1825),
)
SiaeActivityFactory(siae=siae_4, geo_range=siae_constants.GEO_RANGE_CUSTOM, geo_range_custom_distance=10)

# La Tronche is a city located just next to Grenoble
SiaeFactory(
siae_5 = SiaeFactory(
city="La Tronche",
department="38",
region=cls.auvergne_rhone_alpes_perimeter.name,
geo_range=siae_constants.GEO_RANGE_CUSTOM,
geo_range_custom_distance=10,
coords=Point(5.746, 45.2124),
)
SiaeActivityFactory(siae=siae_5, geo_range=siae_constants.GEO_RANGE_CUSTOM, geo_range_custom_distance=10)

# Chamrousse is a city located further away from Grenoble
SiaeFactory(
siae_6 = SiaeFactory(
city=cls.chamrousse_perimeter.name,
department="38",
region=cls.auvergne_rhone_alpes_perimeter.name,
geo_range=siae_constants.GEO_RANGE_CUSTOM,
geo_range_custom_distance=5,
coords=Point(5.8862, 45.1106),
)
SiaeFactory(
SiaeActivityFactory(siae=siae_6, geo_range=siae_constants.GEO_RANGE_CUSTOM, geo_range_custom_distance=5)

siae_7 = SiaeFactory(
city="Lyon",
department="69",
region=cls.auvergne_rhone_alpes_perimeter.name,
geo_range=siae_constants.GEO_RANGE_COUNTRY,
)
SiaeFactory(
SiaeActivityFactory(siae=siae_7, with_country_perimeter=True)

siae_8 = SiaeFactory(
city="Lyon",
department="69",
region=cls.auvergne_rhone_alpes_perimeter.name,
geo_range=siae_constants.GEO_RANGE_REGION,
)
SiaeFactory(
siae_8_activity = SiaeActivityFactory(siae=siae_8, with_zones_perimeter=True)
siae_8_activity.locations.add(cls.auvergne_rhone_alpes_perimeter)
siae_9 = SiaeFactory(
city="Lyon",
department="69",
region=cls.auvergne_rhone_alpes_perimeter.name,
geo_range=siae_constants.GEO_RANGE_DEPARTMENT,
)
SiaeFactory(
siae_9_activity = SiaeActivityFactory(siae=siae_9, with_zones_perimeter=True)
siae_9_activity.locations.add(cls.isere_perimeter)

siae_10 = SiaeFactory(
city="Lyon",
department="69",
region=cls.auvergne_rhone_alpes_perimeter.name,
geo_range=siae_constants.GEO_RANGE_CUSTOM,
geo_range_custom_distance=50,
coords=Point(4.8236, 45.7685),
)
SiaeFactory(
SiaeActivityFactory(siae=siae_10, geo_range=siae_constants.GEO_RANGE_CUSTOM, geo_range_custom_distance=50)

siae_11 = SiaeFactory(
city=cls.quimper_perimeter.name,
department=cls.quimper_perimeter.department_code,
region=cls.bretagne_perimeter.name,
geo_range=siae_constants.GEO_RANGE_COUNTRY,
)
SiaeFactory(
SiaeActivityFactory(siae=siae_11, with_country_perimeter=True)

siae_12 = SiaeFactory(
city=cls.quimper_perimeter.name,
department=cls.quimper_perimeter.department_code,
region=cls.bretagne_perimeter.name,
geo_range=siae_constants.GEO_RANGE_REGION,
)
SiaeFactory(
siae_12_activity = SiaeActivityFactory(siae=siae_12, with_zones_perimeter=True)
siae_12_activity.locations.add(cls.bretagne_perimeter)

siae_13 = SiaeFactory(
city=cls.quimper_perimeter.name,
department=cls.quimper_perimeter.department_code,
region=cls.bretagne_perimeter.name,
geo_range=siae_constants.GEO_RANGE_DEPARTMENT,
)
SiaeFactory(
siae_13_activity = SiaeActivityFactory(siae=siae_13, with_zones_perimeter=True)
siae_13_activity.locations.add(cls.finister_perimeter)

siae_14 = SiaeFactory(
city=cls.quimper_perimeter.name,
department=cls.quimper_perimeter.department_code,
region=cls.bretagne_perimeter.name,
geo_range=siae_constants.GEO_RANGE_CUSTOM,
geo_range_custom_distance=50,
coords=Point(47.9914, -4.0916),
)
SiaeActivityFactory(siae=siae_14, geo_range=siae_constants.GEO_RANGE_CUSTOM, geo_range_custom_distance=50)

def test_object_count(self):
self.assertEqual(Perimeter.objects.count(), 7)
Expand All @@ -441,52 +471,60 @@ def test_search_perimeter_region(self):
def test_search_perimeter_department(self):
form = SiaeFilterForm(data={"perimeters": [self.isere_perimeter]})
qs = form.filter_queryset()
self.assertEqual(qs.count(), 6)
self.assertEqual(qs.count(), 8)

def test_search_perimeter_city(self):
"""
We should return:
- all the Siae exactly in the city - Grenoble (4 Siae)
+ all the Siae in the city's department (except GEO_RANGE_CUSTOM) - Isere (0 new Siae)
+ all the Siae with geo_range=GEO_RANGE_CUSTOM + coords in the geo_range_custom_distance range of Grenoble (1 new Siae: La Tronche. Chamrousse is outside) # noqa
+ all the Siae with activities in the region Auvergne-Rhône-Alpes (1 new Siae)
+ all the Siae with activities in the department Isere (1 new Siae)
"""
form = SiaeFilterForm(data={"perimeters": [self.grenoble_perimeter.slug]})
self.assertTrue(form.is_valid())
qs = form.filter_queryset()
self.assertEqual(qs.count(), 4 + 0 + 1)
self.assertEqual(qs.count(), 4 + 0 + 1 + 1 + 1)

def test_search_perimeter_city_2(self):
"""
We should return:
- all the Siae exactly in the city - Chamrousse (1 Siae)
+ all the Siae in the city's department (except GEO_RANGE_CUSTOM) - Isere (3 new Siae)
+ all the Siae with geo_range=GEO_RANGE_CUSTOM + coords in the geo_range_custom_distance range of Grenoble (1 Siae, 0 new: Chamrousse. Grenoble & La Tronche are outside) # noqa
+ all the Siae with activities in the region Auvergne-Rhône-Alpes (0 new Siae, siae_8 already matched by city's department) # noqa
+ all the Siae with activities in the department Isere (1 new Siae)
"""
form = SiaeFilterForm(data={"perimeters": [self.chamrousse_perimeter]})
qs = form.filter_queryset()
self.assertEqual(qs.count(), 1 + 3 + 0)
self.assertEqual(qs.count(), 1 + 3 + 0 + 0 + 1)

def test_search_perimeter_multiperimeter_1(self):
"""
We should return:
- all the Siae exactly in these cities - Grenoble & Chamrousse (4 + 1 Siae)
+ all the Siae in the cities departments (except GEO_RANGE_CUSTOM) - Isere (0 new Siae)
+ all the Siae with geo_range=GEO_RANGE_CUSTOM + coords in the geo_range_custom_distance range of Grenoble or Chamrousse (2 Siae, 1 new) # noqa
+ all the Siae with activities in the region Auvergne-Rhône-Alpes (1 new Siae)
+ all the Siae with activities in the department Isere (1 new Siae)
"""
form = SiaeFilterForm(data={"perimeters": [self.grenoble_perimeter, self.chamrousse_perimeter]})
qs = form.filter_queryset()
self.assertEqual(qs.count(), 5 + 0 + 1)
self.assertEqual(qs.count(), 5 + 0 + 1 + 1 + 1)

def test_search_perimeter_multiperimeter_2(self):
"""
We should return:
- all the Siae exactly in these cities - Grenoble & Quimper (4 + 4)
+ all the Siae in the cities departments (except GEO_RANGE_CUSTOM) - Isere & 29 (0 new Siae)
+ all the Siae with geo_range=GEO_RANGE_CUSTOM + coords in the geo_range_custom_distance range of Grenoble or Quimper (1 new Siae) # noqa
+ all the Siae with activities in the region Auvergne-Rhône-Alpes (1 new Siae)
+ all the Siae with activities in the department Isere (0 new Siae, siae_9 already matched by city's department)
"""
form = SiaeFilterForm(data={"perimeters": [self.grenoble_perimeter, self.quimper_perimeter]})
qs = form.filter_queryset()
self.assertEqual(qs.count(), 8 + 0 + 1)
self.assertEqual(qs.count(), 8 + 0 + 1 + 1 + 0)

def test_search_perimeter_multiperimeter_error(self):
"""
Expand Down Expand Up @@ -1080,6 +1118,9 @@ def setUpTestData(cls):
SiaeFactory(name="Ma boite")
SiaeFactory(name="Une autre structure")
SiaeFactory(name="ABC Insertion")
cls.isere_perimeter = PerimeterFactory(
name="Isère", kind=Perimeter.KIND_DEPARTMENT, insee_code="38", region_code="84"
)
cls.grenoble_perimeter = PerimeterFactory(
name="Grenoble",
kind=Perimeter.KIND_CITY,
Expand Down Expand Up @@ -1137,26 +1178,25 @@ def test_should_bring_the_siae_with_offers_to_the_top(self):
self.assertEqual(siaes[0].name, "ZZ ESI offer")

def test_should_bring_the_siae_closer_to_the_city_to_the_top(self):
SiaeFactory(
siae_1 = SiaeFactory(
name="ZZ GEO Pontcharra",
department="38",
geo_range=siae_constants.GEO_RANGE_DEPARTMENT,
coords=Point(6.0271, 45.4144),
)
SiaeFactory(
siae_activity_1 = SiaeActivityFactory(siae=siae_1, with_zones_perimeter=True)
siae_activity_1.locations.add(self.isere_perimeter)
siae_2 = SiaeFactory(
name="ZZ GEO La Tronche",
department="38",
geo_range=siae_constants.GEO_RANGE_CUSTOM,
geo_range_custom_distance=10,
coords=Point(5.746, 45.2124),
)
SiaeFactory(
SiaeActivityFactory(siae=siae_2, geo_range=siae_constants.GEO_RANGE_CUSTOM, geo_range_custom_distance=10)
siae_3 = SiaeFactory(
name="ZZ GEO Grenoble",
department="38",
geo_range=siae_constants.GEO_RANGE_CUSTOM,
geo_range_custom_distance=10,
coords=Point(5.7301, 45.1825),
)
SiaeActivityFactory(siae=siae_3, geo_range=siae_constants.GEO_RANGE_CUSTOM, geo_range_custom_distance=10)
url = f"{self.url}?perimeters={self.grenoble_perimeter.slug}"
response = self.client.get(url)
siaes = list(response.context["siaes"])
Expand Down

0 comments on commit b3a57e8

Please sign in to comment.