From b5ec746f7c6f07e42728890d6454fdde365bddd2 Mon Sep 17 00:00:00 2001
From: Jean-Pascal MILCENT <jp.milcent@cbn-alpin.fr>
Date: Thu, 2 May 2024 12:13:41 +0200
Subject: [PATCH] feat: improve municipalities search (unaccent, word part)

Resolve #531.
---
 atlas/atlasAPI.py                             |   2 +-
 .../repositories/vmCommunesRepository.py      | 149 +++++++++---------
 data/update/update_1.6.1to1.6.2.sql           |   5 +
 docs/changelog.rst                            |  15 ++
 4 files changed, 96 insertions(+), 75 deletions(-)

diff --git a/atlas/atlasAPI.py b/atlas/atlasAPI.py
index de661edce..aeb736a75 100644
--- a/atlas/atlasAPI.py
+++ b/atlas/atlasAPI.py
@@ -30,7 +30,7 @@ def searchCommuneAPI():
     session = db.session
     search = request.args.get("search", "")
     limit = request.args.get("limit", 50)
-    results = vmCommunesRepository.getCommunesSearch(session, search, limit)
+    results = vmCommunesRepository.searchMunicipalities(session, search, limit)
     session.close()
     return jsonify(results)
 
diff --git a/atlas/modeles/repositories/vmCommunesRepository.py b/atlas/modeles/repositories/vmCommunesRepository.py
index bd5c8dcf6..b1d781e3b 100644
--- a/atlas/modeles/repositories/vmCommunesRepository.py
+++ b/atlas/modeles/repositories/vmCommunesRepository.py
@@ -1,74 +1,75 @@
-# -*- coding:utf-8 -*-
-
-import ast
-
-from flask import current_app
-from sqlalchemy import distinct
-from sqlalchemy.sql import text
-from sqlalchemy.sql.expression import func
-
-from atlas.modeles.entities.vmCommunes import VmCommunes
-
-
-def getAllCommunes(session):
-    req = session.query(distinct(VmCommunes.commune_maj), VmCommunes.insee).all()
-    communeList = list()
-    for r in req:
-        temp = {"label": r[0], "value": r[1]}
-        communeList.append(temp)
-    return communeList
-
-
-def getCommunesSearch(session, search, limit=50):
-    req = session.query(
-        distinct(VmCommunes.commune_maj), VmCommunes.insee, func.length(VmCommunes.commune_maj)
-    ).filter(VmCommunes.commune_maj.ilike("%" + search + "%"))
-
-    req = req.order_by(VmCommunes.commune_maj)
-
-    req = req.limit(limit).all()
-
-    communeList = list()
-    for r in req:
-        temp = {"label": r[0], "value": r[1]}
-        communeList.append(temp)
-    return communeList
-
-
-def getCommuneFromInsee(connection, insee):
-    sql = """
-        SELECT c.commune_maj,
-           c.insee,
-           c.commune_geojson
-        FROM atlas.vm_communes c
-        WHERE c.insee = :thisInsee
-    """
-    req = connection.execute(text(sql), thisInsee=insee)
-    communeObj = dict()
-    for r in req:
-        communeObj = {
-            "areaName": r.commune_maj,
-            "areaCode": str(r.insee),
-            "areaGeoJson": ast.literal_eval(r.commune_geojson),
-        }
-    return communeObj
-
-
-def getCommunesObservationsChilds(connection, cd_ref):
-    sql = """
-        SELECT DISTINCT (com.insee) AS insee, com.commune_maj
-        FROM atlas.vm_communes com
-        JOIN atlas.vm_observations obs
-        ON obs.insee = com.insee
-        WHERE obs.cd_ref IN (
-                SELECT * FROM atlas.find_all_taxons_childs(:thiscdref)
-            )
-            OR obs.cd_ref = :thiscdref
-        ORDER BY com.commune_maj ASC
-    """
-    req = connection.execute(text(sql), thiscdref=cd_ref)
-    listCommunes = list()
-    for r in req:
-        temp = {"insee": r.insee, "commune_maj": r.commune_maj}
-        listCommunes.append(temp)
-    return listCommunes
+# -*- coding:utf-8 -*-
+
+import ast
+
+from sqlalchemy import distinct
+from sqlalchemy.sql import text
+from sqlalchemy.sql.expression import func
+
+from atlas.modeles.entities.vmCommunes import VmCommunes
+
+
+def getAllCommunes(session):
+    req = session.query(distinct(VmCommunes.commune_maj), VmCommunes.insee).all()
+    communeList = list()
+    for r in req:
+        temp = {"label": r[0], "value": r[1]}
+        communeList.append(temp)
+    return communeList
+
+
+def searchMunicipalities(session, search, limit=50):
+    like_search = "%" + search.replace(" ", "%") + "%"
+
+    query = (
+        session.query(
+            distinct(VmCommunes.commune_maj),
+            VmCommunes.insee,
+            func.length(VmCommunes.commune_maj),
+        )
+        .filter(func.unaccent(VmCommunes.commune_maj).ilike(func.unaccent(like_search)))
+        .order_by(VmCommunes.commune_maj)
+        .limit(limit)
+    )
+    results = query.all()
+
+    return [{"label": r[0], "value": r[1]} for r in results]
+
+
+def getCommuneFromInsee(connection, insee):
+    sql = """
+        SELECT c.commune_maj,
+           c.insee,
+           c.commune_geojson
+        FROM atlas.vm_communes c
+        WHERE c.insee = :thisInsee
+    """
+    req = connection.execute(text(sql), thisInsee=insee)
+    communeObj = dict()
+    for r in req:
+        communeObj = {
+            "areaName": r.commune_maj,
+            "areaCode": str(r.insee),
+            "areaGeoJson": ast.literal_eval(r.commune_geojson),
+        }
+    return communeObj
+
+
+def getCommunesObservationsChilds(connection, cd_ref):
+    sql = """
+        SELECT DISTINCT (com.insee) AS insee, com.commune_maj
+        FROM atlas.vm_communes com
+        JOIN atlas.vm_observations obs
+        ON obs.insee = com.insee
+        WHERE obs.cd_ref IN (
+                SELECT * FROM atlas.find_all_taxons_childs(:thiscdref)
+            )
+            OR obs.cd_ref = :thiscdref
+        ORDER BY com.commune_maj ASC
+    """
+    req = connection.execute(text(sql), thiscdref=cd_ref)
+    listCommunes = list()
+    for r in req:
+        temp = {"insee": r.insee, "commune_maj": r.commune_maj}
+        listCommunes.append(temp)
+    return listCommunes
diff --git a/data/update/update_1.6.1to1.6.2.sql b/data/update/update_1.6.1to1.6.2.sql
index 8841c9526..c2a20a1e4 100644
--- a/data/update/update_1.6.1to1.6.2.sql
+++ b/data/update/update_1.6.1to1.6.2.sql
@@ -43,3 +43,8 @@ CREATE INDEX ON atlas.vm_observations_mailles
 
 CREATE INDEX ON atlas.vm_observations_mailles
     USING btree (id_maille, cd_ref);
+
+
+-- ISSUE #531 & #532
+CREATE EXTENSION IF NOT EXISTS unaccent SCHEMA "public";
+
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 2709dccf9..66b57b61a 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -2,6 +2,21 @@
 CHANGELOG
 =========
 
+[Unreleased]
+------------------
+
+🐛 **Optimisations**
+
+- Amélioration de la "recherche par commune": mots tronqués, sans accent (#531 par @jpm-cbna)
+
+
+⚠ **Notes de version**
+
+Si vous mettez Ă  jour GeoNature-atlas :
+
+- Exécutez le script SQL de mise à jour de la BDD : https://github.com/PnX-SI/GeoNature-atlas/blob/master/data/update/update_1.6.1to1.6.2.sql
+
+
 1.6.1 (2023-10-16)
 ------------------