Skip to content

Commit

Permalink
fix(Activités des structures): ajout de la correspondance directe sur…
Browse files Browse the repository at this point in the history
… la ville, le département ou la région de la structure (#1487)
  • Loading branch information
SebastienReuiller authored Nov 12, 2024
1 parent e508900 commit 21e97c4
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 43 deletions.
9 changes: 7 additions & 2 deletions lemarche/siaes/management/commands/create_siae_activities.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,13 @@ def create_siae_activities(self, siae: Siae):
continue

case _:
self.stdout_warning(f"Unknown geo_range: {siae.geo_range}")
continue
# Create a SiaeActivity with no location to continue to match the sectors
siae_activity = SiaeActivity.objects.create(
siae=siae,
sector_group_id=sector_group_id,
presta_type=siae.presta_type,
geo_range=siae_constants.GEO_RANGE_ZONES,
)
siae_activity.sectors.set(siae.sectors.filter(group_id=sector_group_id))

self.stdout_info(f"Created {len(siae_sector_group_ids)} activities for {siae}")
66 changes: 39 additions & 27 deletions lemarche/siaes/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1500,33 +1500,45 @@ def geo_range_in_perimeter_list(self, perimeters: models.QuerySet, include_count
# Match siae activity with geo range zone and same perimeter
conditions |= Q(Q(geo_range=siae_constants.GEO_RANGE_ZONES) & Q(locations=perimeter))

if perimeter.kind == Perimeter.KIND_CITY:
# Match siae activity with geo range custom and siae city is in area
conditions |= Q(
Q(geo_range=siae_constants.GEO_RANGE_CUSTOM)
& Q(geo_range_custom_distance__gte=Distance("siae__coords", perimeter.coords) / 1000)
)

# Match the department that includes this city
conditions |= Q(
Q(geo_range=siae_constants.GEO_RANGE_ZONES)
& Q(locations__kind=Perimeter.KIND_DEPARTMENT)
& Q(locations__insee_code=perimeter.department_code)
)

# Match the region that includes this city
conditions |= Q(
Q(geo_range=siae_constants.GEO_RANGE_ZONES)
& Q(locations__kind=Perimeter.KIND_REGION)
& Q(locations__insee_code=f"R{perimeter.region_code}")
)
if perimeter.kind == Perimeter.KIND_DEPARTMENT:
# Match the region that includes this department
conditions |= Q(
Q(geo_range=siae_constants.GEO_RANGE_ZONES)
& Q(locations__kind=Perimeter.KIND_REGION)
& Q(locations__insee_code=f"R{perimeter.region_code}")
)
match perimeter.kind:
case Perimeter.KIND_CITY:
# Match siae activity with geo range custom and siae city is in area
conditions |= Q(
Q(geo_range=siae_constants.GEO_RANGE_CUSTOM)
& Q(geo_range_custom_distance__gte=Distance("siae__coords", perimeter.coords) / 1000)
)

# Match the department that includes this city
conditions |= Q(
Q(geo_range=siae_constants.GEO_RANGE_ZONES)
& Q(locations__kind=Perimeter.KIND_DEPARTMENT)
& Q(locations__insee_code=perimeter.department_code)
)

# Match the region that includes this city
conditions |= Q(
Q(geo_range=siae_constants.GEO_RANGE_ZONES)
& Q(locations__kind=Perimeter.KIND_REGION)
& Q(locations__insee_code=f"R{perimeter.region_code}")
)

# Try to match directly the siae city
conditions |= Q(siae__post_code__in=perimeter.post_codes)

case Perimeter.KIND_DEPARTMENT:
# Match the region that includes this department
conditions |= Q(
Q(geo_range=siae_constants.GEO_RANGE_ZONES)
& Q(locations__kind=Perimeter.KIND_REGION)
& Q(locations__insee_code=f"R{perimeter.region_code}")
)

# Try to match directly the siae department
conditions |= Q(siae__department=perimeter.insee_code)

case Perimeter.KIND_REGION:
# Try to match directly the siae region
conditions |= Q(siae__region=perimeter.name)

if include_country_area:
conditions = Q(geo_range=siae_constants.GEO_RANGE_COUNTRY) | conditions
Expand Down
19 changes: 18 additions & 1 deletion lemarche/siaes/tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,21 @@ def test_create_activities(self):
)
siae4.sectors.set([self.sector2, self.sector3])

# without geo_range
siae5 = SiaeFactory(
is_active=True,
kind=siae_constants.KIND_EA,
presta_type=[siae_constants.PRESTA_DISP],
department="978",
region=self.region_name,
)
siae5.sectors.set([self.sector3])

call_command("create_siae_activities", dry_run=True)
self.assertEqual(SiaeActivity.objects.count(), 0)

call_command("create_siae_activities")
self.assertEqual(SiaeActivity.objects.count(), 2 + 1 + 1 + 2)
self.assertEqual(SiaeActivity.objects.count(), 2 + 1 + 1 + 2 + 1)
siae1_activities = SiaeActivity.objects.filter(siae=siae1)
self.assertEqual(siae1_activities.count(), 2)
self.assertEqual(siae1_activities.filter(sectors__in=[self.sector1]).count(), 1)
Expand Down Expand Up @@ -136,3 +146,10 @@ def test_create_activities(self):
self.assertEqual(siae_activity.geo_range, siae_constants.GEO_RANGE_ZONES)
self.assertEqual(siae_activity.locations.count(), 1)
self.assertEqual(siae_activity.locations.first(), self.perimeter_department)

siae5_activities = SiaeActivity.objects.filter(siae=siae5)
self.assertEqual(siae5_activities.count(), 1)
self.assertEqual(siae5_activities.filter(sectors__in=[self.sector3]).count(), 1)
self.assertEqual(siae5_activities.first().presta_type, [siae_constants.PRESTA_DISP])
self.assertEqual(siae5_activities.first().geo_range, siae_constants.GEO_RANGE_ZONES)
self.assertEqual(siae5_activities.first().locations.count(), 0)
69 changes: 56 additions & 13 deletions lemarche/tenders/tests/test_matching.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from timeit import default_timer as timer

from django.contrib.gis.geos import Point
from django.test import TestCase

Expand Down Expand Up @@ -72,25 +74,41 @@ def setUpTestData(cls):
)
cls.siae_three_activity.sectors.add(cls.other_sector)

