feat: Mise en place du projet

This commit is contained in:
Simon C 2024-02-08 12:28:51 +01:00
parent 10bc230e2e
commit 9b13a144e0
19 changed files with 368 additions and 369 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
node_modules
tmp
.env
initdb/*.csv

View File

@ -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

25
build.sh Normal file
View File

@ -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'

29
docker-compose.local.yml Normal file
View File

@ -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

View File

@ -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

View File

@ -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:
API_URL: ${POSTGREST_URL:-http://localhost:3000/}

View File

@ -1,3 +0,0 @@
body {
margin-bottom: 40px;
}

11
html/css/pure-min.css vendored

File diff suppressed because one or more lines are too long

View File

@ -1,43 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!-- css -->
<link rel="stylesheet" href="css/pure-min.css">
<link rel="stylesheet" href="css/app.css">
<!-- javascript -->
<script src="js/app.js"></script>
<title>World Database</title>
</head>
<body>
<div class="pure-g">
<div class="pure-u-1-8"></div>
<div class="pure-u-3-4">
<h1>World Database</h1>
<form class="pure-form" onsubmit="event.preventDefault(); app.searchClick()">
<fieldset>
<input id="city-input" type="text" placeholder="City" required>
<input type="submit" class="pure-button pure-button-primary" value="Search">
</fieldset>
</form>
<table class="pure-table pure-table-bordered pure-table-striped hidden" id="results-table">
<thead>
<th>Name</th>
<th>District</th>
<th>Country</th>
<th>Population</th>
</thead>
<tbody id="results-table-body">
</tbody>
</table>
</div>
<div class="pure-u-1-8"></div>
</div>
</body>
</html>

View File

@ -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 = '<tr><td colspan="4">Results not found</td></tr>';
}
document.getElementById('results-table-body').innerHTML = rowsString;
app.showElement('results-table');
}
app.buildResultRowString = function (row) {
return '<tr>' +
'<td>' + row.name + '</td>' +
'<td>' + row.district + '</td>' +
'<td>' + row.countrycode + '</td>' +
'<td>' + row.population + '</td>' +
'</tr>';
}
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);
}

View File

@ -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;

View File

@ -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;

77
initdb/4_sirene.sql Normal file
View File

@ -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;

View File

@ -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",

4
scripts/download.sh Executable file
View File

@ -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

View File

@ -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}`);
}
}

View File

@ -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

View File

@ -1,7 +0,0 @@
import { importFiness } from './helpers/finess.js'
async function main() {
importFiness()
}
main()

10
scripts/transform.sh Executable file
View File

@ -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/