Skip to content

Commit

Permalink
Merge branch 'staging'
Browse files Browse the repository at this point in the history
  • Loading branch information
annelhote committed Oct 5, 2023
2 parents bf3f757 + 13cd9d6 commit 471e86f
Show file tree
Hide file tree
Showing 63 changed files with 5,959 additions and 217 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

# misc
.env*
.DS_Store

# production
yarn.lock
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/resources.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Les resources de Paysage

L'accès aux resources se fait à l'adresse `api.paysage.staging.dataesr.ovh`, uniquement via le protocol HTTPS.
L'accès aux resources se fait à l'adresse `paysage-api.staging.dataesr.ovh`, uniquement via le protocol HTTPS.
Les requètes et les réponses se font toutes au format JSON.
Les routes suivent les principes de l'architecture REpresentational State Transfer (REST) et sont documentées via un document OpenAPI.

Expand Down
4,610 changes: 4,478 additions & 132 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"jsonwebtoken": "^8.5.1",
"mongodb": "^4.0.0",
"multer": "^1.4.5-lts.1",
"node-fetch": "^3.3.1",
"nodemailer": "^6.6.2",
"otplib": "^12.0.1",
"pkgcloud": "^2.2.0",
Expand Down
52 changes: 52 additions & 0 deletions scripts/import-geographical-categories-countries.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* eslint-disable max-len */
/* eslint-disable no-console */

/*
1. Récupérer toutes les catégories de level "pays" dans la collection geographicalcategories
2. Récupérer tous les pays dans le fichier countries.geo.json
3. Pour chaque pays du fichier countries.geo.json, s'il existe dans la collection geographicalcategories, mettre à jour sinon ajouter
*/

// Lancement : NODE_ENV=development MONGO_URI="mongodb://localhost:27017" MONGO_DBNAME="paysage" node --experimental-specifier-resolution=node scripts/import-geographical-categories-countries.js
import 'dotenv/config';
import worldGeoJSON from './countries.geo.json' assert { type: "json" };

import { client, db } from '../src/services/mongo.service';
import BaseMongoCatalog from '../src/api/commons/libs/base.mongo.catalog';

const MONGO_TARGET_COLLECTION_NAME = 'geographicalcategories';

async function getPaysageIds(existingIdsCount) {
const catalog = new BaseMongoCatalog({ db, collection: '_catalog' });
return Promise.all(
worldGeoJSON.features.slice(0, worldGeoJSON.features.length - existingIdsCount).map(() => catalog.getUniqueId(MONGO_TARGET_COLLECTION_NAME, 5)),
);
}

async function getCountriesToUpgrade() {
return db.collection(MONGO_TARGET_COLLECTION_NAME).find({ level: 'country' }).toArray();
}

async function treatment() {
const countriesToUpgrade = await getCountriesToUpgrade();

// Get paysage ids
const ids = await getPaysageIds(countriesToUpgrade.length);

const promises = worldGeoJSON.features.map((country, index) => ({
geometry: country.geometry,
id: countriesToUpgrade.find((item) => item.originalId === country.properties.iso_a3)?.id || ids[index],
level: 'country',
nameFr: country.properties.name_fr,
originalId: country.properties.iso_a3,
})).map((geo) => (
db.collection(MONGO_TARGET_COLLECTION_NAME).updateOne({ originalId: geo.originalId }, { $set: geo }, { upsert: true })
));

await Promise.all(promises);
}

console.log('--- START ---');
await treatment();
await client.close();
console.log('--- END ---');
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Example : NODE_ENV=development MONGO_URI="mongodb://localhost:27017" MONGO_DBNAME="paysage" node --experimental-specifier-resolution=node scripts/import-geographical-categories-regions-departments-academies.js
// source regions: https://data.opendatasoft.com/explore/dataset/georef-france-region%40public/export/?disjunctive.reg_name
// Source departments: https://data.opendatasoft.com/explore/dataset/georef-france-departement%40public/table/?disjunctive.reg_name&disjunctive.dep_name&sort=year
import 'dotenv/config';

import { client, db } from '../src/services/mongo.service';
import BaseMongoCatalog from '../src/api/commons/libs/base.mongo.catalog';

const MONGO_SOURCE_COLLECTION_NAME = 'geocodes';
const MONGO_TARGET_COLLECTION_NAME = 'geographicalcategories';

import regions from './data/regions.geo.json' assert { type: "json" };
import departments from './data/departments.geo.json' assert { type: "json" };
import academies from './data/fr-en-contour-academies-2020.geo.json' assert { type: "json" };