# siae with activity without locations to check if match directly city/department/region
cls.siae_four = SiaeFactory(
is_active=True,
kind=siae_constants.KIND_ESAT,
department="75",
region="Île-de-France",
post_code="75018",
)
cls.siae_four_activity = SiaeActivityFactory(
siae=cls.siae_four,
sector_group=cls.sectors[0].group,
presta_type=[siae_constants.PRESTA_PREST, siae_constants.PRESTA_BUILD],
geo_range=siae_constants.GEO_RANGE_ZONES,
)
cls.siae_four_activity.sectors.add(cls.sectors[i])

def test_matching_siae_presta_type(self):
tender = TenderFactory(presta_type=[], sectors=self.sectors, perimeters=self.perimeters)
siae_found_list = Siae.objects.filter_with_tender_through_activities(tender)
self.assertEqual(len(siae_found_list), 2)
self.assertEqual(len(siae_found_list), 3)
tender = TenderFactory(
presta_type=[siae_constants.PRESTA_BUILD], sectors=self.sectors, perimeters=self.perimeters
)
siae_found_list = Siae.objects.filter_with_tender_through_activities(tender)
self.assertEqual(len(siae_found_list), 2)
self.assertEqual(len(siae_found_list), 3)
tender = TenderFactory(
presta_type=[siae_constants.PRESTA_PREST], sectors=self.sectors, perimeters=self.perimeters
)
siae_found_list = Siae.objects.filter_with_tender_through_activities(tender)
self.assertEqual(len(siae_found_list), 1)
self.assertEqual(len(siae_found_list), 2)

def test_matching_siae_kind(self):
tender = TenderFactory(siae_kind=[], sectors=self.sectors, perimeters=self.perimeters)
siae_found_list = Siae.objects.filter_with_tender_through_activities(tender)
self.assertEqual(len(siae_found_list), 2)
self.assertEqual(len(siae_found_list), 3)
tender = TenderFactory(siae_kind=[siae_constants.KIND_AI], sectors=self.sectors, perimeters=self.perimeters)
siae_found_list = Siae.objects.filter_with_tender_through_activities(tender)
self.assertEqual(len(siae_found_list), 1)
Expand All @@ -100,15 +118,15 @@ def test_matching_siae_kind(self):
perimeters=self.perimeters,
)
siae_found_list = Siae.objects.filter_with_tender_through_activities(tender)
self.assertEqual(len(siae_found_list), 2)
self.assertEqual(len(siae_found_list), 3)
tender = TenderFactory(siae_kind=[siae_constants.KIND_SEP], sectors=self.sectors, perimeters=self.perimeters)
siae_found_list = Siae.objects.filter_with_tender_through_activities(tender)
self.assertEqual(len(siae_found_list), 0)

