From 5f94db722a96be1a9880d4fc93fa8ddba4d9e12b Mon Sep 17 00:00:00 2001 From: Maxime Vergez <85738261+mvergez@users.noreply.github.com> Date: Mon, 23 Jan 2023 16:27:42 +0100 Subject: [PATCH] Feat/get all sites (#26) * test(api): test all_geometries route * feat(api): geojson instead of geobuf for sites * feat(api): add all_sites_group_geometry route To return the geometries of all sites groups * test(api): refactor fixture To add a new one: site_group_with_sites since not all sites_groups have sites * test(api): test get_sites_groups route * feat(api): add possibility to filter On id_base_site, base_site_name and id_sites_group * test(api): add fixture to get group without site --- backend/gn_module_monitoring/routes/site.py | 22 +++++++++++++- .../routes/sites_groups.py | 30 +++++++++++++++++-- .../tests/fixtures/site.py | 4 +-- .../tests/fixtures/sites_groups.py | 10 +++++++ .../tests/test_routes/test_site.py | 28 +++++++++++++++++ .../tests/test_routes/test_sites_groups.py | 15 ++++++++++ backend/gn_module_monitoring/utils/routes.py | 20 +++++++++++++ 7 files changed, 123 insertions(+), 6 deletions(-) diff --git a/backend/gn_module_monitoring/routes/site.py b/backend/gn_module_monitoring/routes/site.py index 92a209ad0..b4c8cc673 100644 --- a/backend/gn_module_monitoring/routes/site.py +++ b/backend/gn_module_monitoring/routes/site.py @@ -4,14 +4,15 @@ from gn_module_monitoring.blueprint import blueprint from gn_module_monitoring.monitoring.models import BibTypeSite, TMonitoringSites +from gn_module_monitoring.monitoring.schemas import BibTypeSiteSchema, MonitoringSitesSchema from gn_module_monitoring.utils.routes import ( filter_params, + geojson_query, get_limit_page, get_sort, paginate, sort, ) -from gn_module_monitoring.monitoring.schemas import MonitoringSitesSchema,BibTypeSiteSchema @blueprint.route("/sites/types", methods=["GET"]) @@ -60,6 +61,25 @@ def get_sites(): ) +@blueprint.route("/sites/geometries", methods=["GET"]) +def get_all_site_geometries(): + params = MultiDict(request.args) + subquery = ( + TMonitoringSites.query.with_entities( + TMonitoringSites.id_base_site, + TMonitoringSites.base_site_name, + TMonitoringSites.geom, + TMonitoringSites.id_sites_group, + ) + .filter_by_params(params) + .subquery() + ) + + result = geojson_query(subquery) + + return jsonify(result) + + @blueprint.route("/sites/module/", methods=["GET"]) def get_module_sites(module_code: str): # TODO: load with site_categories.json API diff --git a/backend/gn_module_monitoring/routes/sites_groups.py b/backend/gn_module_monitoring/routes/sites_groups.py index e999c2351..e04b27c65 100644 --- a/backend/gn_module_monitoring/routes/sites_groups.py +++ b/backend/gn_module_monitoring/routes/sites_groups.py @@ -1,16 +1,19 @@ -from flask import request +from flask import jsonify, request +from geonature.utils.env import db +from sqlalchemy import func from werkzeug.datastructures import MultiDict from gn_module_monitoring.blueprint import blueprint -from gn_module_monitoring.monitoring.models import TMonitoringSitesGroups +from gn_module_monitoring.monitoring.models import TMonitoringSites, TMonitoringSitesGroups +from gn_module_monitoring.monitoring.schemas import MonitoringSitesGroupsSchema from gn_module_monitoring.utils.routes import ( filter_params, + geojson_query, get_limit_page, get_sort, paginate, sort, ) -from gn_module_monitoring.monitoring.schemas import MonitoringSitesGroupsSchema @blueprint.route("/sites_groups", methods=["GET"]) @@ -29,3 +32,24 @@ def get_sites_groups(): limit=limit, page=page, ) + + +@blueprint.route("/sites_groups/geometries", methods=["GET"]) +def get_sites_group_geometries(): + subquery = ( + db.session.query( + TMonitoringSitesGroups.id_sites_group, + TMonitoringSitesGroups.sites_group_name, + func.st_convexHull(func.st_collect(TMonitoringSites.geom)), + ) + .group_by(TMonitoringSitesGroups.id_sites_group, TMonitoringSitesGroups.sites_group_name) + .join( + TMonitoringSites, + TMonitoringSites.id_sites_group == TMonitoringSitesGroups.id_sites_group, + ) + .subquery() + ) + + result = geojson_query(subquery) + + return jsonify(result) diff --git a/backend/gn_module_monitoring/tests/fixtures/site.py b/backend/gn_module_monitoring/tests/fixtures/site.py index e3d16d973..4125b3e16 100644 --- a/backend/gn_module_monitoring/tests/fixtures/site.py +++ b/backend/gn_module_monitoring/tests/fixtures/site.py @@ -7,7 +7,7 @@ @pytest.fixture() -def sites(users, types_site, sites_groups): +def sites(users, types_site, site_group_with_sites): user = users["user"] geom_4326 = from_shape(Point(43, 24), srid=4326) sites = {} @@ -21,7 +21,7 @@ def sites(users, types_site, sites_groups): geom=geom_4326, id_nomenclature_type_site=types_site[key].id_nomenclature_type_site, types_site=[types_site[key]], - id_sites_group=sites_groups["Site_Groupe"].id_sites_group, + id_sites_group=site_group_with_sites.id_sites_group, ) with db.session.begin_nested(): db.session.add_all(sites.values()) diff --git a/backend/gn_module_monitoring/tests/fixtures/sites_groups.py b/backend/gn_module_monitoring/tests/fixtures/sites_groups.py index bfadf071e..eb86a2128 100644 --- a/backend/gn_module_monitoring/tests/fixtures/sites_groups.py +++ b/backend/gn_module_monitoring/tests/fixtures/sites_groups.py @@ -14,3 +14,13 @@ def sites_groups(): db.session.add_all(groups.values()) return groups + + +@pytest.fixture +def site_group_with_sites(sites_groups): + return sites_groups["Site_Groupe"] + + +@pytest.fixture +def site_group_without_sites(sites_groups): + return sites_groups["Site_eolien"] diff --git a/backend/gn_module_monitoring/tests/test_routes/test_site.py b/backend/gn_module_monitoring/tests/test_routes/test_site.py index 07448b1a3..bb00e5006 100644 --- a/backend/gn_module_monitoring/tests/test_routes/test_site.py +++ b/backend/gn_module_monitoring/tests/test_routes/test_site.py @@ -57,6 +57,34 @@ def test_get_sites_id_base_site(self, sites): assert len(r.json["items"]) == 1 assert r.json["items"][0]["id_base_site"] == id_base_site + def test_get_all_site_geometries(self, sites): + r = self.client.get(url_for("monitorings.get_all_site_geometries")) + + json_resp = r.json + features = json_resp.get("features") + sites_values = list(sites.values()) + assert r.content_type == "application/json" + assert json_resp.get("type") == "FeatureCollection" + assert len(features) >= len(sites_values) + for site in sites_values: + id_ = [ + obj["properties"] + for obj in features + if obj["properties"]["base_site_name"] == site.base_site_name + ][0]["id_base_site"] + assert id_ == site.id_base_site + + def test_get_all_site_geometries_filter_site_group(self, sites, site_group_without_sites): + r = self.client.get( + url_for( + "monitorings.get_all_site_geometries", + id_sites_group=site_group_without_sites.id_sites_group, + ) + ) + json_resp = r.json + features = json_resp.get("features") + assert features is None + def test_get_module_sites(self): module_code = "TEST" r = self.client.get(url_for("monitorings.get_module_sites", module_code=module_code)) diff --git a/backend/gn_module_monitoring/tests/test_routes/test_sites_groups.py b/backend/gn_module_monitoring/tests/test_routes/test_sites_groups.py index a81abf66d..f459d22ce 100644 --- a/backend/gn_module_monitoring/tests/test_routes/test_sites_groups.py +++ b/backend/gn_module_monitoring/tests/test_routes/test_sites_groups.py @@ -39,3 +39,18 @@ def test_serialize_sites_groups(self, sites_groups, sites): ).all() schema = MonitoringSitesGroupsSchema() assert [schema.dump(site) for site in groups] + + def test_get_sites_groups_geometries(self, sites, site_group_with_sites): + r = self.client.get(url_for("monitorings.get_sites_group_geometries")) + + json_resp = r.json + features = json_resp.get("features") + assert r.content_type == "application/json" + assert json_resp.get("type") == "FeatureCollection" + assert len(features) >= 1 + id_ = [ + obj["properties"] + for obj in features + if obj["properties"]["sites_group_name"] == site_group_with_sites.sites_group_name + ][0]["id_sites_group"] + assert id_ == site_group_with_sites.id_sites_group diff --git a/backend/gn_module_monitoring/utils/routes.py b/backend/gn_module_monitoring/utils/routes.py index 473f8500b..2871d86dd 100644 --- a/backend/gn_module_monitoring/utils/routes.py +++ b/backend/gn_module_monitoring/utils/routes.py @@ -2,7 +2,10 @@ from flask import Response from flask.json import jsonify +from geonature.utils.env import DB from marshmallow import Schema +from sqlalchemy import cast, func, text +from sqlalchemy.dialects.postgresql import JSON from sqlalchemy.orm import Query from werkzeug.datastructures import MultiDict @@ -37,3 +40,20 @@ def sort(query: MonitoringQuery, sort: str, sort_dir: str) -> MonitoringQuery: if sort_dir in ["desc", "asc"]: query = query.sort(label=sort, direction=sort_dir) return query + + +def geojson_query(subquery) -> bytes: + subquery_name = "q" + subquery = subquery.alias(subquery_name) + query = DB.session.query( + func.json_build_object( + text("'type'"), + text("'FeatureCollection'"), + text("'features'"), + func.json_agg(cast(func.st_asgeojson(subquery), JSON)), + ) + ) + result = query.first() + if len(result) > 0: + return result[0] + return b""