Endpoints to search an address (Fantoir/Rivoli) #8

Merged
johan.le.baut merged 4 commits from search-by-address into master 2021-11-10 23:26:09 +01:00
14 changed files with 442 additions and 7 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
__pycache__
/elig-test.ini

View file

@ -15,7 +15,7 @@ Une fois poetry installé, vous pouvez obtenir les dépendances Python à l'aide
Nous avons quelques tests pour le parseur. Vous pouvez les jouer à l'aide de:
```bash
$ poetry run python test_axione_api.py
$ poetry run pytest
```
## Debugger l'Application Localement
@ -26,3 +26,22 @@ Pour lancer la webapp localement en mode debug, vous pouvez utiliser le script `
```bash
./run-dev-server
```
### Tester la recherche d'adresses
Pour tester la recherche d'addresse il faut en local la DB sqlite contenant les infos de Fantoir et de la base de la Poste (Infos sur les communes)
Dans le fichier elig-test.ini bien renseigner le chemin en local vers cette base (champ `db_addresses_sqlite_path` dans la section `[ADDRESSES]`)
Une fois le serveur lancer comme décrit plus haut, on peut tester l'API via curl :
```bash
# Obtenir la liste de toutes les communes
curl -v http://127.0.0.1:5000/addresses/communes
# Chercher une commune par nom
curl -v http://127.0.0.1:5000/addresses/communes?s=plonevez+du+faou
# Chercher une commune par code postal
curl -v http://127.0.0.1:5000/addresses/communes?s=29530
# Chercher les voies d'une commune via son code insee
curl -v http://127.0.0.1:5000/addresses/fantoirvoies/29036
```

View file

79
address_finder/api.py Normal file
View file

@ -0,0 +1,79 @@
import sqlite3
import sys
import json
from .model import Commune,FantoirVoie
# DB with addresses info
DB_ADDRESSES_PATH_ENV="DB_ADDRESSES_PATH"
DB_ADDRESSES_DEFAULT_PATH="/etc/fantoir.sqlite"
# Table for insee codes
DB_TABLE_INSEE_NAME="insee"
DB_COL_COMMUNE_INSEE="Code_commune_INSEE"
DB_COL_COMMUNE_NAME="Nom_commune"
DB_COL_COMMUNE_POSTE="Code_postal"
# Table for Fantoir voies (code Rivoli)
DB_TABLE_FANTOIR_NAME="keyv"
DB_COL_FANTOIR_INSEE="key"
DB_FANTOIR_INSEE_KEY_SUFFIX="keyv:"
# Utility to find an address
class AddressFinder:
def __init__(self, db_addresses_sqlite_path: str):
self.dbPath = db_addresses_sqlite_path
print("DB addresses Path : " + self.dbPath)
def getCommunesFromNameOrZip(self, communeNameOrZip: str) -> list[Commune]:
con = sqlite3.connect(self.dbPath)
con.row_factory = sqlite3.Row
cur = con.cursor()
communes: list[Commune] = []
try:
if communeNameOrZip is None:
cur.execute(f"SELECT * from \"{DB_TABLE_INSEE_NAME}\"")
else:
cur.execute(f"SELECT * from \"{DB_TABLE_INSEE_NAME}\" WHERE {DB_COL_COMMUNE_NAME}=\"{communeNameOrZip}\" COLLATE nocase OR {DB_COL_COMMUNE_POSTE}=\"{communeNameOrZip}\"")
except sqlite3.OperationalError as err:
print("Error querying DB : {0}".format(err), file=sys.stderr)
return []
rows = [dict(row) for row in cur.fetchall()]
con.close()
for row in rows:
commune=Commune(
codeInsee=row[DB_COL_COMMUNE_INSEE],
nom=row[DB_COL_COMMUNE_NAME],
codeZip=row[DB_COL_COMMUNE_POSTE])
communes.append(commune)
return communes
def getCommuneFantoirVoies(self, communeInseeCode: str) -> list[FantoirVoie]:
# Extract data from DB
con = sqlite3.connect(self.dbPath)
con.row_factory = sqlite3.Row
cur = con.cursor()
try:
cur.execute(f"SELECT value from \"{DB_TABLE_FANTOIR_NAME}\" WHERE {DB_COL_FANTOIR_INSEE}=\"{DB_FANTOIR_INSEE_KEY_SUFFIX}{communeInseeCode}\"")
except sqlite3.OperationalError as err:
print("Error querying DB : {0}".format(err), file=sys.stderr)
return []
data_raw = cur.fetchone()
con.close()
## Get JSON payload
fantoir_dict = []
# Check if data where found
if data_raw is not None:
data = dict(data_raw)
# Extract the data behind "value" which is a JSON structure
data_dict=json.loads(data.get("value"))
# In extracted JSON data, the interesting payload is behind "value" key
fantoir_dict = data_dict.get("value")
else:
print("Did not found any data matching Insee code " + str(communeInseeCode))
# Return the json dump
return fantoir_dict

17
address_finder/model.py Normal file
View file

@ -0,0 +1,17 @@
from typing import TypedDict
class Commune(TypedDict):
codeInsee: str
nom: str
codeZip: str
class FantoirVoie(TypedDict):
id: str
dateAjout: int
libelle: list[str]
typeVoie:str
codeCommune: str
codeFantoir: str
cleRivoli: str
nomCommune: str
predecesseur: bool

0
config/__init__.py Normal file
View file

View file

@ -2,10 +2,11 @@ import configparser
class Config:
def __init__(self, username, password, source_addr):
def __init__(self, username, password, source_addr, db_addresses_sqlite_path):
self.username = username
self.password = password
self.source_addr = source_addr
self.db_addresses_sqlite_path = db_addresses_sqlite_path
self.debug = False
@ -16,4 +17,5 @@ def parse_config(cfgPath):
username = cfg.get("API", "username")
passwd = cfg.get("API", "password")
source_addr = cfg.get("API", "source_addr")
return Config(username, passwd, source_addr)
db_addresses_sqlite_path = cfg.get("ADDRESSES","db_addresses_sqlite_path")
return Config(username, passwd, source_addr,db_addresses_sqlite_path)

View file

@ -3,4 +3,6 @@
password = xxx
# Whitelisted IP address from axione
# to send the requests from.
source_addr = xxx.xxx.xxx.xxx
source_addr = xxx.xxx.xxx.xxx
[ADDRESSES]
db_addresses_sqlite_path = path/to/db.sqlite

140
poetry.lock generated
View file

@ -1,3 +1,25 @@
[[package]]
name = "atomicwrites"
version = "1.4.0"
description = "Atomic file writes."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "attrs"
version = "21.2.0"
description = "Classes Without Boilerplate"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.extras]
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
[[package]]
name = "black"
version = "21.9b0"
@ -76,6 +98,14 @@ gevent = ["gevent (>=1.4.0)"]
setproctitle = ["setproctitle"]
tornado = ["tornado (>=0.2)"]
[[package]]
name = "iniconfig"
version = "1.1.1"
description = "iniconfig: brain-dead simple config-ini parsing"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "itsdangerous"
version = "2.0.1"
@ -131,6 +161,17 @@ category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "packaging"
version = "21.2"
description = "Core utilities for Python packages"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
pyparsing = ">=2.0.2,<3"
[[package]]
name = "pathspec"
version = "0.9.0"
@ -151,6 +192,55 @@ python-versions = ">=3.6"
docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"]
test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"]
[[package]]
name = "pluggy"
version = "1.0.0"
description = "plugin and hook calling mechanisms for python"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "py"
version = "1.11.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "pyparsing"
version = "2.4.7"
description = "Python parsing module"
category = "dev"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "pytest"
version = "6.2.5"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
py = ">=1.8.2"
toml = "*"
[package.extras]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
[[package]]
name = "regex"
version = "2021.10.8"
@ -197,9 +287,17 @@ watchdog = ["watchdog"]
[metadata]
lock-version = "1.1"
python-versions = "^3.8"
content-hash = "2e09ffd1a733b1eebc75fcc481e28791601a142256ce45ad97f71abe520e428c"
content-hash = "3dbddacec432f6900d040f9ca3f1faca877382ffb5a8e15c4d077798fa21b1fc"
[metadata.files]
atomicwrites = [
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
]
attrs = [
{file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
{file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
]
black = [
{file = "black-21.9b0-py3-none-any.whl", hash = "sha256:380f1b5da05e5a1429225676655dddb96f5ae8c75bdf91e53d798871b902a115"},
{file = "black-21.9b0.tar.gz", hash = "sha256:7de4cfc7eb6b710de325712d40125689101d21d25283eed7e9998722cf10eb91"},
@ -217,8 +315,13 @@ flask = [
{file = "Flask-2.0.2.tar.gz", hash = "sha256:7b2fb8e934ddd50731893bdcdb00fc8c0315916f9fcd50d22c7cc1a95ab634e2"},
]
gunicorn = [
{file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"},
{file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"},
]
iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
]
itsdangerous = [
{file = "itsdangerous-2.0.1-py3-none-any.whl", hash = "sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c"},
{file = "itsdangerous-2.0.1.tar.gz", hash = "sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0"},
@ -233,6 +336,9 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"},
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"},
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"},
{file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"},
{file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"},
{file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"},
{file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"},
{file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
@ -244,6 +350,9 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
@ -255,6 +364,9 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
{file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"},
@ -267,6 +379,9 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"},
{file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"},
{file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"},
{file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
@ -279,6 +394,9 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"},
{file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"},
{file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"},
{file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
@ -312,6 +430,10 @@ mypy-extensions = [
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
]
packaging = [
{file = "packaging-21.2-py3-none-any.whl", hash = "sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0"},
{file = "packaging-21.2.tar.gz", hash = "sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966"},
]
pathspec = [
{file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
{file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
@ -320,6 +442,22 @@ platformdirs = [
{file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"},
{file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"},
]
pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
]
py = [
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
]
pyparsing = [
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
]
pytest = [
{file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
{file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"},
]
regex = [
{file = "regex-2021.10.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:094a905e87a4171508c2a0e10217795f83c636ccc05ddf86e7272c26e14056ae"},
{file = "regex-2021.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:981c786293a3115bc14c103086ae54e5ee50ca57f4c02ce7cf1b60318d1e8072"},

View file

@ -12,6 +12,7 @@ gunicorn = "^20.1.0"
[tool.poetry.dev-dependencies]
mypy = "^0.910"
black = "^21.9b0"
pytest = "^6.2.5"
[build-system]
requires = ["poetry-core>=1.0.0"]

View file

@ -1,3 +1,3 @@
#!/usr/bin/env bash
DEBUG=true CONFIG=./elig-test.ini.sample FLASK_APP=webapp poetry run flask run --reload
DEBUG=true CONFIG=./elig-test.ini FLASK_APP=webapp poetry run flask run --reload

View file

@ -0,0 +1,150 @@
import unittest
import os
from address_finder.api import *
row1_insee_expected = Commune(
codeInsee="33063", nom="BORDEAUX", codeZip="33000")
row1_insee_db = {
DB_COL_COMMUNE_INSEE: row1_insee_expected.get('codeInsee'),
DB_COL_COMMUNE_NAME: row1_insee_expected.get('nom'),
DB_COL_COMMUNE_POSTE: row1_insee_expected.get('codeZip')
}
row2_insee_expected = Commune(codeInsee="64445", nom="PAU", codeZip="64000")
row2_insee_db = {
DB_COL_COMMUNE_INSEE: row2_insee_expected.get('codeInsee'),
DB_COL_COMMUNE_NAME: row2_insee_expected.get('nom'),
DB_COL_COMMUNE_POSTE: row2_insee_expected.get('codeZip')
}
rows_db = [row1_insee_expected, row2_insee_expected]
fantoir_insee_to_test = "33063"
fantoir_expected = [
FantoirVoie(
id=f"{fantoir_insee_to_test}-9500",
dateAjout=1987001,
libelle=["rue toto"],
typeVoie="voie",
codeCommune=fantoir_insee_to_test,
codeFantoir="9500",
cleRivoli="X",
nomCommune="BORDEAUX",
predecesseur=False
),
FantoirVoie(
id=f"{fantoir_insee_to_test}-9501",
dateAjout=1987001,
libelle=["rue tata"],
typeVoie="voie",
codeCommune=fantoir_insee_to_test,
codeFantoir="9501",
cleRivoli="X",
nomCommune="BORDEAUX",
predecesseur=False
)
]
fantoir_db = {
DB_COL_FANTOIR_INSEE: f"{DB_FANTOIR_INSEE_KEY_SUFFIX}{fantoir_insee_to_test}",
'value': json.dumps({
'value': fantoir_expected,
'expires': 'null'
})
}
class TestAddressFinderAPI(unittest.TestCase):
@classmethod
def setUpClass(self):
print("SETUP class")
try:
os.remove("testdb.sqlite")
except OSError:
pass
self.conn = sqlite3.connect('testdb.sqlite')
c = self.conn.cursor()
# Create table communes (insee)
c.execute(f'''CREATE TABLE {DB_TABLE_INSEE_NAME}
({DB_COL_COMMUNE_INSEE} text, {DB_COL_COMMUNE_NAME} text, {DB_COL_COMMUNE_POSTE} text)''')
# Create table fantoir voies
c.execute(f'''CREATE TABLE {DB_TABLE_FANTOIR_NAME}
({DB_COL_FANTOIR_INSEE} text, value text)''')
c.execute(
f"INSERT INTO {DB_TABLE_INSEE_NAME} VALUES (:{DB_COL_COMMUNE_INSEE}, :{DB_COL_COMMUNE_NAME}, :{DB_COL_COMMUNE_POSTE})", row1_insee_db)
c.execute(
f"INSERT INTO {DB_TABLE_INSEE_NAME} VALUES (:{DB_COL_COMMUNE_INSEE}, :{DB_COL_COMMUNE_NAME}, :{DB_COL_COMMUNE_POSTE})", row2_insee_db)
c.execute(
f"INSERT INTO {DB_TABLE_FANTOIR_NAME} VALUES (:{DB_COL_FANTOIR_INSEE}, :value)", fantoir_db)
# Save (commit) the changes
self.conn.commit()
def test_no_db_returns_empty(self):
try:
os.remove("nodbhere.sqlite")
addressFinder = AddressFinder("nodbhere.sqlite")
communes = addressFinder.getCommunesFromNameOrZip(None)
self.assertEqual(len(communes), 0)
voies = addressFinder.getCommuneFantoirVoies(fantoir_insee_to_test)
self.assertEqual(len(voies), 0)
os.remove("nodbhere.sqlite")
except OSError:
pass
def test_get_all_communes(self):
addressFinder = AddressFinder("testdb.sqlite")
communes = addressFinder.getCommunesFromNameOrZip(None)
self.assertEqual(len(communes), 2)
self.assertDictEqual(communes[0], row1_insee_expected)
self.assertDictEqual(communes[1], row2_insee_expected)
def test_get_commune_by_name(self):
addressFinder = AddressFinder("testdb.sqlite")
communes = addressFinder.getCommunesFromNameOrZip(
row1_insee_expected.get('nom'))
self.assertEqual(len(communes), 1)
self.assertDictEqual(communes[0], row1_insee_expected)
def test_get_commune_by_zip(self):
addressFinder = AddressFinder("testdb.sqlite")
communes = addressFinder.getCommunesFromNameOrZip(
row2_insee_expected.get('codeZip'))
self.assertEqual(len(communes), 1)
self.assertDictEqual(communes[0], row2_insee_expected)
def test_get_no_commune_bad_search(self):
addressFinder = AddressFinder("testdb.sqlite")
communes = addressFinder.getCommunesFromNameOrZip("baaaaad name")
self.assertEqual(len(communes), 0)
def test_get_voies_by_valid_insee(self):
addressFinder = AddressFinder("testdb.sqlite")
voies = addressFinder.getCommuneFantoirVoies(fantoir_insee_to_test)
self.assertEqual(len(voies), 2)
self.assertDictEqual(voies[0], fantoir_expected[0])
self.assertDictEqual(voies[1], fantoir_expected[1])
def test_get_no_voies_not_valid_insee(self):
addressFinder = AddressFinder("testdb.sqlite")
voies = addressFinder.getCommuneFantoirVoies("baaaaad")
self.assertEqual(len(voies), 0)
@classmethod
def tearDownClass(self):
self.conn.close()
try:
os.remove("testdb.sqlite")
except OSError:
pass
if __name__ == "__main__":
unittest.main()

View file

@ -1,8 +1,10 @@
import os
from flask import Flask, render_template, request, escape
import json
from axione_api.config import parse_config
from config.config import parse_config
from axione_api.api import query_axione_pto, parse_response
from address_finder.api import AddressFinder
def load_config():
cfg_path = os.environ.get("CONFIG", "/etc/ftth-elig/conf.ini")
@ -19,6 +21,9 @@ def load_config():
return cfg
cfg = load_config()
addressFinder = AddressFinder(cfg.db_addresses_sqlite_path)
app = Flask(__name__)
@app.route("/", methods=['GET'])
@ -30,3 +35,23 @@ def show_result():
pto = escape(request.form['pto'])
result = parse_response(query_axione_pto(cfg, pto))
return render_template("result.html", pto=pto, result=result)
@app.route("/addresses/communes", methods=['GET'])
def get_communes():
to_search=request.args.get('s')
print(to_search)
communes=addressFinder.getCommunesFromNameOrZip(to_search)
response = app.response_class(
response=json.dumps(communes),
mimetype='application/json'
)
return response
@app.route("/addresses/fantoirvoies/<codeInsee>", methods=['GET'])
def get_fantoir_voies(codeInsee):
fantoirVoies=addressFinder.getCommuneFantoirVoies(codeInsee)
response = app.response_class(
response=json.dumps(fantoirVoies),
mimetype='application/json'
)
return response