const configs = [
{
data: regions,
geoCodeField: 'reg_code',
level: 'region',
prefix: 'R',
sourceIdField: 'reg_id',
sourceIdLength: 2,
sourceNameField: 'reg_nom',
},
{
data: departments,
geoCodeField: 'dep_code',
level: 'department',
parentIdField: 'reg_id',
prefix: 'D',
sourceIdField: 'dep_id',
sourceIdLength: 3,
sourceNameField: 'dep_nom',
},
{
data: academies,
geoCodeField: 'code_academie',
level: 'academy',
parentIdField: 'reg_id',
prefix: 'A',
sourceIdField: 'aca_id',
sourceIdLength: 3,
sourceNameField: 'aca_nom',
},
];

async function treatment() {
const promises = configs.map(async (config) => {
const data = config.data;
// Load uniq from Mongo geocodes
const uniqueGeoIds = await db.collection(MONGO_SOURCE_COLLECTION_NAME).distinct(config.sourceIdField);
const uniqueGeos = await Promise.all(
uniqueGeoIds.map((uniqueId) => db.collection(MONGO_SOURCE_COLLECTION_NAME).findOne({ [config.sourceIdField]: uniqueId })),
);
// Generate as many ids as needed
const catalog = new BaseMongoCatalog({ db, collection: '_catalog' });
const allIds = await Promise.all(
uniqueGeos.map(() => catalog.getUniqueId(MONGO_TARGET_COLLECTION_NAME, 5)),
);
const p = uniqueGeos.map((geo, index) => {
if (config.level === 'academy') {
return {
geometry: data.features.find((geojson) => `A${geojson.properties.code_academie}` === geo[config.sourceIdField])?.geometry || null,
id: allIds[index],
level: config.level,
nameFr: geo[config.sourceNameField],
originalId: geo[config.sourceIdField],
parentOriginalId: geo[config.parentIdField],
};
}
return {
geometry: data.features.find((geojson) => `${config.prefix}${geojson.properties[config.geoCodeField][0].length < config.sourceIdLength ? '0' : ''}${geojson.properties[config.geoCodeField][0]}` === geo[config.sourceIdField])?.geometry || null,
id: allIds[index],
level: config.level,
nameFr: geo[config.sourceNameField],
originalId: geo[config.sourceIdField],
parentOriginalId: config.level === 'region' ? 'FRA' : geo[config.parentIdField],
};
}
).map((geo) => db.collection(MONGO_TARGET_COLLECTION_NAME).updateOne({ originalId: geo.originalId }, { $set: geo }, { upsert: true }));
await Promise.all(p);
});
await Promise.all(promises);
}

console.log('--- START ---');
await treatment();
await client.close();
console.log('--- END ---');

// TODO
// Ajouter les geojson
// Ajouter le parent direct
// Modifier l'API pour autoriser l'urban unitf
// Pays : https://data.enseignementsup-recherche.gouv.fr/explore/dataset/curiexplore-pays/table/?disjunctive.iso3&sort=iso3
// + ajouter lien vers la fichecurie
// pour une fiche pays ajouter les groupes d'appartenance (Bologne, UE27, EURO blabla)

// description
80 changes: 80 additions & 0 deletions scripts/import-geographical-categories-uu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import fs from 'fs';

import 'dotenv/config';
import refUU from './data/referentiel-geographique-francais-communes-unites-urbaines-aires-urbaines-depart.json' assert { type: "json" };

import { client, db } from '../src/services/mongo.service';
import BaseMongoCatalog from '../src/api/commons/libs/base.mongo.catalog';

const MONGO_TARGET_COLLECTION_NAME = 'geographicalcategories';

async function getUUToUpgrade() {
return db.collection(MONGO_TARGET_COLLECTION_NAME).find({ level: 'urbanUnity' }).toArray();
}

async function getPaysageIds(existingIdsCount, listUU) {
const catalog = new BaseMongoCatalog({ db, collection: '_catalog' });
return Promise.all(
Object.keys(listUU).slice(0, Object.keys(listUU).length - existingIdsCount).map(() => catalog.getUniqueId(MONGO_TARGET_COLLECTION_NAME, 5)),
);
}

