Commit ec6d79f5 authored by Christian Quest's avatar Christian Quest
Browse files

première version fonctionnelle

parent e668aa29
Pipeline #965 failed with stages
in 53 seconds
......@@ -29,8 +29,22 @@ Lancer l'API via gunicorn à l'aide de :
`gunicorn siretor:app`
L'API répond par défaut sur http://localhost:8000/siretor
## Utilisation de l'API
Recherche par nom approximatif d'établiseement ou entreprise sur une commune connue:
http://localhost:8000/siretor?nom=petit+duc&code_commune=94068
Recherche par nom et adresse approximatif sur une commune connue:
http://localhost:8000/siretor?nom=petit+duc&adresse=avenue+charles+de+gaule&code_commune=94068
Recherche par nom approximatif et code NAF partiel sur une commune connue:
http://localhost:8000/siretor?nom=petit+duc&naf=10&code_commune=94068
Les noms des champs retournés ainsi que leur contenu sont ceux de la base SIRENE de l'INSEE.
## Todo
......
#! /usr/bin/python3
# required modules
import falcon
import psycopg2
class siretor(object):
def on_get(self, req, resp):
db = psycopg2.connect("dbname=sirene")
cur = db.cursor()
limite_nom = 0.5
limite_adresse = 0.5
where = ''
score = ''
score_max = 0
# recherches "exactes" (codes INSEE dep/com et NAF)
code_commune = req.params.get('code_commune', None)
if code_commune and len(code_commune) > 1:
where = where + cur.mogrify(' AND s.code_commune = %s ', (code_commune,)).decode('utf8')
siren = req.params.get('siren', None)
if siren and len(siren) == 9:
where = where + cur.mogrify(' AND s.siren = %s ', (siren,)).decode('utf8')
cat = req.params.get('categorie', None)
if cat:
where = where + cur.mogrify(' AND s.categorie = %s ', (cat,)).decode('utf8')
# recherches floues (nom et adresse)
nom = req.params.get('nom',None)
if nom:
if len(nom.strip()) < 3:
resp.status = falcon.HTTP_413
resp.body = '{"erreur": "nom doit avoir au moins 3 caractères"}'
else:
nom = cur.mogrify(nom).decode('utf8')
where = where + cur.mogrify(" AND s.nom <->> %s < %s ", (nom, limite_nom)).decode('utf8')
score = score + cur.mogrify(" + (s.nom <-> %s)::numeric + (s.nom <->> %s)::numeric + (s.nom <<-> %s)::numeric ", (nom, nom, nom)).decode('utf8')
score_max = score_max + 3
adresse = req.params.get('adresse',None)
if adresse:
if len(adresse.strip()) < 10:
resp.status = falcon.HTTP_413
resp.body = '{"erreur": "adresse doit avoir au moins 10 caractères"}'
else:
where = where + cur.mogrify(' AND s.adresse <->> %s < %s ',(adresse, limite_adresse)).decode('utf8')
score = score + cur.mogrify(" + (s.adresse <-> %s)::numeric + (s.adresse <->> %s)::numeric ", (adresse, adresse)).decode('utf8')
score_max = score_max + 2
# recherches "commence par" (nom et adresse)
cp = req.params.get('cp', None)
if cp and len(cp) > 1:
where = where + cur.mogrify(' AND s.cp_ville like %s ',(cp+'%',)).decode('utf8')
naf = req.params.get('naf',None)
if naf:
where = where + cur.mogrify(' AND s.apet700 like %s ',(naf+'%',)).decode('utf8')
# score de 1 par défaut si aucune recherche floue
if score_max == 0:
score_max = 1
score = ''
if where != '':
query = """
set statement_timeout=10000;
select json_build_object(
'source', 'Base SIRENE, INSEE',
'millesime', '2021-09',
'licence', 'Licence Ouverte 2.0',
'query_execution_time_ms', EXTRACT(EPOCH FROM (statement_timestamp()-now())*1000000),
'count', count(r),
'result', array_to_json(array_agg(r.j))
)::text
from (
select
json_build_object(
'siretor_score', round(1-((0 %s) / %s),3),
'etablissement', row_to_json(et),
'unitelegale', row_to_json(en)
) as j
from
siretor s
join
siret et on (et.siret = s.siret)
join
siren en on (en.siren = s.siren)
where
(true %s)
order by 1-((0 %s) / %s) desc
limit 10) as r""" % (score, score_max, where, score, score_max)
cur.execute(query)
siret = cur.fetchone()
resp.status = falcon.HTTP_200
resp.set_header('X-Powered-By', 'siretor')
resp.set_header('Access-Control-Allow-Origin', '*')
resp.set_header("Access-Control-Expose-Headers","Access-Control-Allow-Origin")
resp.set_header('Access-Control-Allow-Headers','Origin, X-Requested-With, Content-Type, Accept')
resp.body = siret[0]
else:
resp.status = falcon.HTTP_413
resp.body = '{"erreur": "aucune critère de recherche indiqué"}'
db.close()
app = falcon.API()
app.add_route('/siretor', siretor())
export PGDATABASE=siretor
wget -nv -N http://files.data.gouv.fr/insee-sirene/StockUniteLegale_utf8.zip &
wget -nv -N http://data.cquest.org/geo_sirene/v2019/last/StockEtablissement_utf8_geo.csv.gz
wait
# création de la table SIREN (entreprises)
psql -c "drop table if exists siren_temp; create table siren_temp (`unzip -p StockUniteLegale_utf8.zip | head -n 1 | sed 's/,/ text,/g;s/$/ text/'`);"
unzip -p StockUniteLegale_utf8.zip | psql $DB -c "\copy siren_temp from stdin with (format csv, header true)"
# création de la table des établissements
psql -c "drop table if exists siret_temp; create table siret_temp (`zcat StockEtablissement_utf8_geo.csv.gz | head -n 1 | sed 's/,/ text,/g;s/$/ text/'`);"
zcat StockEtablissement_utf8_geo.csv.gz | psql -c "\copy siret_temp from stdin with (format csv, header true)"
# mise à jour live (transaction) de la table "siren" avec optimisation: clustering de la table sur SIREN
psql -c "
-- extensions postgresql nécessaires
create extension if not exists pg_trgm;
create extension if not exists postgis;
begin;
-- définition des tables finales à partir des temporaires
create table if not exists siren as (select * from siren_temp limit 1);
create table if not exists siret as (select * from siret_temp limit 1);
-- on vide les tables finales pour leur mise à jour
truncate siren;
truncate siret;
-- création d'une vue matérialisée pour l'indexation
create materialized view if not exists siretor as
select
et.siren,
et.siret,
activiteprincipaleetablissement as apet700,
categorieentreprise as categorie,
trim(regexp_replace(format('%s / %s / %s %s %s / %s / %s',
denominationunitelegale,
denominationusuelle1unitelegale,
nomunitelegale,prenom1unitelegale,prenom2unitelegale,
denominationusuelleetablissement,
enseigne1etablissement
), ' */ *',' ','g')) as nom,
format('%s%s %s %s - %s',
numerovoieetablissement, indicerepetitionetablissement, typevoieetablissement, libellevoieetablissement,
geo_adresse
) as adresse,
format('%s %s', codepostaletablissement, libellecommuneetablissement) as cp_ville,
codecommuneetablissement as code_commune,
st_setsrid(ST_makepoint(longitude::numeric, latitude::numeric),4326) as geom,
etatadministratifetablissement as etat
from siret et
join siren en on (en.siren=et.siren)
order by code_commune;
-- remplissage des tables finales
insert into siren select * from siren_temp order by siren;
insert into siret select * from siret_temp order by siret;
-- mise à jour de la vue matérialisée
refresh materialized view siretor;
-- suppression des tables temporaires
drop table siren_temp;
drop table siret_temp;
-- validatation de la transaction
commit;
"
echo "######## Indexation en cours"
psql -c "CREATE INDEX if not exists siren_siren on siren using brin (siren);" &
psql -c "CREATE INDEX if not exists siret_siren on siret using brin (siren);" &
psql -c "CREATE INDEX if not exists siret_siret on siret using brin (siret);" &
psql -c "CREATE INDEX if not exists siretor_code_commune ON siretor USING BRIN (code_commune) ;" &
psql -c "CREATE INDEX if not exists siretor_siren ON siretor (siren) ;" &
psql -c "CREATE INDEX if not exists siretor_apet700 ON siretor (apet700) ;" &
psql -c "CREATE INDEX if not exists siretor_categorie ON siretor (categorie) ;" &
psql -c "CREATE INDEX if not exists siretor_nom ON siretor USING GIST (nom gist_trgm_ops) ;" &
psql -c "CREATE INDEX if not exists siretor_adresse ON siretor USING GIST (adresse gist_trgm_ops) ;" &
psql -c "CREATE INDEX if not exists siretor_cp_ville ON siretor USING GIST (cp_ville gist_trgm_ops) ;" &
psql -c "CREATE INDEX if not exists siretor_geom ON siretor USING GIST (geom) ;" &
wait
echo "######## Import/mise à jour et indexation terminés"
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment