From 9b13a144e07c444a43c0d72da306b1ef70b7112c Mon Sep 17 00:00:00 2001 From: Simon C Date: Thu, 8 Feb 2024 12:28:51 +0100 Subject: [PATCH] feat: Mise en place du projet --- .gitignore | 1 + README.md | 30 +- build.sh | 25 ++ docker-compose.local.yml | 29 ++ docker-compose.traefik.yml | 19 ++ docker-compose.yml | 67 +--- html/css/app.css | 3 - html/css/pure-min.css | 11 - html/index.html | 43 --- html/js/app.js | 53 ---- ...reate_finess_database.sql => 2_finess.sql} | 17 +- initdb/3_decoupage_administratif.sql | 18 ++ initdb/4_sirene.sql | 77 +++++ package.json | 5 + scripts/download.sh | 4 + scripts/helpers/finess.js | 295 +++++++----------- scripts/helpers/functions.js | 23 ++ scripts/import.js | 7 - scripts/transform.sh | 10 + 19 files changed, 368 insertions(+), 369 deletions(-) create mode 100644 build.sh create mode 100644 docker-compose.local.yml create mode 100644 docker-compose.traefik.yml delete mode 100644 html/css/app.css delete mode 100644 html/css/pure-min.css delete mode 100644 html/index.html delete mode 100644 html/js/app.js rename initdb/{2_create_finess_database.sql => 2_finess.sql} (82%) create mode 100644 initdb/3_decoupage_administratif.sql create mode 100644 initdb/4_sirene.sql create mode 100755 scripts/download.sh delete mode 100644 scripts/import.js create mode 100755 scripts/transform.sh diff --git a/.gitignore b/.gitignore index 2fb241b..a8b9b6e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules tmp .env +initdb/*.csv diff --git a/README.md b/README.md index eddc74d..c9745f4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Data Project +# Project Open Data Création d'une API unifié pour accéder à l'ensemble des données dont P4Pillon a besoin. @@ -26,23 +26,25 @@ Il y a 2 posibilités pour récupérer les données de l'ensemble des entreprise - depuis [des fichiers](https://www.data.gouv.fr/fr/datasets/base-sirene-des-entreprises-et-de-leurs-etablissements-siren-siret/) -## Script - -Nous mettons à disposition des scripts d'import des données de l'Open Data. - -``` -npm run install # Install les dépendances du projet -npm run download # Télécharge toutes les données -npm run transform # Transforme les données téléchargés dans un format que postgres pourra intégrer -``` - ## Composants logiciels du projet - Base de données : [PostgreSQL](https://www.postgresql.org/) - API : [PostgREST](https://postgrest.org/) ([Documentation](https://postgrest.org/en/stable/explanations/install.html#docker)) - UI : [Swagger UI](https://swagger.io/tools/swagger-ui/) +- Virtualisation : [Docker](https://docs.docker.com/) - Orchestrateur : [Docker Compose](https://docs.docker.com/compose/) + +## Script + +Un script permet d'initialiser le projet en téléchargeant les dépendences, les données de l'Open Data ainsi que de transformer ses données en fichiers CSV. + +Il faut avoir Docker d'installé et executer cette commande : + +```bash +bash build.sh +``` + ### Docker Compose Nous utilisons Docker Compose pour la mise en place des différents services. @@ -53,16 +55,16 @@ La configuration se fait à l'aide d'un fichier `.env`, vous devez modifier les Voici quelques commandes possibles : -``` +```bash docker compose up -d # Création des conteneurs docker compose down -v # Suppresion des conteneurs ainsi que des données ``` ### PostgreSQL -Nous utilisons la fonction +La fonction `COPY` est utilisé dans les fichiers SQL du dossier `./initdb` permettant d'importer les données d'un fichier CSV dans une base de données. ### PostgREST Pour la configuration : https://postgrest.org/en/latest/configuration.html#environment-variables -https://postgrest.org/en/stable/references/api/tables_views.html#horizontal-filtering-rows +Pour requêter des données : https://postgrest.org/en/stable/references/api/tables_views.html#horizontal-filtering-rows diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..ee2406f --- /dev/null +++ b/build.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Commande de base avec Docker +npm="docker run -it --rm --user $(id -u):$(id -g) -v $PWD:/home/node/app -w /home/node/app node:lts-alpine npm config set update-notifier false && npm" +ash="docker run -it --rm --user $(id -u):$(id -g) -v $PWD:/home/node/app -w /home/node/app apteno/alpine-jq ash" + +Suppression des fichiers +echo $'\n\n#\n# Suppression des fichiers 🧹\n#\n' +eval $npm run clean + +# Installation des dépendances +echo $'\n\n#\n# Installation des dépendances ⬇️\n#\n' +eval $npm install + +# Téléchargement des données +echo $'\n\n#\n# Téléchargement des données 💡\n#\n' +eval $npm run download +eval $ash ./scripts/download.sh + +# Transformation des données +echo $'\n\n#\n# Transformation des données 🏗️\n#\n' +eval $npm run transform +eval $ash ./scripts/transform.sh + +echo $'\n\n#\n# Le projet est prêt à être lancé avec Docker 🚀\n#\n' diff --git a/docker-compose.local.yml b/docker-compose.local.yml new file mode 100644 index 0000000..9e60f22 --- /dev/null +++ b/docker-compose.local.yml @@ -0,0 +1,29 @@ +--- + +version: "3.8" + +networks: + front-end: + back-end: + driver: bridge + +services: + + postgres: + ports: + - "5432:5432" + networks: + - back-end + + postgrest: + ports: + - "3000:3000" + networks: + - back-end + + swagger: + ports: + - "8080:8080" + networks: + - front-end + - back-end diff --git a/docker-compose.traefik.yml b/docker-compose.traefik.yml new file mode 100644 index 0000000..b29bddd --- /dev/null +++ b/docker-compose.traefik.yml @@ -0,0 +1,19 @@ +--- + +version: "3.8" + +networks: + traefik: + name: ${TRAEFIK_NETWORK_NAME:-traefik} + external: true + opendata: + name: ${OPENDATA_NETWORK_NAME:-opendata} + +services: + postgrest: + networks: + - traefik + + swagger: + networks: + - traefik \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 2a86ce9..70a15ec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,47 +8,10 @@ volumes: services: - postgrest: - container_name: ${POSTGREST_CONTAINER_NAME:-postgrest} - image: ${POSTGREST_IMAGE:-postgrest/postgrest:v12.0.0} - restart: always - ports: - - "3000:3000" - # Available environment variables documented here: - # https://postgrest.org/en/latest/configuration.html#environment-variables - environment: - # The standard connection URI format, documented at - # https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING - PGRST_DB_URI: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:?err} - # The name of which database schema to expose to REST clients - PGRST_DB_SCHEMA: ${DB_SCHEMA} - # The database role to use when no client authentication is provided - PGRST_DB_ANON_ROLE: ${DB_ANON_ROLE} - # Overrides the base URL used within the OpenAPI self-documentation hosted at the API root path - PGRST_OPENAPI_SERVER_PROXY_URI: http://localhost:3000 - depends_on: - - postgres - networks: - - back-end - - postgrest-demo: - container_name: postgrest-demo - image: nginx:mainline-alpine - ports: - - "80:80" - volumes: - # anything in html directory is hosted via nginx - - "./html:/usr/share/nginx/html" - restart: always - networks: - - back-end - postgres: container_name: ${POSTGRES_CONTAINER_NAME:-postgres} image: ${POSTGRES_IMAGE:-postgres:16.1-alpine} restart: always - ports: - - "5432:5432" environment: POSTGRES_USER: ${POSTGRES_USER:?err} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?err} @@ -60,21 +23,25 @@ services: - "./initdb:/docker-entrypoint-initdb.d" - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro - networks: - - back-end + + postgrest: + container_name: ${POSTGREST_CONTAINER_NAME:-postgrest} + image: ${POSTGREST_IMAGE:-postgrest/postgrest:v12.0.0} + restart: always + # https://postgrest.org/en/latest/configuration.html#environment-variables + environment: + PGRST_DB_URI: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:?err} + PGRST_DB_SCHEMA: ${DB_SCHEMA} + PGRST_DB_ANON_ROLE: ${DB_ANON_ROLE} + PGRST_OPENAPI_SERVER_PROXY_URI: ${POSTGREST_URL:-http://localhost:3000} + # https://postgrest.org/en/stable/references/configuration.html#db-max-rows + PGRST_DB_MAX_ROWS: ${PGRST_DB_MAX_ROWS:-100} + depends_on: + - postgres swagger: container_name: ${SWAGGER_CONTAINER_NAME:-swagger} - image: ${SWAGGER_IMAGE:-swaggerapi/swagger-ui:v5.10.3} + image: ${SWAGGER_IMAGE:-swaggerapi/swagger-ui:v5.11.0} restart: always - ports: - - "8080:8080" environment: - API_URL: http://localhost:3000/ - networks: - - front-end - - back-end - -networks: - front-end: - back-end: \ No newline at end of file + API_URL: ${POSTGREST_URL:-http://localhost:3000/} diff --git a/html/css/app.css b/html/css/app.css deleted file mode 100644 index 6b1eb6a..0000000 --- a/html/css/app.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - margin-bottom: 40px; -} \ No newline at end of file diff --git a/html/css/pure-min.css b/html/css/pure-min.css deleted file mode 100644 index 5b2aaf8..0000000 --- a/html/css/pure-min.css +++ /dev/null @@ -1,11 +0,0 @@ -/*! -Pure v2.0.6 -Copyright 2013 Yahoo! -Licensed under the BSD License. -https://github.com/pure-css/pure/blob/master/LICENSE -*/ -/*! -normalize.css v | MIT License | git.io/normalize -Copyright (c) Nicolas Gallagher and Jonathan Neal -*/ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}html{font-family:sans-serif}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{letter-spacing:-.31em;text-rendering:optimizespeed;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-line-pack:start;align-content:flex-start}@media all and (-ms-high-contrast:none),(-ms-high-contrast:active){table .pure-g{display:block}}.opera-only :-o-prefocus,.pure-g{word-spacing:-0.43em}.pure-u{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-g [class*=pure-u]{font-family:sans-serif}.pure-u-1,.pure-u-1-1,.pure-u-1-12,.pure-u-1-2,.pure-u-1-24,.pure-u-1-3,.pure-u-1-4,.pure-u-1-5,.pure-u-1-6,.pure-u-1-8,.pure-u-10-24,.pure-u-11-12,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-2-24,.pure-u-2-3,.pure-u-2-5,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24,.pure-u-3-24,.pure-u-3-4,.pure-u-3-5,.pure-u-3-8,.pure-u-4-24,.pure-u-4-5,.pure-u-5-12,.pure-u-5-24,.pure-u-5-5,.pure-u-5-6,.pure-u-5-8,.pure-u-6-24,.pure-u-7-12,.pure-u-7-24,.pure-u-7-8,.pure-u-8-24,.pure-u-9-24{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%}.pure-u-1-12,.pure-u-2-24{width:8.3333%}.pure-u-1-8,.pure-u-3-24{width:12.5%}.pure-u-1-6,.pure-u-4-24{width:16.6667%}.pure-u-1-5{width:20%}.pure-u-5-24{width:20.8333%}.pure-u-1-4,.pure-u-6-24{width:25%}.pure-u-7-24{width:29.1667%}.pure-u-1-3,.pure-u-8-24{width:33.3333%}.pure-u-3-8,.pure-u-9-24{width:37.5%}.pure-u-2-5{width:40%}.pure-u-10-24,.pure-u-5-12{width:41.6667%}.pure-u-11-24{width:45.8333%}.pure-u-1-2,.pure-u-12-24{width:50%}.pure-u-13-24{width:54.1667%}.pure-u-14-24,.pure-u-7-12{width:58.3333%}.pure-u-3-5{width:60%}.pure-u-15-24,.pure-u-5-8{width:62.5%}.pure-u-16-24,.pure-u-2-3{width:66.6667%}.pure-u-17-24{width:70.8333%}.pure-u-18-24,.pure-u-3-4{width:75%}.pure-u-19-24{width:79.1667%}.pure-u-4-5{width:80%}.pure-u-20-24,.pure-u-5-6{width:83.3333%}.pure-u-21-24,.pure-u-7-8{width:87.5%}.pure-u-11-12,.pure-u-22-24{width:91.6667%}.pure-u-23-24{width:95.8333%}.pure-u-1,.pure-u-1-1,.pure-u-24-24,.pure-u-5-5{width:100%}.pure-button{display:inline-block;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-group{letter-spacing:-.31em;text-rendering:optimizespeed}.opera-only :-o-prefocus,.pure-button-group{word-spacing:-0.43em}.pure-button-group .pure-button{letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:rgba(0,0,0,.8);border:none transparent;background-color:#e6e6e6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:focus,.pure-button:hover{background-image:-webkit-gradient(linear,left top,left bottom,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000}.pure-button-disabled,.pure-button-disabled:active,.pure-button-disabled:focus,.pure-button-disabled:hover,.pure-button[disabled]{border:none;background-image:none;opacity:.4;cursor:not-allowed;-webkit-box-shadow:none;box-shadow:none;pointer-events:none}.pure-button-hidden{display:none}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-button-group .pure-button{margin:0;border-radius:0;border-right:1px solid rgba(0,0,0,.2)}.pure-button-group .pure-button:first-child{border-top-left-radius:2px;border-bottom-left-radius:2px}.pure-button-group .pure-button:last-child{border-top-right-radius:2px;border-bottom-right-radius:2px;border-right:none}.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 3px #ddd;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 3px #ddd;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form input[type=color]{padding:.2em .5em}.pure-form input[type=color]:focus,.pure-form input[type=date]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=email]:focus,.pure-form input[type=month]:focus,.pure-form input[type=number]:focus,.pure-form input[type=password]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=text]:focus,.pure-form input[type=time]:focus,.pure-form input[type=url]:focus,.pure-form input[type=week]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129fea}.pure-form input:not([type]):focus{outline:0;border-color:#129fea}.pure-form input[type=checkbox]:focus,.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus{outline:thin solid #129FEA;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input[type=color][disabled],.pure-form input[type=date][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=email][disabled],.pure-form input[type=month][disabled],.pure-form input[type=number][disabled],.pure-form input[type=password][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=text][disabled],.pure-form input[type=time][disabled],.pure-form input[type=url][disabled],.pure-form input[type=week][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input:not([type])[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form select:focus:invalid,.pure-form textarea:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=checkbox]:focus:invalid:focus,.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input[type=color],.pure-form-stacked input[type=date],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=email],.pure-form-stacked input[type=file],.pure-form-stacked input[type=month],.pure-form-stacked input[type=number],.pure-form-stacked input[type=password],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=text],.pure-form-stacked input[type=time],.pure-form-stacked input[type=url],.pure-form-stacked input[type=week],.pure-form-stacked label,.pure-form-stacked select,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-stacked input:not([type]){display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned select,.pure-form-aligned textarea,.pure-form-message-inline{display:inline-block;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form .pure-input-rounded,.pure-form input.pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-3-4{width:75%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=color],.pure-group input[type=date],.pure-group input[type=datetime-local],.pure-group input[type=datetime],.pure-group input[type=email],.pure-group input[type=month],.pure-group input[type=number],.pure-group input[type=password],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=text],.pure-group input[type=time],.pure-group input[type=url],.pure-group input[type=week]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0 0}.pure-form-message,.pure-form-message-inline{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu{-webkit-box-sizing:border-box;box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-item,.pure-menu-list{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-heading,.pure-menu-link{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-separator{display:inline-block;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-active>.pure-menu-children,.pure-menu-allow-hover:hover>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;padding:.5em 0}.pure-menu-horizontal .pure-menu-children .pure-menu-separator,.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-horizontal .pure-menu-children .pure-menu-separator{display:block;width:auto}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-heading,.pure-menu-link{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent;cursor:default}.pure-menu-active>.pure-menu-link,.pure-menu-link:focus,.pure-menu-link:hover{background-color:#eee}.pure-menu-selected>.pure-menu-link,.pure-menu-selected>.pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0} \ No newline at end of file diff --git a/html/index.html b/html/index.html deleted file mode 100644 index d76f9ee..0000000 --- a/html/index.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - World Database - - -
-
-
-

World Database

-
-
- - -
-
- - - - - - - - - - - -
-
-
- - \ No newline at end of file diff --git a/html/js/app.js b/html/js/app.js deleted file mode 100644 index 96290e8..0000000 --- a/html/js/app.js +++ /dev/null @@ -1,53 +0,0 @@ -'use strict'; - -var CITY_QUERY_URL_PREFIX = 'http://localhost:3000/city?name=ilike.' - -var app = {}; - -app.addResultRows = function (rows) { - var rowsString = ''; - var rowsLength = rows.length; - - if (rowsLength > 0) { - for (var i = 0; i < rowsLength; i++) { - rowsString += app.buildResultRowString(rows[i]); - } - } else { - rowsString = 'Results not found'; - } - - document.getElementById('results-table-body').innerHTML = rowsString; - app.showElement('results-table'); -} - -app.buildResultRowString = function (row) { - return '' + - '' + row.name + '' + - '' + row.district + '' + - '' + row.countrycode + '' + - '' + row.population + '' + - ''; -} - -app.showElement = function (id) { - document.getElementById(id).classList.remove('hidden'); -} - -app.queryCity = function (cityName) { - var queryURL = CITY_QUERY_URL_PREFIX + '*' + cityName + '*'; - - fetch(queryURL) - .then(function(response) { - return response.json(); - }) - .then(function (j) { - console.log(j); - app.addResultRows(j); - }) -} - -app.searchClick = function () { - var city = document.getElementById('city-input').value; - - app.queryCity(city); -} \ No newline at end of file diff --git a/initdb/2_create_finess_database.sql b/initdb/2_finess.sql similarity index 82% rename from initdb/2_create_finess_database.sql rename to initdb/2_finess.sql index d123539..3d1f16f 100644 --- a/initdb/2_create_finess_database.sql +++ b/initdb/2_finess.sql @@ -1,13 +1,5 @@ --- --- PostgreSQL port of the MySQL "World" database. --- --- The sample data used in the world database is Copyrighted. --- Statistics Finland, http://www.stat.fi/worldinfigures --- BEGIN; -SET client_encoding = 'LATIN1'; - CREATE TABLE IF NOT EXISTS finess ( nofinesset VARCHAR(255), nofinessej VARCHAR(255), @@ -43,3 +35,12 @@ CREATE TABLE IF NOT EXISTS finess ( coordxet FLOAT DEFAULT 0, coordyet FLOAT DEFAULT 0 ); + +COPY finess FROM '/docker-entrypoint-initdb.d/finess.csv' DELIMITER ';' CSV HEADER; + +ALTER TABLE ONLY finess + ADD CONSTRAINT finess_pkey PRIMARY KEY (nofinesset); + +COMMIT; + +ANALYZE finess; \ No newline at end of file diff --git a/initdb/3_decoupage_administratif.sql b/initdb/3_decoupage_administratif.sql new file mode 100644 index 0000000..f149940 --- /dev/null +++ b/initdb/3_decoupage_administratif.sql @@ -0,0 +1,18 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS regions ( + code VARCHAR(3), + chefLieu VARCHAR(5), + nom VARCHAR(255), + typeLiaison VARCHAR(255), + region_zone VARCHAR(255) +); + +COPY regions FROM '/docker-entrypoint-initdb.d/regions.csv' DELIMITER ',' CSV; + +ALTER TABLE ONLY regions + ADD CONSTRAINT regions_pkey PRIMARY KEY (code); + +COMMIT; + +ANALYZE regions; \ No newline at end of file diff --git a/initdb/4_sirene.sql b/initdb/4_sirene.sql new file mode 100644 index 0000000..cdaecff --- /dev/null +++ b/initdb/4_sirene.sql @@ -0,0 +1,77 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS sirene_stock_etablissement ( + siren VARCHAR(9), + nic VARCHAR(5), + siret VARCHAR(14), + statutDiffusionEtablissement VARCHAR(1), + dateCreationEtablissement VARCHAR(19), + trancheEffectifsEtablissement VARCHAR(2), + anneeEffectifsEtablissement VARCHAR(19), + activitePrincipaleRegistreMetiersEtablissement VARCHAR(6), + dateDernierTraitementEtablissement VARCHAR(19), + etablissementSiege VARCHAR(5), + nombrePeriodesEtablissement NUMERIC(2), + complementAdresseEtablissement VARCHAR(38), + numeroVoieEtablissement VARCHAR(4), + indiceRepetitionEtablissement VARCHAR(4), + typeVoieEtablissement VARCHAR(4), + libelleVoieEtablissement VARCHAR(100), + codePostalEtablissement VARCHAR(5), + libelleCommuneEtablissement VARCHAR(100), + libelleCommuneEtrangerEtablissement VARCHAR(100), + distributionSpecialeEtablissement VARCHAR(26), + codeCommuneEtablissement VARCHAR(5), + codeCedexEtablissement VARCHAR(9), + libelleCedexEtablissement VARCHAR(100), + codePaysEtrangerEtablissement VARCHAR(5), + libellePaysEtrangerEtablissement VARCHAR(100), + complementAdresse2Etablissement VARCHAR(38), + numeroVoie2Etablissement VARCHAR(4), + indiceRepetition2Etablissement VARCHAR(4), + typeVoie2Etablissement VARCHAR(4), + libelleVoie2Etablissement VARCHAR(100), + codePostal2Etablissement VARCHAR(5), + libelleCommune2Etablissement VARCHAR(100), + libelleCommuneEtranger2Etablissement VARCHAR(100), + distributionSpeciale2Etablissement VARCHAR(26), + codeCommune2Etablissement VARCHAR(5), + codeCedex2Etablissement VARCHAR(9), + libelleCedex2Etablissement VARCHAR(100), + codePaysEtranger2Etablissement VARCHAR(5), + libellePaysEtranger2Etablissement VARCHAR(100), + dateDebut VARCHAR(19), + etatAdministratifEtablissement VARCHAR(1), + enseigne1Etablissement VARCHAR(50), + enseigne2Etablissement VARCHAR(50), + enseigne3Etablissement VARCHAR(50), + denominationUsuelleEtablissement VARCHAR(100), + activitePrincipaleEtablissement VARCHAR(6), + nomenclatureActivitePrincipaleEtablissement VARCHAR(8), + caractereEmployeurEtablissement VARCHAR(1) +); + + +COPY sirene_stock_etablissement FROM '/docker-entrypoint-initdb.d/StockEtablissement_utf8.csv' DELIMITER ',' CSV HEADER; + +-- Index sur la colonne siren +CREATE INDEX idx_siren ON sirene_stock_etablissement (siren); + +-- Index sur la colonne siret +CREATE INDEX idx_siret ON sirene_stock_etablissement (siret); + +-- Index sur la colonne codePostalEtablissement +CREATE INDEX idx_codePostalEtablissement ON sirene_stock_etablissement (codePostalEtablissement); + +-- Index sur la colonne libelleCommuneEtablissement +CREATE INDEX idx_libelleCommuneEtablissement ON sirene_stock_etablissement (libelleCommuneEtablissement); + +-- Index sur la colonne activitePrincipaleEtablissement +CREATE INDEX idx_activitePrincipaleEtablissement ON sirene_stock_etablissement (activitePrincipaleEtablissement); + +-- ALTER TABLE ONLY sirene_stock_etablissement +-- ADD CONSTRAINT sirene_stock_etablissement_pkey PRIMARY KEY (siren); + +COMMIT; + +ANALYZE sirene_stock_etablissement; \ No newline at end of file diff --git a/package.json b/package.json index 83d4454..8bcf699 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,10 @@ { "type": "module", + "scripts": { + "clean": "rm ./node_modules -rf && rm ./tmp -rf && rm ./initdb/*.csv -rf", + "download": "node scripts/download.js", + "transform": "node scripts/transform.js" + }, "dependencies": { "@etalab/decoupage-administratif": "^3.1.1", "csv-parse": "^5.5.3", diff --git a/scripts/download.sh b/scripts/download.sh new file mode 100755 index 0000000..caca8bc --- /dev/null +++ b/scripts/download.sh @@ -0,0 +1,4 @@ +#!/bin/ash + +echo $'- Téléchargement des données INPI' +wget https://www.data.gouv.fr/fr/datasets/r/0651fb76-bcf3-4f6a-a38d-bc04fa708576 -O ./tmp/StockEtablissement_utf8.zip diff --git a/scripts/helpers/finess.js b/scripts/helpers/finess.js index cdc9ff1..c0a56e2 100644 --- a/scripts/helpers/finess.js +++ b/scripts/helpers/finess.js @@ -1,10 +1,43 @@ import fs from 'fs'; import readline from 'readline'; -import pg from 'pg'; -import { downloadJSON, downloadAndSaveFile, filtrerEtEnregistrerLignes, loadEnvFile } from './functions.js' +import { downloadJSON, downloadAndSaveFile, filtrerEtEnregistrerLignes, loadEnvFile, customSplit } from './functions.js' import { transformToGPS } from './gps.js' -const { Pool } = pg +const columns = [ + 'nofinesset', + 'nofinessej', + 'rs', + 'rslongue', + 'complrs', + 'compldistrib', + 'numvoie', + 'typvoie', + 'voie', + 'compvoie', + 'lieuditbp', + 'commune', + 'departement', + 'libdepartement', + 'ligneacheminement', + 'telephone', + 'telecopie', + 'categetab', + 'libcategetab', + 'categagretab', + 'libcategagretab', + 'siret', + 'codeape', + 'codemft', + 'libmft', + 'codesph', + 'libsph', + 'dateouv', + 'dateautor', + 'datemaj', + 'numuai', + 'coordxet', + 'coordyet' +]; export const downloadFiness = async () => { const MAIN_URL = 'https://www.data.gouv.fr/api/1/datasets/finess-extraction-du-fichier-des-etablissements/' @@ -20,31 +53,26 @@ export const transformFiness = async () => { const fichierEntree = './tmp/finess.csv'; const fichierStructures = './tmp/finess_structureet.csv'; const fichierGeolocalisation = './tmp/finess_geolocalisation.csv'; + const fichierSortie = './initdb/finess.csv' const conditionFiltre = (ligne) => ligne.includes('structureet'); await filtrerEtEnregistrerLignes(fichierEntree, fichierStructures, fichierGeolocalisation, conditionFiltre); - await fusionnerCoordonneesGPSStructures(fichierStructures, fichierGeolocalisation, fichierEntree); -} - -export const importFiness = async () => { - // Exemple d'utilisation avec le nom du fichier CSV - const fichierEntree = './tmp/finess.csv'; - insererDonneesPostgres(fichierEntree); + await fusionnerCoordonneesGPSStructures(fichierStructures, fichierGeolocalisation, fichierSortie); } // Fonction pour fusionner les coordonnées GPS dans le fichier CSV de structures export async function fusionnerCoordonneesGPSStructures(fichierStructures, fichierCoordonnees, fichierSortie) { try { - // Créer un flux de lecture pour le fichier de coordonnées Lambert 93 + // Créer un flux de lecture pour le fichier de coordonnées const lecteurCoordonnees = readline.createInterface({ input: fs.createReadStream(fichierCoordonnees), crlfDelay: Infinity }); - // Créer un dictionnaire pour stocker les coordonnées Lambert 93 par structureid + // Créer un dictionnaire pour stocker les coordonnées par structureid const coordonnees = {}; - // Fonction pour traiter chaque ligne du fichier de coordonnées Lambert 93 + // Fonction pour traiter chaque ligne du fichier de coordonnées const traiterLigneCoordonnees = (ligne) => { // exemple de ligne // geolocalisation;970300802;317351.6;571220.2;2,ATLASANTE,100,IGN,BD_ADRESSE,V2.2,UTM_N22;2024-01-08 @@ -56,7 +84,7 @@ export async function fusionnerCoordonneesGPSStructures(fichierStructures, fichi coordonnees[structureid] = { system, coordX, coordY }; }; - // Lire chaque ligne du fichier de coordonnées Lambert 93 + // Lire chaque ligne du fichier de coordonnées for await (const ligne of lecteurCoordonnees) { traiterLigneCoordonnees(ligne); } @@ -70,193 +98,100 @@ export async function fusionnerCoordonneesGPSStructures(fichierStructures, fichi // Créer un flux d'écriture vers le fichier de sortie const fichierSortieStream = fs.createWriteStream(fichierSortie); - // Écrire l'entête dans le fichier de sortie - // fichierSortieStream.write('structureid,nofinesset,coordxet,coordyet,latitude,longitude\n'); + // Création de l'entête d'insertion + // fichierSortieStream.write('INSERT INTO finess (' + columns.join(', ') + ') VALUES\n'); + fichierSortieStream.write(columns.join(";") + '\n'); - // Fonction pour traiter chaque ligne du fichier de structures - const traiterLigneStructures = (ligne) => { + const ecrireLigneStructure = (ligne, isFirst) => { + let dataArray = ligne.split(';'); + + // Fix le nom d'une association contenant un ; ... "ASSO; LA RELÈVE" + if (dataArray.length > 34) { + dataArray = customSplit(ligne, ';') + dataArray = dataArray.map(value => value.replaceAll(";", "%3B")) + } + + dataArray.shift(); // Suppression du premier champs inutil + + // const defaultValue = (value) => (value === null || value === '' ? 'NULL' : `'${value.replaceAll("'", "''")}'`) + // const typedData = { + // nofinesset: defaultValue(dataArray[0]), + // nofinessej: defaultValue(dataArray[1]), + // rs: defaultValue(dataArray[2]), + // rslongue: defaultValue(dataArray[3]), + // complrs: defaultValue(dataArray[4]), + // compldistrib: defaultValue(dataArray[5]), + // numvoie: defaultValue(dataArray[6]), + // typvoie: defaultValue(dataArray[7]), + // voie: defaultValue(dataArray[8]), + // compvoie: defaultValue(dataArray[9]), + // lieuditbp: defaultValue(dataArray[10]), + // commune: defaultValue(dataArray[11]), + // departement: defaultValue(dataArray[12]), + // libdepartement: defaultValue(dataArray[13]), + // ligneacheminement: defaultValue(dataArray[14]), + // telephone: defaultValue(dataArray[15]), + // telecopie: defaultValue(dataArray[16]), + // categetab: defaultValue(dataArray[17]), + // libcategetab: defaultValue(dataArray[18]), + // categagretab: defaultValue(dataArray[19]), + // libcategagretab: defaultValue(dataArray[20]), + // siret: defaultValue(dataArray[21]), + // codeape: defaultValue(dataArray[22]), + // codemft: defaultValue(dataArray[23]), + // libmft: defaultValue(dataArray[24]), + // codesph: defaultValue(dataArray[25]), + // libsph: defaultValue(dataArray[26]), + // dateouv: defaultValue((new Date(dataArray[27] || '1900-01-01')).toISOString().split('T')[0]), // Utilise la date par défaut si la date est vide + // dateautor: defaultValue((new Date(dataArray[28] || '1900-01-01')).toISOString().split('T')[0]), + // datemaj: defaultValue((new Date(dataArray[29] || '1900-01-01')).toISOString().split('T')[0]), + // numuai: defaultValue(dataArray[30]), + // coordxet: dataArray[31] ? parseFloat(dataArray[31]) : 'NULL', + // coordyet: dataArray[32] ? parseFloat(dataArray[32]) : 'NULL', + // }; + // if (typedData.nofinesset === '690051545') { + // console.log(dataArray[20]) + // } + // const formattedData = Object.values(typedData).map((value) => (isNaN(value) ? `'${value.replaceAll("'", "''")}'` : (value === null || value === '' ? 'NULL' : value))); // Assurez-vous que les chaînes sont entourées de guillemets simples + // fichierSortieStream.write(`${isFirst ? '' : ',\n'} (${Object.values(typedData).join(', ')})`); + // fichierSortieStream.write('INSERT INTO finess (' + columns.join(', ') + ') VALUES ' + `(${Object.values(typedData).join(', ')});\n`); + fichierSortieStream.write(`${dataArray.join(';')}\n`); + } + + let isFirst = true + // Lire chaque ligne du fichier de structures + for await (const ligne of lecteurStructures) { const valeurs = ligne.split(';'); const structureid = valeurs[1]; - // Vérifier si des coordonnées Lambert 93 existent pour cette structureid + // Vérifier si des coordonnées existent pour cette structureid if (coordonnees.hasOwnProperty(structureid)) { const { system, coordX, coordY } = coordonnees[structureid]; const coordonneesGPS = transformToGPS(system, coordX, coordY); if (coordonneesGPS) { // Écrire les valeurs dans le fichier de sortie - fichierSortieStream.write(`${ligne};${coordonneesGPS.latitude};${coordonneesGPS.longitude}\n`); + ecrireLigneStructure(`${ligne};${coordonneesGPS.latitude};${coordonneesGPS.longitude}`, isFirst); } else { // Si aucune coordonnée n'est disponible, écrire la ligne telle quelle dans le fichier de sortie - fichierSortieStream.write(`${ligne};;\n`); + ecrireLigneStructure(`${ligne};;`, isFirst); } } else { // Si aucune coordonnée n'est disponible, écrire la ligne telle quelle dans le fichier de sortie - fichierSortieStream.write(`${ligne};;\n`); + ecrireLigneStructure(`${ligne};;`, isFirst); } - }; - - // Lire chaque ligne du fichier de structures - for await (const ligne of lecteurStructures) { - traiterLigneStructures(ligne); + isFirst = false } + // Fermer la parenthèse finale et le point-virgule + // fichierSortieStream.write(';'); + fichierSortieStream.end(); + + console.log(`Le fichier SQL a été généré : ${fichierSortie}`); + console.log('Fusion des coordonnées GPS dans le fichier de structures terminée.'); } catch (erreur) { console.error(`Erreur lors de la fusion des coordonnées GPS : ${erreur.message}`); } } - -// Fonction pour lire le fichier CSV et insérer les données dans PostgreSQL -export async function insererDonneesPostgres(nomFichierCSV) { - const envVariables = loadEnvFile() - - const pool = new Pool({ - user: envVariables.POSTGRES_USER, - host: 'localhost', - database: envVariables.POSTGRES_DB, - password: envVariables.POSTGRES_PASSWORD, - port: 5432, - max: 10, // Nombre maximum de connexions dans le pool - idleTimeoutMillis: 30000, // Délai d'attente maximum pour une connexion avant d'être libérée - }); - const client = await pool.connect(); - - const columns = [ - 'nofinesset', - 'nofinessej', - 'rs', - 'rslongue', - 'complrs', - 'compldistrib', - 'numvoie', - 'typvoie', - 'voie', - 'compvoie', - 'lieuditbp', - 'commune', - 'departement', - 'libdepartement', - 'ligneacheminement', - 'telephone', - 'telecopie', - 'categetab', - 'libcategetab', - 'categagretab', - 'libcategagretab', - 'siret', - 'codeape', - 'codemft', - 'libmft', - 'codesph', - 'libsph', - 'dateouv', - 'dateautor', - 'datemaj', - 'numuai', - 'coordxet', - 'coordyet' - ]; - - // Fonction pour insérer une ligne dans la base de données - async function insertRow(data) { - const query = { - text: `INSERT INTO finess (${columns.join(', ')}) VALUES (${columns.map((col, index) => `$${index + 1}`).join(', ')})`, - values: data, - }; - - const client = await pool.connect(); - - try { - await client.query(query); - } catch (error) { - console.log(data.join(', ')) - console.error('Erreur lors de l\'insertion de la ligne:', error.message || error); - } finally { - client.release(); // Libérer la connexion du pool - } - } - - // Fonction pour lire le fichier ligne par ligne et appeler la fonction d'insertion - async function processFile(filePath) { - const fileStream = fs.createReadStream(filePath); - const rl = readline.createInterface({ - input: fileStream, - crlfDelay: Infinity, - }); - - for await (const [index, line] of rl) { - // Supposons que les données dans le fichier soient séparées par des virgules (à ajuster selon votre format) - let data = line.split(';'); - data.shift(); - if (data[27] === '') data[27] = null - if (data[28] === '') data[28] = null - if (data[29] === '') data[29] = null - - // Appeler la fonction d'insertion avec la ligne de données - await insertRow(data); - console.log(index) - } - - // Fermer le pool de connexions à la base de données à la fin du traitement - await pool.end(); - } - - // await creerTableFINESS(client); - await processFile(nomFichierCSV) -} - -// Fonction pour créer la table FINESS dans PostgreSQL -export async function creerTableFINESS(client) { - try { - // Requête SQL de création de table - const requeteCreationTable = ` - CREATE TABLE IF NOT EXISTS finess ( - nofinesset VARCHAR(255), - nofinessej VARCHAR(255), - rs VARCHAR(255), - rslongue VARCHAR(255), - complrs VARCHAR(255), - compldistrib VARCHAR(255), - numvoie VARCHAR(255), - typvoie VARCHAR(255), - voie VARCHAR(255), - compvoie VARCHAR(255), - lieuditbp VARCHAR(255), - commune VARCHAR(255), - departement VARCHAR(255), - libdepartement VARCHAR(255), - ligneacheminement VARCHAR(255), - telephone VARCHAR(255), - telecopie VARCHAR(255), - categetab VARCHAR(255), - libcategetab VARCHAR(255), - categagretab VARCHAR(255), - libcategagretab VARCHAR(255), - siret VARCHAR(255), - codeape VARCHAR(255), - codemft VARCHAR(255), - libmft VARCHAR(255), - codesph VARCHAR(255), - libsph VARCHAR(255), - dateouv DATE DEFAULT '1900-01-01', - dateautor DATE DEFAULT '1900-01-01', - datemaj DATE DEFAULT '1900-01-01', - numuai VARCHAR(255), - coordxet FLOAT DEFAULT 0, - coordyet FLOAT DEFAULT 0, - PRIMARY KEY (nofinesset) - ); - `; - - // Exécution de la requête de création de table - await client.query(requeteCreationTable); - - console.log('Table FINESS créée avec succès.'); - - } catch (erreur) { - console.error(`Erreur lors de la création de la table : ${erreur.message}`); - } -} - diff --git a/scripts/helpers/functions.js b/scripts/helpers/functions.js index 8f5bfda..d8f83c4 100644 --- a/scripts/helpers/functions.js +++ b/scripts/helpers/functions.js @@ -3,6 +3,29 @@ import fs from 'fs' import { writeFile, mkdir } from 'fs/promises' import readline from 'readline'; +export function customSplit(line, separator) { + const parts = []; + let currentPart = ''; + let insideQuotes = false; + + for (let i = 0; i < line.length; i++) { + const char = line[i]; + + if (char === '"') { + insideQuotes = !insideQuotes; + } else if (char === separator && !insideQuotes) { + parts.push(currentPart.trim()); + currentPart = ''; + } else { + currentPart += char; + } + } + + parts.push(currentPart.trim()); + + return parts; +} + export const loadEnvFile = () => { // Charger les variables d'environnement à partir du fichier .env const envPath = './.env'; // Spécifiez le chemin correct si différent diff --git a/scripts/import.js b/scripts/import.js deleted file mode 100644 index 3fae873..0000000 --- a/scripts/import.js +++ /dev/null @@ -1,7 +0,0 @@ -import { importFiness } from './helpers/finess.js' - -async function main() { - importFiness() -} - -main() diff --git a/scripts/transform.sh b/scripts/transform.sh new file mode 100755 index 0000000..a00d0df --- /dev/null +++ b/scripts/transform.sh @@ -0,0 +1,10 @@ +#!/bin/ash + +echo $'- Transformation des données de régions' +cat node_modules/@etalab/decoupage-administratif/data/regions.json | jq -r '.[] | [.code, .chefLieu, .nom, .typeLiaison, .zone] | @csv' > initdb/regions.csv + +echo $'- Transformation des données de départements' +cat node_modules/@etalab/decoupage-administratif/data/departements.json | jq -r '.[] | [.code, .region, .chefLieu, .nom, .typeLiaison, .zone] | @csv' > initdb/departements.csv + +echo $'- Extraction des données INPI' +unzip -o ./tmp/StockEtablissement_utf8.zip -d ./initdb/