async function treatment() {
const allUu = {};
refUU.forEach((el) => {
if (el.uu_code) {
if (!allUu[el.uu_code]) {
allUu[el.uu_code] = {
list: [{
code_com: el.com_code,
geometry: el.geom.geometry || null,
}],
uucr_nom: el.uucr_nom
}
} else {
allUu[el.uu_code].list.push({
code_com: el.com_code,
geometry: el.geom.geometry || null,
});
}
}
});

const uUToUpgrade = await getUUToUpgrade();

// Get paysage ids
const ids = await getPaysageIds(uUToUpgrade.length, allUu);
console.log(ids);
const promises = Object.keys(allUu).map((urbanUnity, index) => ({
geometry: { coordinates: allUu[urbanUnity].list.map((commune) => commune.geometry.coordinates[0]), type: "MultiPolygon" },
id: uUToUpgrade.find((item) => item.originalId === urbanUnity)?.id || ids[index],
level: 'urbanUnity',
nameFr: allUu[urbanUnity].uucr_nom,
originalId: urbanUnity,
})).map((geo) => (
db.collection(MONGO_TARGET_COLLECTION_NAME).updateOne({ originalId: geo.originalId }, { $set: geo }, { upsert: true })
));

await Promise.all(promises);
}

console.log('--- START ---');
await treatment();
await client.close();
console.log('--- END ---');


// {
// uu_code: {
// list: [
// {
// code_com: "ghjhg"
// geometry: {}
// }
// ],
// uucr_nom:
// }
// }


// urbanUnity
82 changes: 82 additions & 0 deletions src/api/annuaire/annuaire.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import express from 'express';
import { db } from '../../services/mongo.service';

const annuaire = db.collection('annuaire');
const rt = db.collection('relationtypes');

const router = new express.Router();

const lightProjection = {
_id: 0,
id: 1,
person: "$relatedObject.displayName",
personGender: "$relatedObject.gender",
structureName: "$resource.displayName",
startDate: 1,
endDate: 1,
status: 1,
active: 1,
endDatePrevisional: 1,
mandateEmail: 1,
mandateTemporary: 1,
personalEmail: 1,
mandatePhonenumber: 1,
mandatePrecision: 1,
personId: "$relatedObject.id",
structureId: "$resource.id",
relationType: {
$cond: {
if: { $eq: ["$relatedObject.gender", 'Homme'] },
then: "$relationType.maleName",
else: {
$cond: {
if: { $eq: ["$relatedObject.gender", 'Femme'] },
then: "$relationType.feminineName",
else: "$relationType.name"
}
}
}
},
}

router.get('/annuaire/aggregations', async (req, res) => {
const relationTypesDocs = await rt
.find({ for: "persons" })
.project({ _id: 0, name: 1, priority: { $ifNull: ['$priority', 99] } })
.toArray();
const relationTypes = relationTypesDocs.sort((a, b) => a.priority - b.priority).map(rt => rt.name);
const structures = await annuaire.distinct('resource.displayName');
const categories = await annuaire.distinct('resource.categories.usualNameFr');
const mandateTypeGroups = await annuaire.distinct('relationType.mandateTypeGroup');
return res.json({ relationTypes, structures, categories, mandateTypeGroups });
});

router.get('/annuaire', async (req, res) => {
const { relationType, structure, category, mandateTypeGroup, skip = "0", limit = "0" } = req.query;
const filters = {
...(relationType && { "relationType.name": { $in: relationType.split(',') } }),
...(structure && { "resource.displayName": { $in: structure.split(',') } }),
...(category && { "resource.categories.usualNameFr": { $in: category.split(',') } }),
...(mandateTypeGroup && { "relationType.mandateTypeGroup": { $in: mandateTypeGroup.split(',') } }),
};
const data = (parseInt(limit, 10) > 0)
? await annuaire.find(filters).project(lightProjection).skip(parseInt(skip, 10)).limit(parseInt(limit, 10)).sort({ startDate: -1 }).toArray()
: [];
const totalCount = await annuaire.countDocuments(filters);
return res.json({ data, totalCount });
});

router.get('/annuaire/export', async (req, res) => {
const { relationType, structure, category, mandateTypeGroup } = req.query;
const filters = {
...(relationType && { "relationType.name": { $in: relationType.split(',') } }),
...(structure && { "resource.displayName": { $in: structure.split(',') } }),
...(category && { "resource.categories.usualNameFr": { $in: category.split(',') } }),
...(mandateTypeGroup && { "relationType.mandateTypeGroup": { $in: mandateTypeGroup.split(',') } }),
};
const data = await annuaire.find(filters).toArray()
console.log(data);
return res.json(data);
});

export default router;
Loading

0 comments on commit 471e86f

Please sign in to comment.