def test_matching_siae_sectors(self):
tender = TenderFactory(sectors=self.sectors)
siae_found_list = Siae.objects.filter_with_tender_through_activities(tender)
self.assertEqual(len(siae_found_list), 2)
self.assertEqual(len(siae_found_list), 3)

def test_matching_siae_distance_location(self):
# create SIAE in Tours
Expand Down Expand Up @@ -170,7 +188,7 @@ def test_matching_siae_distance_location(self):
tender.distance_location = None
tender.save()
siae_found_list = Siae.objects.filter_with_tender_through_activities(tender)
self.assertEqual(len(siae_found_list), 2)
self.assertEqual(len(siae_found_list), 3)
self.assertIn(self.siae_one, siae_found_list)
self.assertIn(self.siae_two, siae_found_list)

Expand Down Expand Up @@ -199,7 +217,7 @@ def test_matching_siae_distance_location(self):
perimeters=[self.perimeter_paris], # without effect too
)
siae_found_list = Siae.objects.filter_with_tender_through_activities(tender)
self.assertEqual(len(siae_found_list), 3)
self.assertEqual(len(siae_found_list), 4)
self.assertIn(self.siae_one, siae_found_list)
self.assertIn(self.siae_two, siae_found_list)
self.assertIn(siae_marseille, siae_found_list)
Expand All @@ -217,11 +235,11 @@ def test_matching_siae_perimeters_custom(self):
# tender perimeter custom with include_country_area = False
tender_1 = TenderFactory(sectors=self.sectors, perimeters=self.perimeters)
siae_found_list = Siae.objects.filter_with_tender_through_activities(tender_1)
self.assertEqual(len(siae_found_list), 2 + 0)
self.assertEqual(len(siae_found_list), 3 + 0)
# tender perimeter custom with include_country_area = True
tender_2 = TenderFactory(sectors=self.sectors, perimeters=self.perimeters, include_country_area=True)
siae_found_list = Siae.objects.filter_with_tender_through_activities(tender_2)
self.assertEqual(len(siae_found_list), 2 + 1)
self.assertEqual(len(siae_found_list), 3 + 1)

def test_matching_siae_country(self):
# add Siae with geo_range_country
Expand Down Expand Up @@ -253,7 +271,7 @@ def test_matching_siae_country(self):
# add perimeters
tender_2.perimeters.set(self.perimeters)
siae_found_list_2 = Siae.objects.filter_with_tender_through_activities(tender_2)
self.assertEqual(len(siae_found_list_2), 2 + 2)
self.assertEqual(len(siae_found_list_2), 2 + 3)
tender_2.is_country_area = True
tender_2.save()
siae_found_list_2 = Siae.objects.filter_with_tender_through_activities(tender_2)
Expand Down Expand Up @@ -285,7 +303,7 @@ def test_matching_siae_perimeters_custom_2(self):
# tender perimeter custom
tender = TenderFactory(sectors=self.sectors, perimeters=self.perimeters)
siae_found_list = Siae.objects.filter_with_tender_through_activities(tender)
self.assertEqual(len(siae_found_list), 2 + 1)
self.assertEqual(len(siae_found_list), 3 + 1)

def test_matching_siae_perimeters_france(self):
# tender france
Expand Down Expand Up @@ -357,5 +375,30 @@ def test_with_no_contact_email(self):
siae_activity.sectors.add(self.sectors[0])

siae_found_list = Siae.objects.filter_with_tender_through_activities(tender)
self.assertEqual(len(siae_found_list), 2 + 0)
self.assertEqual(len(siae_found_list), 3 + 0)
self.assertNotIn(siae, siae_found_list)

def test_performance(self):
# create 100 siaes with 10 activities each
for i in range(100):
siae = SiaeFactory(is_active=True, coords=Point(48.86385199985207, 2.337071483848432))
for j in range(10):
siae_activity = SiaeActivityFactory(
siae=siae,
sector_group=self.sectors[j % 10].group,
presta_type=[siae_constants.PRESTA_PREST, siae_constants.PRESTA_BUILD],
with_zones_perimeter=True,
)
siae_activity.locations.set([self.perimeter_paris])
siae_activity.sectors.add(self.sectors[j % 10])

tender = TenderFactory(sectors=self.sectors, perimeters=self.perimeters)

start_time = timer()
with self.assertNumQueries(6):
siae_found_list = Siae.objects.filter_with_tender_through_activities(tender)
self.assertEqual(len(siae_found_list), 100 + 3)

end_time = timer()
duration = end_time - start_time
self.assertLess(duration, 0.5, f"Performance issue: took {duration:.4f} seconds")

0 comments on commit 21e97c4

Please sign in to comment.