diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 014835a..fd746b0 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -17,22 +17,22 @@ jobs: strategy: fail-fast: false matrix: - debian-version: [ '11', '12' ] - sqlalchemy-version: [ '1.4' ] + debian-version: ['11', '12'] + sqlalchemy-version: ['1.4'] include: - - debian-version: "11" - python-version: "3.9" - postgres-version: "13" - postgis-version: "3.2" - - debian-version: "12" - python-version: "3.11" - postgres-version: "15" - postgis-version: "3.3" + - debian-version: '11' + python-version: '3.9' + postgres-version: '13' + postgis-version: '3.2' + - debian-version: '12' + python-version: '3.11' + postgres-version: '15' + postgis-version: '3.3' name: Debian ${{ matrix.debian-version}} - SQLAlchemy ${{ matrix.sqlalchemy-version }} env: - REF_GEO_SQLALCHEMY_DATABASE_URI: "postgresql://geouser:geopasswd@127.0.0.1:5432/refgeo" + REF_GEO_SQLALCHEMY_DATABASE_URI: 'postgresql://geouser:geopasswd@127.0.0.1:5432/refgeo' services: postgres: @@ -66,7 +66,7 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - cache: "pip" + cache: 'pip' - name: Install GDAL run: | sudo apt update @@ -85,6 +85,7 @@ jobs: run: | flask db upgrade ref_geo@head -x local-srid=2154 flask db upgrade ref_geo_fr_municipalities@head + flask db upgrade ref_geo_fr_departments@head flask db upgrade ref_geo_inpn_grids_5@head flask db upgrade ref_geo_inpn_grids_20@head - name: Show database status diff --git a/CHANGELOG.md b/CHANGELOG.md index 1711783..ebd05e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ CHANGELOG ----------------------------- - Ajout des nouvelles mailles officielles de l'INPN en métropole (2x2km, 20x20km, 50x50km), utilisées par la nouvelle version du référentiel de sensibilité (#24, par @lpofredc) +- Ajout des commandes `flask ref_geo activate` et `flask ref_geo deactivate` pour activer/desactiver des zonages dans le référentiel géographique: + - par type de zonage `flask ref_geo activate --area-type COM --area-type DEP` + - par nom de zonage `flask ref_geo activate --area-name Ain --area-name Hautes-Alpes` + - par code de zonage (voir `l_areas.area_code`): `flask ref_geo activate --area-code 01` + - par géométrie : `flask ref_geo activate --area-type in-polygon 'POLYGON ((-1.653442 49.628504, -1.588898 49.628504, -1.588898 49.653849, -1.653442 49.653849, -1.653442 49.628504))'` **⚠️ Notes de version** @@ -36,6 +41,7 @@ alembic upgrade ref_geo_inpn_grids_50@head # Insertion des mailles 50x50km mét - Possibilité d'appeler la route `GET/areas` sans retourner les géométries (#22) + 1.5.1 (2024-01-29) ------------------ diff --git a/src/ref_geo/commands.py b/src/ref_geo/commands.py index 005d9c6..a3c1d8e 100644 --- a/src/ref_geo/commands.py +++ b/src/ref_geo/commands.py @@ -1,6 +1,7 @@ import click from flask.cli import with_appcontext from sqlalchemy import func, select +import sqlalchemy as sa from ref_geo.env import db from ref_geo.models import BibAreasTypes, LAreas @@ -23,3 +24,97 @@ def info(): ) for area_type, count in db.session.scalars(q).unique().all(): click.echo("\t{}: {}".format(area_type.type_name, count)) + + +def change_area_activation_status( + area_code=None, area_name=None, area_type=None, in_polygon=None, enable=True +): + """ + Change the activation status of areas in the geographical referential. + + Parameters + ---------- + area_code : list of str + List of area codes to activate or deactivate. + area_name : list of str + List of area names to activate or deactivate. + area_type : list of str + List of area types to activate or deactivate. The type codes are + checked in the `bib_areas_types` table. + in_polygon : str + WKT polygon defined in WGS84 coordinate reference system. The + areas inside the polygon will be activated or deactivated. + enable : bool + If True, the areas will be activated, otherwise they will be + deactivated. + """ + str_ = "activated" if enable else "deactivated" + if area_code: + click.echo("The following area codes will be {}: {}".format(str_, ", ".join(area_code))) + q = sa.update(LAreas).where(LAreas.area_code.in_(area_code)).values(enable=enable) + db.session.execute(q) + if area_name: + click.echo("The following area names will be {}: {}".format(str_, ", ".join(area_name))) + q = sa.update(LAreas).where(LAreas.area_name.in_(area_name)).values(enable=enable) + db.session.execute(q) + if area_type: + click.echo("The following area types will be {}: {}".format(str_, ", ".join(area_type))) + area_type_ids = db.session.scalars( + select(BibAreasTypes.id_type).where(BibAreasTypes.type_code.in_(area_type)) + ).all() + q = sa.update(LAreas).where(LAreas.id_type.in_(area_type_ids)).values(enable=enable) + db.session.execute(q) + if in_polygon: + click.echo( + "The following areas will be {} in the following polygon: {}".format(str_, in_polygon) + ) + in_polygon_cte = select( + LAreas.id_area, func.ST_Intersects(LAreas.geom_4326, func.ST_GeomFromText(in_polygon)) + ).cte("in_polygon") + q = ( + sa.update(LAreas) + .where(in_polygon_cte.c.id_area == LAreas.id_area) + .values(enable=enable) + ) + db.session.execute(q) + db.session.commit() + + +@ref_geo.command() +@click.option("--area-code", "-a", multiple=True, help="Areas' code to deactivate") +@click.option("--area-name", "-n", multiple=True, help="Areas' name to deactivate") +@click.option( + "--area-type", + "-t", + multiple=True, + help="Area type to deactivate (check `type_code` in `bib_areas_types` table)", +) +@click.option( + "--in-polygon", + "-p", + help="Indicate a polygon in which areas will be deactivated. Must be in WKT format (SRID 4326)", +) +@with_appcontext +def deactivate(area_code, area_name, area_type, in_polygon): + click.echo("RefGeo : deactivating areas...") + change_area_activation_status(area_code, area_name, area_type, in_polygon, False) + + +@ref_geo.command() +@click.option("--area-code", "-a", multiple=True, help="Areas' code to activate") +@click.option("--area-name", "-n", multiple=True, help="Areas' name to activate") +@click.option( + "--area-type", + "-t", + multiple=True, + help="Area type to activate (check `type_code` in `bib_areas_types` table)", +) +@click.option( + "--in-polygon", + "-p", + help="Indicate a polygon in which areas will be activated. Must be in WKT format (SRID 4326)", +) +@with_appcontext +def activate(area_code, area_name, area_type, in_polygon): + click.echo("RefGeo : activating areas...") + change_area_activation_status(area_code, area_name, area_type, in_polygon, True) diff --git a/src/ref_geo/tests/test_ref_geo.py b/src/ref_geo/tests/test_ref_geo.py index d54280e..733c633 100644 --- a/src/ref_geo/tests/test_ref_geo.py +++ b/src/ref_geo/tests/test_ref_geo.py @@ -3,6 +3,7 @@ from flask import url_for, current_app from flask_migrate import Migrate +from ref_geo.commands import change_area_activation_status from werkzeug.exceptions import Unauthorized, BadRequest from jsonschema import validate as validate_json from alembic.migration import MigrationContext @@ -10,7 +11,7 @@ from ref_geo.env import db from ref_geo.models import BibAreasTypes, LAreas -from sqlalchemy import select +from sqlalchemy import select, update polygon = { @@ -29,6 +30,17 @@ } CITY = "La Motte-en-Champsaur" +PARAMETER_ENABLE = [ + (dict(area_code=["50120"]), "50120"), + (dict(area_name=["Ain"]), "01"), + (dict(area_type=["COM"]), "01005"), + ( + dict( + in_polygon="POLYGON ((-1.653442 49.628504, -1.588898 49.628504, -1.588898 49.653849, -1.653442 49.653849, -1.653442 49.628504))" + ), + "50129", + ), +] def has_french_dem(): @@ -436,3 +448,27 @@ def test_get_types_by_name(self, area_commune): ) assert response.status_code == 200 assert len(response.json) > 0 + + @pytest.mark.parametrize( + "parameters,expected_area_code", + PARAMETER_ENABLE, + ) + def test_activate_areas(self, parameters, expected_area_code): + db.session.execute( + update(LAreas).where(LAreas.area_code == expected_area_code).values(enable=False) + ) + change_area_activation_status(**parameters, enable=True) + q = select(LAreas.enable).where(LAreas.area_code == expected_area_code) + assert db.session.scalar(q) == True + + @pytest.mark.parametrize( + "parameters,expected_area_code", + PARAMETER_ENABLE, + ) + def test_deactivate_areas(self, parameters, expected_area_code): + db.session.execute( + update(LAreas).where(LAreas.area_code == expected_area_code).values(enable=True) + ) + change_area_activation_status(**parameters, enable=False) + q = select(LAreas.enable).where(LAreas.area_code == expected_area_code) + assert db.session.scalar(q) == False