Skip to content

Commit

Permalink
feat(api) : Use the new Commune model & change distance search
Browse files Browse the repository at this point in the history
We can now use the new Commune model and perform:

- queries for "districts" (here, some tests cover Paris 11)
- distance from the city centers, not the polygon.

For this we have crafted a slightly different Parquet file:

>>> gdf
    code         nom  departement  region  siren_epci                 codes_postaux                    centre
0  59183   Dunkerque           59      32   245900428         [59140, 59240, 59279]  POINT (2.34310 51.01690)
1  59295  Hazebrouck           59      32   200040947                       [59190]  POINT (2.53570 50.72540)
2  59350       Lille           59      32   200093201         [59000, 59160, 59260]  POINT (3.04680 50.63110)
3  59392    Maubeuge           59      32   200043396                       [59600]  POINT (3.96110 50.28340)
4  59512     Roubaix           59      32   200093201                       [59100]  POINT (3.18430 50.68870)
5  75056       Paris           75      11   200054781  [75001, 75002, 75003, 75004]  POINT (2.34700 48.85890)
6  75111   Paris 11e           75      11   200054781                       [75011]  POINT (2.38160 48.86010)

No more complex geometries, hello codes postaux and districts.
  • Loading branch information
vperron committed Sep 3, 2024
1 parent c219275 commit 7988635
Show file tree
Hide file tree
Showing 5 changed files with 20 additions and 34 deletions.
5 changes: 2 additions & 3 deletions api/src/data_inclusion/api/inclusion_data/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@
RegionCodeEnum,
RegionSlugEnum,
)
from data_inclusion.api.code_officiel_geo.models import Commune
from data_inclusion.api.code_officiel_geo.utils import (
get_departement_by_code_or_slug,
get_region_by_code_or_slug,
)
from data_inclusion.api.config import settings
from data_inclusion.api.core import db
from data_inclusion.api.inclusion_data import schemas, services
from data_inclusion.api.inclusion_data import models, schemas, services
from data_inclusion.api.utils import pagination, soliguide

router = fastapi.APIRouter(tags=["Données"])
Expand Down Expand Up @@ -411,7 +410,7 @@ def search_services_endpoint(
commune_instance = None
search_point = None
if code_commune is not None:
commune_instance = db_session.get(Commune, code_commune)
commune_instance = db_session.get(models.Commune, code_commune)
if commune_instance is None:
raise fastapi.HTTPException(
status_code=fastapi.status.HTTP_422_UNPROCESSABLE_ENTITY,
Expand Down
38 changes: 11 additions & 27 deletions api/src/data_inclusion/api/inclusion_data/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@

from data_inclusion import schema as di_schema
from data_inclusion.api.code_officiel_geo.constants import (
CODE_COMMUNE_BY_CODE_ARRONDISSEMENT,
Departement,
Region,
)
from data_inclusion.api.code_officiel_geo.models import Commune
from data_inclusion.api.inclusion_data import models

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -198,19 +196,16 @@ def list_structures(
query = query.filter_by(id=id_)

if commune_code is not None:
commune_code = CODE_COMMUNE_BY_CODE_ARRONDISSEMENT.get(
commune_code, commune_code
)
query = query.filter(models.Structure.code_insee == commune_code)

if departement is not None:
query = query.filter(models.Structure.code_insee.startswith(departement.code))

if region is not None:
query = query.join(Commune).options(
query = query.join(models.Commune).options(
orm.contains_eager(models.Structure.commune_)
)
query = query.filter(Commune.region == region.code)
query = query.filter(models.Commune.region == region.code)

if typologie is not None:
query = query.filter_by(typologie=typologie.value)
Expand Down Expand Up @@ -324,14 +319,12 @@ def list_services(
query = query.filter(models.Service.code_insee.startswith(departement.code))

if region is not None:
query = query.join(Commune).options(orm.contains_eager(models.Service.commune_))
query = query.filter(Commune.region == region.code)

if code_commune is not None:
code_commune = CODE_COMMUNE_BY_CODE_ARRONDISSEMENT.get(
code_commune, code_commune
query = query.join(models.Commune).options(
orm.contains_eager(models.Service.commune_)
)
query = query.filter(models.Commune.region == region.code)

if code_commune is not None:
query = query.filter(models.Service.code_insee == code_commune)

query = filter_services(
Expand All @@ -357,7 +350,7 @@ def search_services(
request: fastapi.Request,
db_session: orm.Session,
sources: list[str] | None = None,
commune_instance: Commune | None = None,
commune_instance: models.Commune | None = None,
thematiques: list[di_schema.Thematique] | None = None,
frais: list[di_schema.Frais] | None = None,
modes_accueil: list[di_schema.ModeAccueil] | None = None,
Expand Down Expand Up @@ -415,24 +408,15 @@ def search_services(
if search_point is not None:
dest_geometry = search_point
else:
dest_geometry = (
sqla.select(
sqla.cast(
geoalchemy2.functions.ST_Simplify(Commune.geom, 0.01),
geoalchemy2.Geography(geometry_type="GEOMETRY", srid=4326),
)
)
.filter(Commune.code == commune_instance.code)
.scalar_subquery()
)
dest_geometry = commune_instance.centre

query = query.filter(
sqla.or_(
# either `en-presentiel` within a given distance
geoalchemy2.functions.ST_DWithin(
src_geometry,
dest_geometry,
50_000, # meters or 50km
50_000, # meters
),
# or `a-distance`
models.Service.modes_accueil.contains(
Expand All @@ -454,8 +438,8 @@ def search_services(
src_geometry,
dest_geometry,
)
/ 1000
).cast(sqla.Integer), # conversion to kms
/ 1000 # conversion to kms
).cast(sqla.Integer),
),
else_=sqla.null().cast(sqla.Integer),
)
Expand Down
Binary file modified api/tests/communes.parquet.gzip
Binary file not shown.
2 changes: 1 addition & 1 deletion api/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
from fastapi.testclient import TestClient

from data_inclusion.api.app import create_app
from data_inclusion.api.code_officiel_geo import models
from data_inclusion.api.config import settings
from data_inclusion.api.core import db
from data_inclusion.api.inclusion_data import models

from . import factories

Expand Down
9 changes: 6 additions & 3 deletions api/tests/e2e/api/test_inclusion_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
LILLE = {"code_insee": "59350", "latitude": 50.633333, "longitude": 3.066667}
MAUBEUGE = {"code_insee": "59392"}
PARIS = {"code_insee": "75056", "latitude": 48.866667, "longitude": 2.333333}
PARIS_11 = {"code_insee": "75111", "latitude": 48.86010, "longitude": 2.38160}
ROUBAIX = {"code_insee": "59512"}


Expand Down Expand Up @@ -614,7 +615,8 @@ def test_can_filter_resources_by_slug_region(api_client, url, factory):
(None, DUNKERQUE["code_insee"], False),
(DUNKERQUE["code_insee"], DUNKERQUE["code_insee"], True),
(DUNKERQUE["code_insee"], "62041", False),
(PARIS["code_insee"], "75101", True),
(PARIS["code_insee"], "75056", True),
(PARIS_11["code_insee"], "75111", True),
],
)
def test_can_filter_resources_by_code_commune(
Expand Down Expand Up @@ -837,7 +839,8 @@ def test_can_filter_resources_by_sources(api_client, url, factory):
(None, DUNKERQUE["code_insee"], False),
(DUNKERQUE, DUNKERQUE["code_insee"], True),
(DUNKERQUE, MAUBEUGE["code_insee"], False),
(PARIS, "75101", True),
(PARIS, "75056", True),
(PARIS_11, "75111", True),
pytest.param(PARIS, PARIS["code_insee"], True, marks=pytest.mark.xfail),
],
)
Expand Down Expand Up @@ -1224,7 +1227,7 @@ def test_search_services_with_code_commune_sample_distance(api_client):
resp_data = response.json()
assert_paginated_response_data(resp_data, total=1)
assert resp_data["items"][0]["service"]["id"] == service_1.id
assert resp_data["items"][0]["distance"] == 35
assert resp_data["items"][0]["distance"] == 39


@pytest.mark.with_token
Expand Down

0 comments on commit 7988635

Please sign in to comment.