Skip to content

Commit

Permalink
refactor(Structures): SiaeActivity : script pour les générer à partir…
Browse files Browse the repository at this point in the history
… des Siae (#1263)
  • Loading branch information
raphodn authored Jun 26, 2024
1 parent 224ec11 commit 876d85d
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 12 deletions.
92 changes: 92 additions & 0 deletions lemarche/siaes/management/commands/create_siae_activities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from lemarche.perimeters.models import Perimeter
from lemarche.siaes.models import Siae, SiaeActivity
from lemarche.siaes.utils import match_location_to_perimeter
from lemarche.utils.commands import BaseCommand
from lemarche.utils.data import reset_app_sql_sequences


class Command(BaseCommand):
"""
Script to generate SiaeActivities from Siae data
- create 1 SiaeActivity per Siae sector_group
- match the location on the Siae address (post_code & city)
- copy the presta_type, geo_range, geo_range_custom_distance from the Siae
Note: Will delete all existing SiaeActivities !!
Usage:
poetry run python manage.py create_siae_activities --dry-run
poetry run python manage.py create_siae_activities
"""

help = ""

def add_arguments(self, parser):
parser.add_argument("--dry-run", dest="dry_run", action="store_true", help="Dry run (no changes to the DB)")

def handle(self, *args, **options):
self.stdout_info("-" * 80)
self.stdout_info("Script to create SiaeActivities...")

# Step 1: overview
self.stdout_info("-" * 80)
siae_qs = Siae.objects.all()
siae_with_sectors_qs = siae_qs.filter(sectors__isnull=False).distinct()
siae_activity_qs = SiaeActivity.objects.all()
self.stdout_info(f"Siae count: {siae_qs.count()}")
self.stdout_info(f"Siae with sectors count: {siae_with_sectors_qs.count()}")
self.stdout_info(f"SiaeActivity count: {siae_activity_qs.count()}")

if not options["dry_run"]:
# Step 2: clear existing SiaeActivities
self.stdout_info("-" * 80)
self.stdout_info("Deleting existing SiaeActivities")
SiaeActivity.objects.all().delete()
reset_app_sql_sequences("siaes")

# Step 3: create SiaeActivities
self.stdout_info("-" * 80)
self.stdout_info("Creating SiaeActivities")
for index, siae in enumerate(siae_qs):
siae_location: Perimeter | None = match_location_to_perimeter(siae)
self.create_siae_activities(siae, siae_location=siae_location)
if (index % 500) == 0:
self.stdout_info(f"{index}...")

# Recap
self.stdout_info("-" * 80)
siae_activities_created = SiaeActivity.objects.count()
msg_success = [
"----- Create SiaeActivities -----",
f"Done! Processed {siae_with_sectors_qs.count()} Siaes with sectors (out of {siae_qs.count()} Siaes)",
f"Created {siae_activities_created} SiaeActivities",
]
self.stdout_messages_success(msg_success)

self.stdout_warning(f"No location found for {siae} (post_code empty)")
return None

def create_siae_activities(self, siae: Siae, siae_location: Perimeter = None):
"""
- sector_group / sectors: we look at the existing siae sectors, and create an activity per sector group
- presta_type: we look at the existing siae presta_types
- location / geo_range / geo_range_custom_distance: we generate a perimeter from the siae address, and take the existing geo_range info # noqa
"""
if siae.sectors.count() == 0:
self.stdout_warning(f"No sectors for {siae}")
return

siae_sector_group_ids = list(set(siae.sectors.values_list("group", flat=True)))
# For each SectorGroup, create a SiaeActivity
for sector_group_id in siae_sector_group_ids:
siae_activity = SiaeActivity.objects.create(
siae=siae,
sector_group_id=sector_group_id,
presta_type=siae.presta_type,
location=siae_location,
geo_range=siae.geo_range,
geo_range_custom_distance=siae.geo_range_custom_distance,
)
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}")
39 changes: 27 additions & 12 deletions lemarche/siaes/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@
from lemarche.utils.history import HISTORY_TYPE_CREATE, HISTORY_TYPE_UPDATE


PERIMETER_GRENOBLE = {
"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),
}


class SiaeGroupModelTest(TestCase):
@classmethod
def setUpTestData(cls):
Expand Down Expand Up @@ -516,15 +527,7 @@ def setUpTestData(cls):
cls.finistere_perimeter = PerimeterFactory(
name="Finistère", kind=Perimeter.KIND_DEPARTMENT, insee_code="29", region_code="53"
)
cls.grenoble_perimeter = 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.grenoble_perimeter = PerimeterFactory(**PERIMETER_GRENOBLE)
cls.chamrousse_perimeter = PerimeterFactory(
name="Chamrousse",
kind=Perimeter.KIND_CITY,
Expand Down Expand Up @@ -612,13 +615,25 @@ def test_siae_labels_through(self):
class SiaeUtilsTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.siae_with_siret_1 = SiaeFactory(siret="12312312312345", is_active=True)
cls.siae_with_siret_2 = SiaeFactory(siret="12312312312346", is_active=True)
cls.siae_with_siret_inactive = SiaeFactory(siret="12312312312347", is_active=False)
pass

def test_calculate_etablissement_count(self):
self.siae_with_siret_1 = SiaeFactory(siret="12312312312345", is_active=True)
self.siae_with_siret_2 = SiaeFactory(siret="12312312312346", is_active=True)
self.siae_with_siret_inactive = SiaeFactory(siret="12312312312347", is_active=False)
self.assertEqual(siae_utils.calculate_etablissement_count(self.siae_with_siret_1), 2)

def test_match_location_to_perimeter(self):
self.siae_grenoble_from_post_code = SiaeFactory(post_code="38000")
self.siae_grenoble_from_insee_code = SiaeFactory(post_code="38185")
self.grenoble_perimeter = PerimeterFactory(**PERIMETER_GRENOBLE)
self.assertEqual(
siae_utils.match_location_to_perimeter(self.siae_grenoble_from_post_code), self.grenoble_perimeter
)
self.assertEqual(
siae_utils.match_location_to_perimeter(self.siae_grenoble_from_insee_code), self.grenoble_perimeter
)


class SiaeActivitiesTest(TestCase):
@classmethod
Expand Down
30 changes: 30 additions & 0 deletions lemarche/siaes/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,37 @@
from lemarche.perimeters.models import Perimeter
from lemarche.siaes.models import Siae


def calculate_etablissement_count(siae: Siae):
if siae.siren:
return Siae.objects.filter(is_active=True, siret__startswith=siae.siren).count()
return 0


def match_location_to_perimeter(siae: Siae):
"""
Find the Siae's location based on the post_code (and city)
- first do a post_code search
- if multiple perimeters returned, try to match with the city
- if still multiple results returned, return None
"""
if siae.post_code:
location_results_from_siae_post_code = Perimeter.objects.post_code_search(
siae.post_code, include_insee_code=True
)

if not location_results_from_siae_post_code.exists():
print(f"No location found for {siae} (with post_code {siae.post_code})")
return None
elif location_results_from_siae_post_code.count() == 1:
return location_results_from_siae_post_code.first()
else:
# found multiple locations with the post_code, try to match with the city
if siae.city:
location_results_from_siae_city = Perimeter.objects.name_search(siae.city)
if location_results_from_siae_city.count():
if location_results_from_siae_post_code.first() == location_results_from_siae_post_code.first():
return location_results_from_siae_post_code.first()
else:
print(f"Multiple locations found for {siae} (with post_code {siae.post_code})")
return None

0 comments on commit 876d85d

Please sign in to comment.