├── aio ├── aio-proxy │ ├── CHANGELOG.md │ ├── aio_proxy │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── main.pyc │ │ ├── __init__.pyc │ │ ├── __pycache__ │ │ │ ├── main.cpython-39.pyc │ │ │ ├── routes.cpython-39.pyc │ │ │ ├── __init__.cpython-39.pyc │ │ │ ├── __main__.cpython-39.pyc │ │ │ └── settings.cpython-39.pyc │ │ ├── settings.py │ │ ├── main.py │ │ └── routes.py │ ├── requirements.pip │ ├── README.md │ ├── config │ │ └── aio_proxy.yaml │ └── Makefile └── Dockerfile ├── .gitignore ├── db ├── init │ ├── 80-remove-downloaded-files.sh │ ├── 80-generate-sitemap.sh │ ├── 00-get-last-siren-data.sh │ ├── 10-get-last-siret-data.sh │ ├── 50-create-intermediary-view.sh │ ├── 30-populate-siren.sh │ ├── 60-enrich-columns.sh │ ├── 65-create-agg-table.sh │ ├── 40-populate-siret.sh │ ├── 20-postgresql_setup.sh │ └── 70-create-view-and-functions.sh └── Dockerfile ├── docker-compose-postgres-blue.yml ├── docker-compose-postgres-green.yml ├── api ├── postgrest.conf └── Dockerfile ├── docker-compose-postgres-init.yml ├── docker-compose-aio.yml ├── docker-compose-traefik.yml ├── docker-compose-postgrest-blue.yml ├── docker-compose-postgrest-green.yml ├── docker-compose.yml ├── deploy.sh ├── LICENSE ├── utils └── server.nginx.conf └── README.md /aio/aio-proxy/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | database-data/ 2 | data/ 3 | old/ 4 | nohup.out 5 | -------------------------------------------------------------------------------- /aio/aio-proxy/aio_proxy/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.0.1' 2 | -------------------------------------------------------------------------------- /db/init/80-remove-downloaded-files.sh: -------------------------------------------------------------------------------- 1 | rm -rf /var/lib/postgresql/files -------------------------------------------------------------------------------- /aio/aio-proxy/aio_proxy/__main__.py: -------------------------------------------------------------------------------- 1 | from aio_proxy.main import main 2 | 3 | main() 4 | -------------------------------------------------------------------------------- /aio/aio-proxy/requirements.pip: -------------------------------------------------------------------------------- 1 | aiohttp==3.7.3 2 | PyYAML==5.3.1 3 | pyaml-env==1.1.3 4 | -------------------------------------------------------------------------------- /aio/aio-proxy/aio_proxy/main.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etalab/annuaire-entreprises-sirene-api/HEAD/aio/aio-proxy/aio_proxy/main.pyc -------------------------------------------------------------------------------- /aio/aio-proxy/aio_proxy/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etalab/annuaire-entreprises-sirene-api/HEAD/aio/aio-proxy/aio_proxy/__init__.pyc -------------------------------------------------------------------------------- /aio/aio-proxy/aio_proxy/__pycache__/main.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etalab/annuaire-entreprises-sirene-api/HEAD/aio/aio-proxy/aio_proxy/__pycache__/main.cpython-39.pyc -------------------------------------------------------------------------------- /aio/aio-proxy/aio_proxy/__pycache__/routes.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etalab/annuaire-entreprises-sirene-api/HEAD/aio/aio-proxy/aio_proxy/__pycache__/routes.cpython-39.pyc -------------------------------------------------------------------------------- /aio/aio-proxy/aio_proxy/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etalab/annuaire-entreprises-sirene-api/HEAD/aio/aio-proxy/aio_proxy/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /aio/aio-proxy/aio_proxy/__pycache__/__main__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etalab/annuaire-entreprises-sirene-api/HEAD/aio/aio-proxy/aio_proxy/__pycache__/__main__.cpython-39.pyc -------------------------------------------------------------------------------- /aio/aio-proxy/aio_proxy/__pycache__/settings.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etalab/annuaire-entreprises-sirene-api/HEAD/aio/aio-proxy/aio_proxy/__pycache__/settings.cpython-39.pyc -------------------------------------------------------------------------------- /db/init/80-generate-sitemap.sh: -------------------------------------------------------------------------------- 1 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "\copy (select nom_url from siren_full WHERE nature_juridique_entreprise != '1000') to '/tmp/sitemap-name.csv' with csv" -------------------------------------------------------------------------------- /aio/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9 2 | 3 | COPY ./aio-proxy /app 4 | 5 | WORKDIR /app 6 | 7 | RUN pip install -r requirements.pip 8 | 9 | EXPOSE 4400 10 | 11 | CMD [ "make", "run" ] 12 | -------------------------------------------------------------------------------- /aio/aio-proxy/README.md: -------------------------------------------------------------------------------- 1 | # aio-proxy 2 | 3 | ## config 4 | ``` 5 | export AIO_PROXY_HOST="0.0.0.0" 6 | export AIO_PROXY_PORT="4400" 7 | export POSTGREST_API_HOST="postrest" 8 | export POSTGREST_API_PORT="3000" 9 | ``` 10 | -------------------------------------------------------------------------------- /db/init/00-get-last-siren-data.sh: -------------------------------------------------------------------------------- 1 | mkdir -p /var/lib/postgresql/files 2 | cd /var/lib/postgresql/files && wget https://files.data.gouv.fr/insee-sirene/StockUniteLegale_utf8.zip 3 | cd /var/lib/postgresql/files && unzip -o StockUniteLegale_utf8.zip -------------------------------------------------------------------------------- /aio/aio-proxy/aio_proxy/settings.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | import yaml 3 | 4 | from pyaml_env import parse_config 5 | 6 | BASE_DIR = pathlib.Path(__file__).parent.parent 7 | config_path = BASE_DIR / 'config' / 'aio_proxy.yaml' 8 | 9 | config = parse_config(config_path) 10 | -------------------------------------------------------------------------------- /aio/aio-proxy/config/aio_proxy.yaml: -------------------------------------------------------------------------------- 1 | host: !ENV "${AIO_PROXY_HOST}" 2 | port: !ENV "${AIO_PROXY_PORT}" 3 | 4 | output_url_siren: !ENV "http://${POSTGREST_API_HOST}:${POSTGREST_API_PORT}/rpc/get_etablissements" 5 | output_url_siret: !ENV "http://${POSTGREST_API_HOST}:${POSTGREST_API_PORT}/rpc/get_etablissement" 6 | -------------------------------------------------------------------------------- /aio/aio-proxy/Makefile: -------------------------------------------------------------------------------- 1 | FLAGS= 2 | 3 | 4 | flake: 5 | flake8 aio_proxy setup.py 6 | 7 | clean: 8 | rm -rf `find . -name __pycache__` 9 | find . -type f -name '*.py[co]' -delete 10 | find . -type f -name '*~' -delete 11 | find . -type f -name '.*~' -delete 12 | rm -rf build 13 | 14 | run: 15 | python -m aio_proxy 16 | 17 | .PHONY: flake clean 18 | -------------------------------------------------------------------------------- /db/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM postgres:12 2 | 3 | WORKDIR /srv/sirene/ 4 | 5 | RUN apt-get update \ 6 | && apt-get install -y wget \ 7 | && apt-get install -y unzip \ 8 | && rm -rf /var/lib/apt/lists/* 9 | 10 | RUN mkdir -p /tmp/data/ 11 | 12 | EXPOSE 5432 13 | 14 | HEALTHCHECK --interval=60s --timeout=3s \ 15 | CMD psql -U $POSTGRES_USER -d $POSTGRES_DB -c "SELECT * FROM get_etablissement('');" -------------------------------------------------------------------------------- /docker-compose-postgres-blue.yml: -------------------------------------------------------------------------------- 1 | version: '3.6' 2 | services: 3 | db-blue: 4 | build: 5 | context: ./db 6 | shm_size: '60gb' 7 | shm_size: '60gb' 8 | volumes: 9 | - ./db/init/:/docker-entrypoint-initdb.d/ 10 | environment: 11 | - POSTGRES_DB=sirene 12 | - POSTGRES_USER=sirene 13 | - POSTGRES_PASSWORD=sirene 14 | networks: 15 | - traefik 16 | networks: 17 | traefik: 18 | external: 19 | name: backend-siren_backendsiren 20 | -------------------------------------------------------------------------------- /docker-compose-postgres-green.yml: -------------------------------------------------------------------------------- 1 | version: '3.6' 2 | services: 3 | db-green: 4 | build: 5 | context: ./db 6 | shm_size: '60gb' 7 | shm_size: '60gb' 8 | volumes: 9 | - ./db/init/:/docker-entrypoint-initdb.d/ 10 | environment: 11 | - POSTGRES_DB=sirene 12 | - POSTGRES_USER=sirene 13 | - POSTGRES_PASSWORD=sirene 14 | networks: 15 | - traefik 16 | networks: 17 | traefik: 18 | external: 19 | name: backend-siren_backendsiren 20 | -------------------------------------------------------------------------------- /api/postgrest.conf: -------------------------------------------------------------------------------- 1 | # The standard connection URI format, documented at 2 | # https://www.postgresql.org/docs/current/static/libpq-connect.html#AEN45347 3 | db-uri = "postgres://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@$(POSTGRES_DB_HOST):$(POSTGRES_DB_PORT)/$(POSTGRES_DB)" 4 | 5 | # The name of which database schema to expose to REST clients 6 | db-schema = "public" 7 | 8 | # The database role to use when no client authentication is provided. 9 | # Can (and should) differ from user in db-uri 10 | db-anon-role = "$(POSTGRES_DB)" 11 | -------------------------------------------------------------------------------- /docker-compose-postgres-init.yml: -------------------------------------------------------------------------------- 1 | version: '3.6' 2 | services: 3 | db-init: 4 | build: 5 | context: ./db 6 | shm_size: '60gb' 7 | shm_size: '60gb' 8 | volumes: 9 | - ./db/init/:/docker-entrypoint-initdb.d/ 10 | - type: bind 11 | source: /var/lib/postgresql 12 | target: /var/lib/postgresql 13 | - type: bind 14 | source: /var/lib/postgresql/data 15 | target: /var/lib/postgresql/data 16 | environment: 17 | - POSTGRES_DB=sirene 18 | - POSTGRES_USER=sirene 19 | - POSTGRES_PASSWORD=sirene 20 | -------------------------------------------------------------------------------- /aio/aio-proxy/aio_proxy/main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | import aiohttp 4 | from aiohttp import web 5 | 6 | from aio_proxy.routes import routes 7 | from aio_proxy.settings import config 8 | 9 | 10 | def main(): 11 | logging.basicConfig(level=logging.DEBUG) 12 | 13 | app = web.Application() 14 | 15 | app['http_session'] = aiohttp.ClientSession() 16 | app.router.add_routes(routes) 17 | 18 | app['config'] = config 19 | web.run_app(app, 20 | host=config['host'], 21 | port=config['port']) 22 | 23 | 24 | if __name__ == '__main__': 25 | main() 26 | -------------------------------------------------------------------------------- /docker-compose-aio.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | http-proxy: 4 | build: 5 | context: ./aio 6 | networks: 7 | - traefik 8 | labels: 9 | - "traefik.enable=true" 10 | - "traefik.http.routers.aio.rule=Host(`localhost`)" 11 | - "traefik.http.routers.aio.entrypoints=aio" 12 | - "traefik.port=4400" 13 | environment: 14 | - AIO_PROXY_HOST=0.0.0.0 15 | - AIO_PROXY_PORT=4400 16 | - POSTGREST_API_HOST=postgrest 17 | - POSTGREST_API_PORT=3000 18 | networks: 19 | traefik: 20 | external: 21 | name: backend-siren_backendsiren 22 | -------------------------------------------------------------------------------- /docker-compose-traefik.yml: -------------------------------------------------------------------------------- 1 | version: "3.3" 2 | 3 | services: 4 | 5 | traefik: 6 | image: "traefik:v2.3" 7 | networks: 8 | - backendsiren 9 | container_name: "traefik" 10 | command: 11 | - "--api.insecure=true" 12 | - "--providers.docker=true" 13 | - "--providers.docker.exposedbydefault=false" 14 | - "--entrypoints.postgrest.address=:3000" 15 | - "--entrypoints.aio.address=:4400" 16 | ports: 17 | - "127.0.0.1:3000:3000" 18 | - "127.0.0.1:4400:4400" 19 | - "127.0.0.1:8090:8080" 20 | volumes: 21 | - "/var/run/docker.sock:/var/run/docker.sock:ro" 22 | networks: 23 | backendsiren: 24 | driver: bridge 25 | -------------------------------------------------------------------------------- /docker-compose-postgrest-blue.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | postgrest: 4 | build: 5 | context: ./api 6 | args: 7 | - POSTGREST_VERSION=v7.0.1 8 | environment: 9 | - POSTGRES_DB_HOST=db-blue 10 | - POSTGRES_DB=sirene 11 | - POSTGRES_USER=sirene 12 | - POSTGRES_PASSWORD=sirene 13 | - POSTGRES_DB_PORT=5432 14 | networks: 15 | - traefik 16 | labels: 17 | - "traefik.enable=true" 18 | - "traefik.http.routers.postgrest.rule=Host(`localhost`)" 19 | - "traefik.http.routers.postgrest.entrypoints=postgrest" 20 | - "traefik.port=3000" 21 | networks: 22 | traefik: 23 | external: 24 | name: backend-siren_backendsiren 25 | -------------------------------------------------------------------------------- /docker-compose-postgrest-green.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | postgrest: 4 | build: 5 | context: ./api 6 | args: 7 | - POSTGREST_VERSION=v7.0.1 8 | environment: 9 | - POSTGRES_DB_HOST=db-green 10 | - POSTGRES_DB=sirene 11 | - POSTGRES_USER=sirene 12 | - POSTGRES_PASSWORD=sirene 13 | - POSTGRES_DB_PORT=5432 14 | networks: 15 | - traefik 16 | labels: 17 | - "traefik.enable=true" 18 | - "traefik.http.routers.postgrest.rule=Host(`localhost`)" 19 | - "traefik.http.routers.postgrest.entrypoints=postgrest" 20 | - "traefik.port=3000" 21 | networks: 22 | traefik: 23 | external: 24 | name: backend-siren_backendsiren 25 | -------------------------------------------------------------------------------- /db/init/10-get-last-siret-data.sh: -------------------------------------------------------------------------------- 1 | for d in `seq -w 1 19` 2A 2B `seq 21 74` `seq 76 95` 98 ""; do 2 | cd /var/lib/postgresql/files && wget https://files.data.gouv.fr/geo-sirene/last/dep/geo_siret_$d.csv.gz 3 | gunzip /var/lib/postgresql/files/geo_siret_$d.csv.gz 4 | done 5 | #Cas particulier Paris 6 | for d in `seq -w 1 20`; do 7 | cd /var/lib/postgresql/files && wget https://files.data.gouv.fr/geo-sirene/last/dep/geo_siret_751$d.csv.gz 8 | gunzip /var/lib/postgresql/files/geo_siret_751$d.csv.gz 9 | done 10 | #Cas particulier DOM 11 | for d in `seq -w 1 8`; do 12 | cd /var/lib/postgresql/files && wget https://files.data.gouv.fr/geo-sirene/last/dep/geo_siret_97$d.csv.gz 13 | gunzip /var/lib/postgresql/files/geo_siret_97$d.csv.gz 14 | done -------------------------------------------------------------------------------- /db/init/50-create-intermediary-view.sh: -------------------------------------------------------------------------------- 1 | 2 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c " 3 | CREATE VIEW intermediary_view AS 4 | SELECT 5 | T.codepostaletablissement as code_postal, 6 | T.libellecommuneetablissement as libelle_commune, 7 | T.libellevoieetablissement as libelle_voie, 8 | N.nomunitelegale as nom, 9 | N.denominationunitelegale as nom_raison_sociale, 10 | T.numerovoieetablissement as numero_voie, 11 | N.prenom1unitelegale as prenom, 12 | N.sigleunitelegale as sigle, 13 | N.siren, 14 | T.siret, 15 | T.typevoieetablissement as type_voie, 16 | T.codecommuneetablissement as commune 17 | FROM siret T 18 | LEFT JOIN siren N 19 | ON N.siren = T.siren 20 | WHERE T.etablissementsiege = 't';" 21 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | db: 4 | build: 5 | context: ./db 6 | volumes: 7 | - ./db/init/:/docker-entrypoint-initdb.d/ 8 | - ./database-data:/var/lib/postgresql/data 9 | environment: 10 | - POSTGRES_PASSWORD=password 11 | networks: 12 | - traefik 13 | postgrest: 14 | build: 15 | context: ./api 16 | args: 17 | - POSTGREST_VERSION=v7.0.1 18 | networks: 19 | - traefik 20 | labels: 21 | - "traefik.enable=true" 22 | - "traefik.http.routers.postgrest.rule=Host(`localhost`)" 23 | - "traefik.http.routers.postgrest.entrypoints=postgrest" 24 | - "traefik.port=3000" 25 | depends_on: 26 | - db 27 | networks: 28 | traefik: 29 | external: 30 | name: backend-siren_backendsirene 31 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ENV="db-blue" 4 | echo $ENV 5 | 6 | if [ $(docker ps -f name=db-$ENV -q | wc -l) = 0 ] 7 | then 8 | ENV="blue" 9 | OLD="green" 10 | else 11 | ENV="green" 12 | OLD="blue" 13 | fi 14 | 15 | echo $ENV 16 | 17 | echo "Starting Postgres "$ENV" container" 18 | docker-compose -f docker-compose-postgres-$ENV.yml --project-name=$ENV up --build -d 19 | 20 | while [ $(docker ps --filter "health=healthy" | grep $ENV | wc -l) = 0 ] 21 | do 22 | sleep 30s 23 | echo "Waiting..." 24 | done 25 | 26 | echo "Starting Postgrest "$ENV" container" 27 | docker-compose -f docker-compose-postgrest-$ENV.yml --project-name=$ENV up --build -d 28 | 29 | while [ $(docker ps --filter "health=healthy" | grep $ENV | wc -l) = 0 ] 30 | do 31 | sleep 30s 32 | echo "Waiting..." 33 | done 34 | 35 | echo "Container up and healthy" 36 | 37 | echo "Stopping "$OLD" container" 38 | docker-compose -f docker-compose-postgres-$OLD.yml --project-name=$OLD down 39 | docker-compose -f docker-compose-postgrest-$OLD.yml --project-name=$OLD down 40 | -------------------------------------------------------------------------------- /db/init/30-populate-siren.sh: -------------------------------------------------------------------------------- 1 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "COPY siren(siren,statutDiffusionUniteLegale,unitePurgeeUniteLegale,dateCreationUniteLegale,sigleUniteLegale,sexeUniteLegale,prenom1UniteLegale,prenom2UniteLegale,prenom3UniteLegale,prenom4UniteLegale,prenomUsuelUniteLegale,pseudonymeUniteLegale, identifiantAssociationUniteLegale,trancheEffectifsUniteLegale,anneeEffectifsUniteLegale,dateDernierTraitementUniteLegale,nombrePeriodesUniteLegale,categorieEntreprise,anneeCategorieEntreprise,dateDebut,etatAdministratifUniteLegale,nomUniteLegale,nomUsageUniteLegale,denominationUniteLegale,denominationUsuelle1UniteLegale,denominationUsuelle2UniteLegale,denominationUsuelle3UniteLegale,categorieJuridiqueUniteLegale,activitePrincipaleUniteLegale,nomenclatureActivitePrincipaleUniteLegale,nicSiegeUniteLegale,economieSocialeSolidaireUniteLegale,caractereEmployeurUniteLegale) FROM '/var/lib/postgresql/files/StockUniteLegale_utf8.csv' delimiter ',' CSV HEADER ENCODING 'UTF8';" 2 | 3 | 4 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE INDEX siren_siren ON siren (siren);" 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Etalab 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /aio/aio-proxy/aio_proxy/routes.py: -------------------------------------------------------------------------------- 1 | from aiohttp import web 2 | 3 | 4 | ESCAPED_CHARS = "!@#$\'\"()/&`\\§°*£%+=_;.,?:<>" 5 | 6 | 7 | TYPEVOIES = ['promenade','avenue','boulevard','place', 'carrefour', 'passage', 'allee', 'impasse', 'lieu dit', 'hameau', 'rue'] 8 | 9 | routes = web.RouteTableDef() 10 | 11 | @routes.get('/siren') 12 | async def search_endpoint(request): 13 | output_url = request.app['config']['output_url_siren'] 14 | try: 15 | page_ask = request.rel_url.query['page'] 16 | except: 17 | page_ask = 1 18 | 19 | json_body = { 20 | 'siren_search': request.rel_url.query['q'], 21 | 'page_ask': page_ask 22 | } 23 | 24 | async with request.app['http_session'].post(output_url, json=json_body) as resp: 25 | res_status = resp.status 26 | res = await resp.json() 27 | 28 | return web.json_response(res, status=res_status) 29 | 30 | 31 | 32 | @routes.get('/siret') 33 | async def search_endpoint(request): 34 | output_url = request.app['config']['output_url_siret'] 35 | 36 | json_body = { 37 | 'siret_search': request.rel_url.query['q'] 38 | } 39 | 40 | async with request.app['http_session'].post(output_url, json=json_body) as resp: 41 | res_status = resp.status 42 | res = await resp.json() 43 | 44 | return web.json_response(res, status=res_status) 45 | -------------------------------------------------------------------------------- /api/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM debian:buster 3 | 4 | ARG POSTGREST_VERSION 5 | 6 | # Install postgrest 7 | RUN BUILD_DEPS="curl ca-certificates xz-utils" && \ 8 | apt-get -qq update && \ 9 | apt-get -qq install -y --no-install-recommends $BUILD_DEPS && \ 10 | cd /tmp && \ 11 | curl -SLO https://github.com/PostgREST/postgrest/releases/download/${POSTGREST_VERSION}/postgrest-${POSTGREST_VERSION}-linux-x64-static.tar.xz && \ 12 | tar -xJvf postgrest-${POSTGREST_VERSION}-linux-x64-static.tar.xz && \ 13 | mv postgrest /usr/local/bin/postgrest && \ 14 | cd / && \ 15 | apt-get -qq purge --auto-remove -y $BUILD_DEPS && \ 16 | apt-get -qq clean && \ 17 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 18 | 19 | RUN apt-get update && apt-get install -y curl 20 | 21 | RUN apt-get update && apt-get install -y gnupg2 22 | 23 | RUN echo 'deb http://apt.postgresql.org/pub/repos/apt/ buster-pgdg main 12' $PG_MAJOR > /etc/apt/sources.list.d/pgdg.list 24 | RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8 25 | 26 | RUN apt-get update && apt-get install -y postgresql-client-12 27 | 28 | 29 | COPY postgrest.conf /etc/postgrest.conf 30 | 31 | ENV PGRST_DB_URI= \ 32 | PGRST_DB_SCHEMA=public \ 33 | PGRST_DB_ANON_ROLE= \ 34 | PGRST_DB_POOL=100 \ 35 | PGRST_DB_POOL_TIMEOUT=1 \ 36 | PGRST_DB_EXTRA_SEARCH_PATH=public \ 37 | PGRST_SERVER_HOST=*4 \ 38 | PGRST_SERVER_PORT=3000 \ 39 | PGRST_OPENAPI_SERVER_PROXY_URI= \ 40 | PGRST_JWT_SECRET= \ 41 | PGRST_SECRET_IS_BASE64=false \ 42 | PGRST_JWT_AUD= \ 43 | PGRST_MAX_ROWS= \ 44 | PGRST_PRE_REQUEST= \ 45 | PGRST_ROLE_CLAIM_KEY=".role" \ 46 | PGRST_ROOT_SPEC= \ 47 | PGRST_RAW_MEDIA_TYPES= 48 | 49 | RUN groupadd -g 1000 postgrest && \ 50 | useradd -r -u 1000 -g postgrest postgrest && \ 51 | chown postgrest:postgrest /etc/postgrest.conf 52 | 53 | USER 1000 54 | 55 | # PostgREST reads /etc/postgrest.conf so map the configuration 56 | # file in when you run this container 57 | EXPOSE 3000 58 | 59 | HEALTHCHECK --interval=1s --timeout=3s \ 60 | CMD PGPASSWORD=$POSTGRES_PASSWORD psql -h $POSTGRES_DB_HOST -p $POSTGRES_DB_PORT -U $POSTGRES_USER -d $POSTGRES_DB -c "select * from etablissements_siren limit 1;" 61 | CMD exec postgrest /etc/postgrest.conf 62 | -------------------------------------------------------------------------------- /db/init/60-enrich-columns.sh: -------------------------------------------------------------------------------- 1 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "ALTER TABLE siren ADD COLUMN enseignes TEXT;" 2 | 3 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "UPDATE siren S SET enseignes = (SELECT STRING_AGG(enseignes,', ') AS enseignes FROM (select DISTINCT ST.enseigne1etablissement as enseignes from siret ST where ST.siren = S.siren AND ST.enseigne1etablissement IS NOT NULL) tbl);" 4 | 5 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "DROP VIEW IF EXISTS intermediary_view" 6 | 7 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "ALTER TABLE siren ADD COLUMN etablissements TEXT;" 8 | 9 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "UPDATE siren S SET etablissements = (SELECT STRING_AGG (siret, ',') AS etablissements from (SELECT siret FROM siret where siren = S.siren LIMIT 10) tbl);" 10 | 11 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "ALTER TABLE siren ADD COLUMN nombre_etablissements INTEGER;" 12 | 13 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "UPDATE siren S SET nombre_etablissements = (SELECT COUNT(*) AS nombre_etablissements from siret where siren = S.siren);" 14 | 15 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "ALTER TABLE siren ADD COLUMN nom_complet TEXT;" 16 | 17 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "UPDATE siren S SET nom_complet = (SELECT 18 | CASE WHEN S.categoriejuridiqueunitelegale = '1000' THEN 19 | CASE WHEN S.sigleunitelegale IS NOT NULL THEN 20 | COALESCE(LOWER(S.prenom1unitelegale),'') || COALESCE(' ' || LOWER(S.nomunitelegale),'') || COALESCE(' (' || LOWER(S.sigleunitelegale) || ')','') 21 | ELSE 22 | COALESCE(LOWER(S.prenom1unitelegale),'') || COALESCE(' ' || LOWER(S.nomunitelegale),'') 23 | END 24 | ELSE 25 | CASE WHEN S.sigleunitelegale IS NOT NULL THEN 26 | COALESCE(LOWER(S.denominationunitelegale),'') || COALESCE(LOWER(' (' || S.sigleunitelegale || ')'),'') 27 | ELSE 28 | COALESCE(LOWER(S.denominationunitelegale),'') 29 | END 30 | END 31 | );" 32 | 33 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "ALTER TABLE siren ADD COLUMN nom_url TEXT;" 34 | 35 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "UPDATE siren S SET nom_url = regexp_replace(nom_complet || '-' || siren, '[^a-zA-Z0-9]+', '-','g');" 36 | 37 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "ALTER TABLE siren ADD COLUMN numero_tva_intra TEXT;" 38 | 39 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "UPDATE siren S SET numero_tva_intra = (SELECT CASE WHEN tvanumber < 10 THEN concat('FR0',tvanumber,siren) ELSE CONCAT('FR',tvanumber,siren) END FROM (SELECT (12+(3*bigintsiren)%97)%97 as tvanumber, siren FROM (select CAST (siren as BIGINT) as bigintsiren,siren from siren WHERE siren = S.siren) tbl) tbl2);" 40 | 41 | -------------------------------------------------------------------------------- /utils/server.nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | server { 4 | listen 80; 5 | server_name recherche.entreprise.dataeng.etalab.studio; 6 | 7 | location /search { 8 | set $temp $arg_q; 9 | set $temp2 $arg_page; 10 | set $temp3 $arg_per_page; 11 | proxy_method POST; 12 | proxy_set_body '{ "search": "$temp","page_ask": "$temp2","per_page_ask": "$temp3" }'; 13 | proxy_pass http://localhost:3000/rpc/get_unite_legale; 14 | 15 | set $args ''; 16 | add_header Access-Control-Allow-Headers Content-Type; 17 | proxy_set_header X-Real-IP $remote_addr; 18 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 19 | proxy_connect_timeout 150; 20 | proxy_send_timeout 100; 21 | proxy_read_timeout 100; 22 | proxy_buffers 4 32k; 23 | client_max_body_size 8m; 24 | client_body_buffer_size 128k; 25 | } 26 | 27 | 28 | location /siren { 29 | set $temp $arg_q; 30 | proxy_method POST; 31 | proxy_set_body '{ "siren_search": "$temp" }'; 32 | proxy_pass http://localhost:3000/rpc/get_etablissements; 33 | 34 | set $args ''; 35 | add_header Access-Control-Allow-Headers Content-Type; 36 | proxy_set_header X-Real-IP $remote_addr; 37 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 38 | proxy_connect_timeout 150; 39 | proxy_send_timeout 100; 40 | proxy_read_timeout 100; 41 | proxy_buffers 4 32k; 42 | client_max_body_size 8m; 43 | client_body_buffer_size 128k; 44 | } 45 | 46 | 47 | location /autocomplete { 48 | 49 | if ($args ~ "^q=(.+)") { 50 | set $key1 $1; 51 | proxy_pass http://localhost:3000/unitelegale_view?select=nom_raison_sociale&nom_raison_sociale=like.$key1*&limit=10; 52 | } 53 | 54 | add_header Access-Control-Allow-Headers Content-Type; 55 | proxy_pass http://localhost:3000; 56 | proxy_set_header X-Real-IP $remote_addr; 57 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 58 | proxy_connect_timeout 150; 59 | proxy_send_timeout 100; 60 | proxy_read_timeout 100; 61 | proxy_buffers 4 32k; 62 | client_max_body_size 8m; 63 | client_body_buffer_size 128k; 64 | } 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /db/init/65-create-agg-table.sh: -------------------------------------------------------------------------------- 1 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c " 2 | CREATE TABLE siren_full 3 | AS ( 4 | SELECT 5 | T.activiteprincipaleetablissement as activite_principale, 6 | N.activiteprincipaleunitelegale as activite_principale_entreprise, 7 | N.statutdiffusionunitelegale as statut_unite_legale, 8 | T.activiteprincipaleregistremetiersetablissement as activite_principale_registre_metier, 9 | N. categorieentreprise as categorie_entreprise, 10 | T.codecedexetablissement as cedex, 11 | T.codepostaletablissement as code_postal, 12 | T.datecreationetablissement as date_creation, N.datecreationunitelegale as date_creation_entreprise, 13 | T.datedebut as date_debut_activite, 14 | N.datederniertraitementunitelegale as date_mise_a_jour, 15 | T.enseigne1etablissement as enseigne, 16 | T.geo_adresse, 17 | T.geo_id, 18 | T.geo_l4, 19 | T.geo_l5, 20 | T.geo_ligne, 21 | T.geo_score, 22 | T.geo_type, 23 | T.etablissementsiege as is_siege, 24 | T.latitude, 25 | T.libellecommuneetablissement as libelle_commune, 26 | T.libellevoieetablissement as libelle_voie, 27 | T.indicerepetitionetablissement as indice_repetition, 28 | T.longitude, 29 | N.categoriejuridiqueunitelegale as nature_juridique_entreprise, 30 | T.nic, 31 | N.nicsiegeunitelegale as nic_siege, 32 | N.nomunitelegale as nom, 33 | N.denominationunitelegale as nom_raison_sociale, 34 | T.numerovoieetablissement as numero_voie, 35 | N.prenom1unitelegale as prenom, 36 | N.sigleunitelegale as sigle, 37 | N.siren, 38 | T.siret, 39 | T.trancheeffectifsetablissement as tranche_effectif_salarie, 40 | N.trancheeffectifsunitelegale as tranche_effectif_salarie_entreprise, 41 | T.typevoieetablissement as type_voie, 42 | T.codecommuneetablissement as commune, 43 | T.etatadministratifetablissement as etat_administratif_etablissement, 44 | N.enseignes, 45 | N.nombre_etablissements, 46 | N.etablissements, 47 | N.nom_complet, 48 | N.nom_url, 49 | N.numero_tva_intra, 50 | N.economieSocialeSolidaireUniteLegale, 51 | N.identifiantAssociationUniteLegale 52 | FROM siret T 53 | LEFT JOIN siren N 54 | ON N.siren = T.siren 55 | WHERE T.etablissementsiege = 't' 56 | );" 57 | 58 | 59 | 60 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE INDEX siren_full_siren ON siren_full (siren);" 61 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE INDEX siren_full_siret ON siren_full (siret);" 62 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE INDEX siren_full_commune ON siren_full (commune);" 63 | 64 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE INDEX siren_full_nom_raison_sociale ON siren_full USING gin (nom_raison_sociale gin_trgm_ops);" 65 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE INDEX siren_full_activite_principale ON siren_full (activite_principale);" 66 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE INDEX siren_full_nature_juridique_entreprise ON siren_full (nature_juridique_entreprise);" 67 | 68 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "DROP TABLE siren;" -------------------------------------------------------------------------------- /db/init/40-populate-siret.sh: -------------------------------------------------------------------------------- 1 | 2 | for d in `seq -w 1 19` 2A 2B `seq 21 74` `seq 76 95` 98 ""; do 3 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "\copy siret(siren, nic, siret, statutDiffusionEtablissement, dateCreationEtablissement, trancheEffectifsEtablissement, anneeEffectifsEtablissement, activitePrincipaleRegistreMetiersEtablissement, dateDernierTraitementEtablissement, etablissementSiege, nombrePeriodesEtablissement, complementAdresseEtablissement, numeroVoieEtablissement, indiceRepetitionEtablissement, typeVoieEtablissement, libelleVoieEtablissement, codePostalEtablissement, libelleCommuneEtablissement, libelleCommuneEtrangerEtablissement, distributionSpecialeEtablissement, codeCommuneEtablissement, codeCedexEtablissement, libelleCedexEtablissement, codePaysEtrangerEtablissement, libellePaysEtrangerEtablissement, complementAdresse2Etablissement, numeroVoie2Etablissement, indiceRepetition2Etablissement, typeVoie2Etablissement, libelleVoie2Etablissement, codePostal2Etablissement, libelleCommune2Etablissement, libelleCommuneEtranger2Etablissement, distributionSpeciale2Etablissement, codeCommune2Etablissement, codeCedex2Etablissement, libelleCedex2Etablissement, codePaysEtranger2Etablissement, libellePaysEtranger2Etablissement, dateDebut, etatAdministratifEtablissement, enseigne1Etablissement, enseigne2Etablissement, enseigne3Etablissement, denominationUsuelleEtablissement, activitePrincipaleEtablissement, nomenclatureActivitePrincipaleEtablissement, caractereEmployeurEtablissement, longitude, latitude, geo_score, geo_type, geo_adresse, geo_id, geo_ligne, geo_l4, geo_l5) FROM '/var/lib/postgresql/files/geo_siret_"$d".csv' delimiter ',' csv header encoding 'UTF8';" 4 | echo "POPULATE dep "$d" OK" 5 | done 6 | 7 | 8 | for d in `seq -w 1 20`; do 9 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "\copy siret(siren, nic, siret, statutDiffusionEtablissement, dateCreationEtablissement, trancheEffectifsEtablissement, anneeEffectifsEtablissement, activitePrincipaleRegistreMetiersEtablissement, dateDernierTraitementEtablissement, etablissementSiege, nombrePeriodesEtablissement, complementAdresseEtablissement, numeroVoieEtablissement, indiceRepetitionEtablissement, typeVoieEtablissement, libelleVoieEtablissement, codePostalEtablissement, libelleCommuneEtablissement, libelleCommuneEtrangerEtablissement, distributionSpecialeEtablissement, codeCommuneEtablissement, codeCedexEtablissement, libelleCedexEtablissement, codePaysEtrangerEtablissement, libellePaysEtrangerEtablissement, complementAdresse2Etablissement, numeroVoie2Etablissement, indiceRepetition2Etablissement, typeVoie2Etablissement, libelleVoie2Etablissement, codePostal2Etablissement, libelleCommune2Etablissement, libelleCommuneEtranger2Etablissement, distributionSpeciale2Etablissement, codeCommune2Etablissement, codeCedex2Etablissement, libelleCedex2Etablissement, codePaysEtranger2Etablissement, libellePaysEtranger2Etablissement, dateDebut, etatAdministratifEtablissement, enseigne1Etablissement, enseigne2Etablissement, enseigne3Etablissement, denominationUsuelleEtablissement, activitePrincipaleEtablissement, nomenclatureActivitePrincipaleEtablissement, caractereEmployeurEtablissement, longitude, latitude, geo_score, geo_type, geo_adresse, geo_id, geo_ligne, geo_l4, geo_l5) FROM '/var/lib/postgresql/files/geo_siret_751"$d".csv' delimiter ',' csv header encoding 'UTF8';" 10 | echo "POPULATE dep 751"$d" OK" 11 | done 12 | 13 | for d in `seq -w 1 8`; do 14 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "\copy siret(siren, nic, siret, statutDiffusionEtablissement, dateCreationEtablissement, trancheEffectifsEtablissement, anneeEffectifsEtablissement, activitePrincipaleRegistreMetiersEtablissement, dateDernierTraitementEtablissement, etablissementSiege, nombrePeriodesEtablissement, complementAdresseEtablissement, numeroVoieEtablissement, indiceRepetitionEtablissement, typeVoieEtablissement, libelleVoieEtablissement, codePostalEtablissement, libelleCommuneEtablissement, libelleCommuneEtrangerEtablissement, distributionSpecialeEtablissement, codeCommuneEtablissement, codeCedexEtablissement, libelleCedexEtablissement, codePaysEtrangerEtablissement, libellePaysEtrangerEtablissement, complementAdresse2Etablissement, numeroVoie2Etablissement, indiceRepetition2Etablissement, typeVoie2Etablissement, libelleVoie2Etablissement, codePostal2Etablissement, libelleCommune2Etablissement, libelleCommuneEtranger2Etablissement, distributionSpeciale2Etablissement, codeCommune2Etablissement, codeCedex2Etablissement, libelleCedex2Etablissement, codePaysEtranger2Etablissement, libellePaysEtranger2Etablissement, dateDebut, etatAdministratifEtablissement, enseigne1Etablissement, enseigne2Etablissement, enseigne3Etablissement, denominationUsuelleEtablissement, activitePrincipaleEtablissement, nomenclatureActivitePrincipaleEtablissement, caractereEmployeurEtablissement, longitude, latitude, geo_score, geo_type, geo_adresse, geo_id, geo_ligne, geo_l4, geo_l5) FROM '/var/lib/postgresql/files/geo_siret_97"$d".csv' delimiter ',' csv header encoding 'UTF8';" 15 | echo "POPULATE dep 97"$d" OK" 16 | done 17 | 18 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE INDEX siret_siret ON siret (siret);" 19 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE INDEX siret_siren ON siret (siren);" 20 | 21 | -------------------------------------------------------------------------------- /db/init/20-postgresql_setup.sh: -------------------------------------------------------------------------------- 1 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE EXTENSION pg_trgm;" 2 | 3 | 4 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "ALTER SYSTEM SET max_wal_size = '30GB';" 5 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "ALTER SYSTEM SET work_mem = '16GB';" 6 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "SELECT * FROM pg_reload_conf();" 7 | 8 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE TABLE siren 9 | ( 10 | id_siren SERIAL PRIMARY KEY NOT NULL, 11 | siren CHARACTER VARYING, 12 | statutDiffusionUniteLegale CHARACTER VARYING, 13 | unitePurgeeUniteLegale CHARACTER VARYING, 14 | dateCreationUniteLegale date default NULL, 15 | sigleUniteLegale CHARACTER VARYING, 16 | sexeUniteLegale CHARACTER VARYING, 17 | prenom1UniteLegale CHARACTER VARYING, 18 | prenom2UniteLegale CHARACTER VARYING, 19 | prenom3UniteLegale CHARACTER VARYING, 20 | prenom4UniteLegale CHARACTER VARYING, 21 | prenomUsuelUniteLegale CHARACTER VARYING, 22 | pseudonymeUniteLegale CHARACTER VARYING, 23 | identifiantAssociationUniteLegale CHARACTER VARYING, 24 | trancheEffectifsUniteLegale CHARACTER VARYING, 25 | anneeEffectifsUniteLegale CHARACTER VARYING, 26 | dateDernierTraitementUniteLegale date default NULL, 27 | nombrePeriodesUniteLegale CHARACTER VARYING, 28 | categorieEntreprise CHARACTER VARYING, 29 | anneeCategorieEntreprise CHARACTER VARYING, 30 | dateDebut date default NULL, 31 | etatAdministratifUniteLegale CHARACTER VARYING, 32 | nomUniteLegale CHARACTER VARYING, 33 | nomUsageUniteLegale CHARACTER VARYING, 34 | denominationUniteLegale CHARACTER VARYING, 35 | denominationUsuelle1UniteLegale CHARACTER VARYING, 36 | denominationUsuelle2UniteLegale CHARACTER VARYING, 37 | denominationUsuelle3UniteLegale CHARACTER VARYING, 38 | categorieJuridiqueUniteLegale CHARACTER VARYING, 39 | activitePrincipaleUniteLegale CHARACTER VARYING, 40 | nomenclatureActivitePrincipaleUniteLegale CHARACTER VARYING, 41 | nicSiegeUniteLegale CHARACTER VARYING, 42 | economieSocialeSolidaireUniteLegale CHARACTER VARYING, 43 | caractereEmployeurUniteLegale CHARACTER VARYING, 44 | UNIQUE(siren) 45 | ) 46 | TABLESPACE pg_default;" 47 | 48 | 49 | psql -U sirene -d sirene -c "CREATE TABLE siret 50 | ( 51 | id_siret SERIAL PRIMARY KEY NOT NULL, 52 | siren CHARACTER VARYING, 53 | nic CHARACTER VARYING, 54 | siret CHARACTER VARYING, 55 | statutDiffusionEtablissement CHARACTER VARYING, 56 | dateCreationEtablissement date default NULL, 57 | trancheEffectifsEtablissement CHARACTER VARYING, 58 | anneeEffectifsEtablissement DECIMAL(9,2), 59 | activitePrincipaleRegistreMetiersEtablissement CHARACTER VARYING, 60 | dateDernierTraitementEtablissement TIMESTAMP default NULL, 61 | etablissementSiege BOOLEAN, 62 | nombrePeriodesEtablissement DECIMAL(9,2), 63 | complementAdresseEtablissement CHARACTER VARYING, 64 | numeroVoieEtablissement CHARACTER VARYING, 65 | indiceRepetitionEtablissement CHARACTER VARYING, 66 | typeVoieEtablissement CHARACTER VARYING, 67 | libelleVoieEtablissement CHARACTER VARYING, 68 | codePostalEtablissement CHARACTER VARYING, 69 | libelleCommuneEtablissement CHARACTER VARYING, 70 | libelleCommuneEtrangerEtablissement CHARACTER VARYING, 71 | distributionSpecialeEtablissement CHARACTER VARYING, 72 | codeCommuneEtablissement CHARACTER VARYING, 73 | codeCedexEtablissement CHARACTER VARYING, 74 | libelleCedexEtablissement CHARACTER VARYING, 75 | codePaysEtrangerEtablissement CHARACTER VARYING, 76 | libellePaysEtrangerEtablissement CHARACTER VARYING, 77 | complementAdresse2Etablissement CHARACTER VARYING, 78 | numeroVoie2Etablissement CHARACTER VARYING, 79 | indiceRepetition2Etablissement CHARACTER VARYING, 80 | typeVoie2Etablissement CHARACTER VARYING, 81 | libelleVoie2Etablissement CHARACTER VARYING, 82 | codePostal2Etablissement CHARACTER VARYING, 83 | libelleCommune2Etablissement CHARACTER VARYING, 84 | libelleCommuneEtranger2Etablissement CHARACTER VARYING, 85 | distributionSpeciale2Etablissement CHARACTER VARYING, 86 | codeCommune2Etablissement CHARACTER VARYING, 87 | codeCedex2Etablissement CHARACTER VARYING, 88 | libelleCedex2Etablissement CHARACTER VARYING, 89 | codePaysEtranger2Etablissement CHARACTER VARYING, 90 | libellePaysEtranger2Etablissement CHARACTER VARYING, 91 | dateDebut date default NULL, 92 | etatAdministratifEtablissement CHARACTER VARYING, 93 | enseigne1Etablissement CHARACTER VARYING, 94 | enseigne2Etablissement CHARACTER VARYING, 95 | enseigne3Etablissement CHARACTER VARYING, 96 | denominationUsuelleEtablissement CHARACTER VARYING, 97 | activitePrincipaleEtablissement CHARACTER VARYING, 98 | nomenclatureActivitePrincipaleEtablissement CHARACTER VARYING, 99 | caractereEmployeurEtablissement CHARACTER VARYING, 100 | longitude NUMERIC(14, 11), 101 | latitude NUMERIC(14, 11), 102 | geo_score DECIMAL(9,2), 103 | geo_type CHARACTER VARYING, 104 | geo_adresse CHARACTER VARYING, 105 | geo_id CHARACTER VARYING, 106 | geo_ligne CHARACTER VARYING, 107 | geo_l4 CHARACTER VARYING, 108 | geo_l5 CHARACTER VARYING, 109 | typecom CHARACTER VARYING 110 | ) 111 | TABLESPACE pg_default;" 112 | 113 | 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gestion de backend pour la base SIRENE Open Data 2 | 3 | Ce repo a pour objectif de proposer une architecture simplifiée pour la mise en API de la base open data SIRENE de l'INSEE. 4 | 5 | ## Architecture 6 | 7 | L'architecture proposée est basée sur Docker (docker-compose). Deux services y sont proposés : 8 | - db : base de données Postgresql contenant l'ensemble des données de la base SIRENE 9 | - postgrest : couche au-dessus de la base postgres qui propose un requêtage API sur la base simplifiée. ([voir documentation](http://postgrest.org/)) 10 | 11 | La base SIRENE open data se mettant à jour une fois par mois, il est proposé d'articuler ces services avec le reverse proxy Traefik pour limiter le temps d'indisponibilité du backend lors des migrations d'une ancienne base de données vers une nouvelle base de données. 12 | 13 | Le workflow est donc le suivant : 14 | - Exécution des services ```db``` et ```postgrest``` sur un réseau docker dédié (les services mettent environ ~35 minutes pour se lancer complètement) 15 | - Exposition du point d'API via un routing de ```traefik``` vers le service ```postgrest``` 16 | - Au 1er du mois, exécution de nouveaux services ```db``` et ```postgrest``` via le script ```deploy.sh```. Tant que les services ne sont pas opérationnels (cad tant que les données ne sont pas complètement chargées dans le service ```db```), Traefik ne reroute pas vers le nouveau service ```postgrest``` 17 | - Une fois les nouveaux services ```db``` et ```postgrest``` prêts, on déconnecte leurs précédentes versions 18 | 19 | ### Services 20 | 21 | #### service db 22 | 23 | Le service db est basé sur l'image docker postgresql 12. 24 | 25 | Au démarrage du service, un certain nombre de scripts contenu dans ```db/init/``` sont exécutés : 26 | - 00-get-last-siren-data.sh : téléchargement des derniers fichiers Siren 27 | - 10-get-last-siret-data.sh : téléchargement des derniers fichiers Siret 28 | - 20-postgresql_setup.sh : Création de la base de données, de l'utilisateur principal et des deux tables ```siren``` et ```siret``` 29 | - 30-populate-siren.sh : Chargement des données SIREN dans la table ```siren``` 30 | - 40-populate-siret.sh : Chargement des données SIRET dans la table ```siret``` 31 | - 50-enrich-columns.sh : Ajout de colonnes enrichies dans la table siren ```tsv``` (tsvector pour la recherche), ```etablissements```(liste partielle des établissements d'une unité légale) ; ```nombre_etablissements``` (nombre total d'établissements par unité légale) 32 | - 60-create-view-and-function.sh : 33 | - Création d'une vue ```etablissements_view``` permettant de requêter tous les établissements et comprenant des éléments au niveau unité légale. 34 | - Création d'une vue ```unitelegale_view``` permettant de requêter toutes les unités légales et comprenant des éléments au niveau établissements sièges. 35 | - Création d'une fonction ```get_unite_legale``` permettant de renvoyer une réponse adaptée lors de la recherche d'une unité légale (utilisé pour l'API principale du backend) 36 | 37 | #### service postgrest 38 | 39 | Le service postgrest permet de s'interfacer avec la base postgresql et offre une interface d'API sur celle-ci. 40 | 41 | Les labels suivants permettent à Traefik de rerouter le traffic vers le port 3000 du container : 42 | ``` 43 | labels: 44 | - "traefik.enable=true" 45 | - "traefik.http.routers.postgrest.rule=Host(`localhost`)" 46 | - "traefik.http.routers.postgrest.entrypoints=postgrest" 47 | - "traefik.port=3000" 48 | ``` 49 | 50 | Un Healthcheck est ajouté afin d'indiquer à Traefik (via les docker Healthcheck) si le container est prêt ou non. Le healthcheck consiste à vérifier si la view etablissements_view est disponible ou non (étape réalisée à la fin de la préparation du container ```db```): 51 | 52 | ``` 53 | HEALTHCHECK --interval=1s --timeout=3s \ 54 | CMD PGPASSWORD=$POSTGRES_PASSWORD psql -h $POSTGRES_DB_HOST -p 5432 -U $POSTGRES_USER -d $POSTGRES_DB -c "select * from etablissements_view limit 1;" 55 | ``` 56 | 57 | #### script deploy.sh 58 | 59 | Ce script permet de gérer les différents jeux de données d'un mois à l'autre. Un environnement courant est taggé avec la couleur ```blue``` (ou ```green```). Le script détecte le tag courant et lance un docker-compose avec un tag de la couleur opposée Une fois les services ```up``` et ```healthy```, le script coupe l'ancien service. 60 | 61 | #### conf Nginx 62 | 63 | Dans le dossier ```utils``` est déposé un exemple de configuration nginx permettant de créer des routes au-dessus de postgrest. Dans ce backend, on retrouve trois routes principales : 64 | 65 | Recherche plain text d'une entreprise. Attention, le paramètre q ne doit pas comporter d'accent : 66 | ``` 67 | http://recherche.entreprise.dataeng.etalab.studio/search?q=la%20poste&page=1&per_page=10 68 | 69 | # 3 paramètres : 70 | # q : recherche plain text 71 | # page : page désirée 72 | ## per_page : nombre de résultats par page désiré 73 | ``` 74 | 75 | Recherche des établissements d'un siren : 76 | ``` 77 | http://recherche.entreprise.dataeng.etalab.studio/siren?q=130025265 78 | 79 | # 1 paramètre : 80 | # q : siren rencherché 81 | ``` 82 | 83 | Recherche des unités légales commençant par un string (utilisé pour l'autocomplete) : 84 | ``` 85 | http://recherche.entreprise.dataeng.etalab.studio/autocomplete?q=DIRECTION%20INTERMI 86 | 87 | # 1 paramètre : 88 | # q : string recherchée 89 | ``` 90 | 91 | 92 | ## Installation 93 | 94 | 1. Clone du repo 95 | 96 | 2. Mise en place de Traefik et création d'un réseau docker dédié 97 | 98 | ``` 99 | docker-compose -f docker-compose-traefik.yml up --build -d 100 | ``` 101 | 102 | 3. Changement des valeurs des variables d'environnement dans les fichiers ```docker-compose-blue.yml``` et ```docker-compose-green.yml```. Mettre les valeurs de votre choix pour les services ```db``` et ```postgres```: 103 | 104 | ``` 105 | ... 106 | db-: 107 | ... 108 | environment: 109 | - POSTGRES_DB=sirene 110 | - POSTGRES_USER=sirene 111 | - POSTGRES_PASSWORD=sirene 112 | postgrest: 113 | ... 114 | environment: 115 | - POSTGRES_DB_HOST=db-blue 116 | - POSTGRES_DB=sirene 117 | - POSTGRES_USER=sirene 118 | - POSTGRES_PASSWORD=sirene 119 | ... 120 | ``` 121 | 122 | 4. Première mise en production du backend 123 | 124 | ``` 125 | docker-compose -f docker-compose-blue.yml build --no-cache 126 | docker-compose -f docker-compose-blue.yml --project-name=blue up --build -d 127 | ``` 128 | 129 | 5. Accès à l'API 130 | 131 | Vous pouvez accéder à l'API via http://IP:3000/ et requêter la base via la [documentation postgrest](http://postgrest.org/). 132 | Exemple : 133 | ``` 134 | http://localhost:3000/etablissements_view?tsv=plfts.boucherie&limit=100 # Retourne les 100 premiers résultats de boucherie dans la base 135 | ``` 136 | 137 | 6. Lorsqu'une nouvelle version des données est disponible : 138 | 139 | ``` 140 | ./deploy.sh 141 | ``` 142 | -------------------------------------------------------------------------------- /db/init/70-create-view-and-functions.sh: -------------------------------------------------------------------------------- 1 | 2 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "DROP VIEW IF EXISTS etablissements_view" 3 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c " 4 | CREATE VIEW etablissements_view AS 5 | SELECT 6 | T.activiteprincipaleetablissement as activite_principale, 7 | N.activite_principale_entreprise, 8 | T.activiteprincipaleregistremetiersetablissement as activite_principale_registre_metier, 9 | N.categorie_entreprise, 10 | T.codecedexetablissement as cedex, 11 | T.codepostaletablissement as code_postal, 12 | T.datecreationetablissement as date_creation, 13 | N.date_creation_entreprise, 14 | T.datedebut as date_debut_activite, 15 | N.date_mise_a_jour, 16 | T.enseigne1etablissement as enseigne, 17 | T.geo_adresse, 18 | T.geo_id, 19 | T.geo_l4, 20 | T.geo_l5, 21 | T.geo_ligne, 22 | T.geo_score, 23 | T.geo_type, 24 | T.etablissementsiege as is_siege, 25 | T.latitude, 26 | T.libellecommuneetablissement as libelle_commune, 27 | T.libellevoieetablissement as libelle_voie, 28 | T.indicerepetitionetablissement as indice_repetition, 29 | T.longitude, 30 | N.nature_juridique_entreprise, 31 | T.nic, 32 | N.nic_siege, 33 | N.nom, 34 | N.nom_raison_sociale, 35 | T.numerovoieetablissement as numero_voie, 36 | N.prenom, 37 | N.sigle, 38 | N.siren, 39 | T.siret, 40 | T.trancheeffectifsetablissement as tranche_effectif_salarie, 41 | N.tranche_effectif_salarie_entreprise, 42 | T.typevoieetablissement as type_voie, 43 | T.codecommuneetablissement as commune, 44 | T.etatadministratifetablissement as etat_administratif_etablissement, 45 | N.economieSocialeSolidaireUniteLegale, 46 | N.identifiantAssociationUniteLegale 47 | FROM siret T 48 | LEFT JOIN siren_full N 49 | ON N.siren = T.siren;" 50 | 51 | 52 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c " 53 | CREATE VIEW etablissements_siren AS 54 | SELECT 55 | T.activiteprincipaleetablissement as activite_principale, 56 | N.activite_principale_entreprise, 57 | T.activiteprincipaleregistremetiersetablissement as activite_principale_registre_metier, 58 | N.categorie_entreprise, 59 | T.codecedexetablissement as cedex, 60 | T.codepostaletablissement as code_postal, 61 | T.datecreationetablissement as date_creation, 62 | N.date_creation_entreprise, 63 | T.datedebut as date_debut_activite, 64 | N.date_mise_a_jour, 65 | T.enseigne1etablissement as enseigne, 66 | T.geo_adresse, 67 | T.geo_id, 68 | T.geo_l4, 69 | T.geo_l5, 70 | T.geo_ligne, 71 | T.geo_score, 72 | T.geo_type, 73 | T.etablissementsiege as is_siege, 74 | T.latitude, 75 | T.libellecommuneetablissement as libelle_commune, 76 | T.libellevoieetablissement as libelle_voie, 77 | T.indicerepetitionetablissement as indice_repetition, 78 | T.longitude, 79 | N.nature_juridique_entreprise, 80 | T.nic, 81 | N.nic_siege, 82 | N.nom, 83 | N.nom_raison_sociale, 84 | T.numerovoieetablissement as numero_voie, 85 | N.prenom, 86 | N.sigle, 87 | N.siren, 88 | T.siret, 89 | T.trancheeffectifsetablissement as tranche_effectif_salarie, 90 | N.tranche_effectif_salarie_entreprise, 91 | T.typevoieetablissement as type_voie, 92 | T.codecommuneetablissement as commune, 93 | T.etatadministratifetablissement as etat_administratif_etablissement, 94 | N.nombre_etablissements, 95 | N.nom_complet, 96 | N.nom_url, 97 | N.numero_tva_intra, 98 | N.economieSocialeSolidaireUniteLegale, 99 | N.identifiantAssociationUniteLegale 100 | FROM siret T 101 | LEFT JOIN siren_full N 102 | ON N.siren = T.siren;" 103 | 104 | 105 | 106 | 107 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE OR REPLACE FUNCTION get_etablissements (siren_search text, page_ask text) 108 | returns table ( 109 | unite_legale jsonb 110 | ) 111 | language plpgsql 112 | as \$\$ 113 | DECLARE 114 | nbent INTEGER := (SELECT nombre_etablissements FROM siren_full WHERE siren = siren_search); 115 | maxent INTEGER := 200; 116 | BEGIN 117 | IF (nbent < maxent) THEN 118 | return query 119 | SELECT 120 | jsonb_agg( 121 | json_build_object( 122 | 'activite_principale', t.activite_principale, 123 | 'activite_principale_entreprise', t.activite_principale_entreprise, 124 | 'activite_principale_registre_metier', t.activite_principale_registre_metier, 125 | 'categorie_entreprise', t.categorie_entreprise, 126 | 'cedex', t.cedex, 127 | 'code_postal', t.code_postal, 128 | 'date_creation', t.date_creation, 129 | 'date_creation_entreprise', t.date_creation_entreprise, 130 | 'date_debut_activite', t.date_debut_activite, 131 | 'date_mise_a_jour', t.date_mise_a_jour, 132 | 'enseigne', t.enseigne, 133 | 'geo_adresse', t.geo_adresse, 134 | 'geo_id', t.geo_id, 135 | 'geo_l4', t.geo_l4, 136 | 'geo_l5', t.geo_l5, 137 | 'geo_ligne', t.geo_ligne, 138 | 'geo_score', t.geo_score, 139 | 'geo_type', t.geo_type, 140 | 'is_siege', t.is_siege, 141 | 'latitude', t.latitude, 142 | 'libelle_commune', t.libelle_commune, 143 | 'libelle_voie', t.libelle_voie, 144 | 'longitude', t.longitude, 145 | 'nature_juridique_entreprise', t.nature_juridique_entreprise, 146 | 'nic', t.nic, 147 | 'nic_siege', t.nic_siege, 148 | 'nom', t.nom, 149 | 'nom_raison_sociale', t.nom_raison_sociale, 150 | 'numero_voie', t.numero_voie, 151 | 'prenom', t.prenom, 152 | 'sigle', t.sigle, 153 | 'siren', t.siren, 154 | 'siret', t.siret, 155 | 'tranche_effectif_salarie', t.tranche_effectif_salarie, 156 | 'tranche_effectif_salarie_entreprise', t.tranche_effectif_salarie_entreprise, 157 | 'type_voie', t.type_voie, 158 | 'commune', t.commune, 159 | 'nombre_etablissements', t.nombre_etablissements, 160 | 'etat_administratif_etablissement', t.etat_administratif_etablissement, 161 | 'nom_complet', t.nom_complet, 162 | 'nom_url', t.nom_url, 163 | 'etablissements', t.etablissements_array, 164 | 'etablissement_siege', t.etablissement_siege, 165 | 'numero_tva_intra', t.numero_tva_intra, 166 | 'economieSocialeSolidaireUniteLegale', t.economieSocialeSolidaireUniteLegale, 167 | 'identifiantAssociationUniteLegale', t.identifiantAssociationUniteLegale 168 | ) 169 | ) as unite_legale 170 | FROM 171 | ( 172 | SELECT 173 | ul.*, 174 | (SELECT json_agg(t1) FROM (SELECT * from etablissements_siren WHERE siren = siren_search ORDER BY is_siege DESC,etat_administratif_etablissement) t1) as etablissements_array, 175 | (SELECT json_agg(t2) FROM (SELECT * from etablissements_siren WHERE siren = siren_search AND is_siege = 't') t2) as etablissement_siege 176 | FROM 177 | siren_full ul 178 | WHERE 179 | ul.siren = siren_search 180 | ) t; 181 | ELSE 182 | return query 183 | SELECT 184 | jsonb_agg( 185 | json_build_object( 186 | 'activite_principale', t.activite_principale, 187 | 'activite_principale_entreprise', t.activite_principale_entreprise, 188 | 'activite_principale_registre_metier', t.activite_principale_registre_metier, 189 | 'categorie_entreprise', t.categorie_entreprise, 190 | 'cedex', t.cedex, 191 | 'code_postal', t.code_postal, 192 | 'date_creation', t.date_creation, 193 | 'date_creation_entreprise', t.date_creation_entreprise, 194 | 'date_debut_activite', t.date_debut_activite, 195 | 'date_mise_a_jour', t.date_mise_a_jour, 196 | 'enseigne', t.enseigne, 197 | 'geo_adresse', t.geo_adresse, 198 | 'geo_id', t.geo_id, 199 | 'geo_l4', t.geo_l4, 200 | 'geo_l5', t.geo_l5, 201 | 'geo_ligne', t.geo_ligne, 202 | 'geo_score', t.geo_score, 203 | 'geo_type', t.geo_type, 204 | 'is_siege', t.is_siege, 205 | 'latitude', t.latitude, 206 | 'libelle_commune', t.libelle_commune, 207 | 'libelle_voie', t.libelle_voie, 208 | 'longitude', t.longitude, 209 | 'nature_juridique_entreprise', t.nature_juridique_entreprise, 210 | 'nic', t.nic, 211 | 'nic_siege', t.nic_siege, 212 | 'nom', t.nom, 213 | 'nom_raison_sociale', t.nom_raison_sociale, 214 | 'numero_voie', t.numero_voie, 215 | 'prenom', t.prenom, 216 | 'sigle', t.sigle, 217 | 'siren', t.siren, 218 | 'siret', t.siret, 219 | 'tranche_effectif_salarie', t.tranche_effectif_salarie, 220 | 'tranche_effectif_salarie_entreprise', t.tranche_effectif_salarie_entreprise, 221 | 'type_voie', t.type_voie, 222 | 'commune', t.commune, 223 | 'nombre_etablissements', t.nombre_etablissements, 224 | 'etat_administratif_etablissement', t.etat_administratif_etablissement, 225 | 'nom_complet', t.nom_complet, 226 | 'nom_url', t.nom_url, 227 | 'etablissements', t.etablissements_array, 228 | 'etablissement_siege', t.etablissement_siege, 229 | 'numero_tva_intra', t.numero_tva_intra, 230 | 'economieSocialeSolidaireUniteLegale', t.economieSocialeSolidaireUniteLegale, 231 | 'identifiantAssociationUniteLegale', t.identifiantAssociationUniteLegale 232 | ) 233 | ) as unite_legale 234 | FROM 235 | ( 236 | SELECT 237 | ul.*, 238 | (SELECT json_agg(t1) FROM (SELECT * from etablissements_siren WHERE siren = siren_search LIMIT maxent OFFSET (CAST(page_ask AS INTEGER)-1)*maxent) t1) as etablissements_array, 239 | (SELECT json_agg(t2) FROM (SELECT * from etablissements_siren WHERE siren = siren_search AND is_siege = 't') t2) as etablissement_siege 240 | FROM 241 | siren_full ul 242 | WHERE 243 | ul.siren = siren_search 244 | ) t; 245 | END IF; 246 | end;\$\$;" 247 | 248 | 249 | 250 | psql -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE OR REPLACE FUNCTION get_etablissement (siret_search text) 251 | returns table ( 252 | etablissement jsonb 253 | ) 254 | language plpgsql 255 | as \$\$ 256 | BEGIN 257 | return query 258 | SELECT 259 | jsonb_agg( 260 | json_build_object( 261 | 262 | 'activite_principale', t.activite_principale, 263 | 'activite_principale_entreprise', t.activite_principale_entreprise, 264 | 'activite_principale_registre_metier', t.activite_principale_registre_metier, 265 | 'categorie_entreprise', t.categorie_entreprise, 266 | 'cedex', t.cedex, 267 | 'code_postal', t.code_postal, 268 | 'date_creation', t.date_creation, 269 | 'date_creation_entreprise', t.date_creation_entreprise, 270 | 'date_debut_activite', t.date_debut_activite, 271 | 'date_mise_a_jour', t.date_mise_a_jour, 272 | 'enseigne', t.enseigne, 273 | 'geo_adresse', t.geo_adresse, 274 | 'geo_id', t.geo_id, 275 | 'geo_l4', t.geo_l4, 276 | 'geo_l5', t.geo_l5, 277 | 'geo_ligne', t.geo_ligne, 278 | 'geo_score', t.geo_score, 279 | 'geo_type', t.geo_type, 280 | 'is_siege', t.is_siege, 281 | 'latitude', t.latitude, 282 | 'libelle_commune', t.libelle_commune, 283 | 'libelle_voie', t.libelle_voie, 284 | 'indice_repetition', t.indice_repetition, 285 | 'longitude', t.longitude, 286 | 'nature_juridique_entreprise', t.nature_juridique_entreprise, 287 | 'nic', t.nic, 288 | 'nic_siege', t.nic_siege, 289 | 'nom', t.nom, 290 | 'nom_raison_sociale', t.nom_raison_sociale, 291 | 'numero_voie', t.numero_voie, 292 | 'prenom', t.prenom, 293 | 'sigle', t.sigle, 294 | 'siren', t.siren, 295 | 'siret', t.siret, 296 | 'tranche_effectif_salarie', t.tranche_effectif_salarie, 297 | 'tranche_effectif_salarie_entreprise', t.tranche_effectif_salarie_entreprise, 298 | 'type_voie', t.type_voie, 299 | 'commune', t.commune, 300 | 'etat_administratif_etablissement', t.etat_administratif_etablissement, 301 | 'unite_legale', t.unite_legale, 302 | 'economieSocialeSolidaireUniteLegale', t.economieSocialeSolidaireUniteLegale, 303 | 'identifiantAssociationUniteLegale', t.identifiantAssociationUniteLegale 304 | ) 305 | ) as etablissement 306 | FROM 307 | ( 308 | SELECT 309 | ev.*, 310 | (SELECT * FROM get_etablissements(ev.siren, '1') t1) as unite_legale 311 | FROM 312 | etablissements_view ev 313 | WHERE 314 | ev.siret = siret_search 315 | ) t; 316 | end;\$\$;" 317 | --------------------------------------------------------------------------------