L'application d'intelligence territoriale pour une stratégie de logement adaptée, durable et inclusive
Le projet dispose d'une interface en ligne de commande intégrée dans l'application API.
# Depuis la racine du monorepo
pnpm -F api cli <commande>
# Ou depuis le dossier apps/api
cd apps/api && pnpm cli <commande>Restaure une sauvegarde de la base de données de production depuis Scalingo.
pnpm --filter @otelo/api cli import-backupFonctionnalités :
- Authentification automatique via l'API Scalingo avec échange de tokens
- Récupération de la dernière sauvegarde complète disponible
- Téléchargement et extraction des archives
.tar.gz - Suppression sécurisée des tables et enums existants
- Restauration via
pg_restoreavec gestion des erreurs non-critiques - Nettoyage automatique des fichiers temporaires
Variables d'environnement requises :
| Variable | Description |
|---|---|
SCALINGO_API_TOKEN |
Token d'authentification Scalingo |
SCALINGO_APP_NAME |
Nom de l'application sur Scalingo |
SCALINGO_ADDON_ID |
ID de l'addon PostgreSQL |
SCALINGO_DB_API_URL |
URL de l'API base de données |
SCALINGO_REGION |
Région Scalingo (ex: osc-fr1) |
DATABASE_URL |
URL de connexion PostgreSQL locale |
Recalcule et enrichit les résultats de simulation en base (métriques stock B11-B15, flux, sitadel, données par année).
Par défaut, la commande fonctionne en dry-run (aucune écriture en base). Il faut passer --write pour persister.
# Dry-run sur toutes les simulations (défaut, aucune écriture)
pnpm -F api cli recalculate-results
# Écriture en base pour toutes les simulations
pnpm -F api cli recalculate-results --write
# Dry-run sur une seule simulation
pnpm -F api cli recalculate-results --simulation-id <uuid>
# Écriture en base pour une seule simulation
pnpm -F api cli recalculate-results --simulation-id <uuid> --writeOptions :
| Option | Description |
|---|---|
--simulation-id <id> |
Recalculer une seule simulation |
--write |
Persister les résultats en base (sans ce flag = dry-run) |
Données calculées et stockées :
- Totaux agrégés par EPCI (total, flux, stock, pre/post-peak)
- Métriques stock B11-B15 par EPCI (hors logement, hébergés, inadéquation financière, mauvaise qualité, inadéquation physique)
- Totaux flux par EPCI (évolution démographique, renouvellement, résidences secondaires, vacance courte/longue durée)
- Données flux par année par EPCI (évolution du parc, besoins en logements, surplus)
- Données Sitadel par EPCI
- Historique complet du calcul (snapshot JSON dans
simulation_results_history)
Importe un fichier CSV dans une table de données versionnée par millésime. Génère du SQL INSERT ... ON CONFLICT DO NOTHING : les doublons sont ignorés, aucune donnée existante n'est écrasée.
# Afficher le SQL dans le terminal (review)
pnpm -F api cli import-csv --table rp --csv ./data/rp.csv --millesime 2024
# Écrire le SQL dans un fichier (pour copier dans pgAdmin/DBeaver)
pnpm -F api cli import-csv --table rp --csv ./data/rp.csv --millesime 2024 --output sql/import-rp.sql
# Exécuter directement en base locale (dev uniquement)
pnpm -F api cli import-csv --table rp --csv ./data/rp.csv --millesime 2024 --executeOptions :
| Option | Requis | Description |
|---|---|---|
--table <name> |
oui | Nom de la table PostgreSQL (ex: rp, sitadel, homeless) |
--csv <path> |
oui | Chemin vers le(s) fichier(s) CSV (répétable : --csv a.csv --csv b.csv) |
--millesime <value> |
non | Millésime à injecter (ex: 2024). Crée le DataPackVersion si inexistant |
--output <path> |
non | Écrire le SQL dans un fichier au lieu du terminal |
--execute |
non | Exécuter le SQL directement en base locale |
Tables autorisées :
rp, sitadel, demographic_evolution_omphale, demographic_evolution_population, household_sizes, vacancy_accommodation, filocom_flux, bad_quality_filocom, bad_quality_rp, bad_quality_fonciers, physical_inadequation_rp, physical_inadequation_filo, financial_inadequation, hosted_filocom, hosted_finess, hosted_sne, hotel, makeshift_housing_rp, makeshift_housing_sne, homeless, social_parc, data_pack_versions
Format CSV attendu :
- Séparateur : virgule
, - Première ligne : en-têtes (noms de colonnes)
- Encodage : UTF-8
- Les valeurs numériques sont préservées telles quelles (pas d'arrondi)
- Les cellules vides →
NULL - La virgule décimale française est convertie en point automatiquement
Mapping automatique des en-têtes :
Le script mappe les en-têtes CSV vers les colonnes PostgreSQL via plusieurs stratégies : match exact (epci_code), case-insensitive (Epci_Code), camelCase→snake_case (epciCode), et aliases courants (epci→epci_code, annee→year). Le millésime passé via --millesime est injecté automatiquement (inutile de l'avoir dans le CSV). Les colonnes created_at/updated_at sont ignorées.
Sécurité :
ON CONFLICT DO NOTHING: aucune donnée existante n'est modifiée- Le
DataPackVersionest créé avecisActive: false(activation manuelle) - Whitelist de tables : impossible d'importer dans les tables sensibles (users, sessions, simulations...)
- INSERT groupés par batch de 500 lignes
Workflow pour un nouveau millésime :
# 1. Exporter chaque onglet Excel en CSV
# 2. Générer les fichiers SQL
mkdir -p sql
pnpm -F api cli import-csv --table rp --csv ./data/rp.csv --millesime 2024 --output sql/01-rp.sql
pnpm -F api cli import-csv --table sitadel --csv ./data/sitadel.csv --millesime 2024 --output sql/02-sitadel.sql
pnpm -F api cli import-csv --table demographic_evolution_omphale --csv ./data/omphale.csv --millesime 2024 --output sql/03-omphale.sql
pnpm -F api cli import-csv --table demographic_evolution_population --csv ./data/pop.csv --millesime 2024 --output sql/04-pop.sql
# ... (une commande par onglet Excel)
# 3. Review les fichiers .sql générés
# 4. Tester en local avec --execute
# 5. Copier les .sql dans pgAdmin connecté à la prodFusion de plusieurs CSV (données réparties) :
Certaines tables (ex: homeless) reçoivent leurs données dans plusieurs fichiers CSV distincts (un pour rp, un pour sne). En passant plusieurs --csv, le script fusionne les lignes par clé primaire et génère un seul INSERT complet.
# homeless : 2 CSV avec des colonnes différentes, fusionnés par epci_code
pnpm -F api cli import-csv --table homeless --csv ./data/homeless_rp.csv --csv ./data/homeless_sne.csv --millesime 2024Les lignes sont matchées par la clé primaire de la table. Chaque CSV apporte ses colonnes, les valeurs non-vides ont priorité. Le résultat est un seul INSERT avec toutes les colonnes remplies.
otelo/
├── apps/
│ ├── api/ # Backend NestJS
│ └── web/ # Frontend Next.js
├── packages/
│ └── shared/ # Types, schémas et enums partagés
└── [configurations racine]
| Technologie | Version | Usage |
|---|---|---|
| NestJS | 11.x | Framework backend |
| Prisma | 6.x | ORM PostgreSQL |
| NextAuth/JWT | - | Authentification |
| ExcelJS | 4.x | Export Excel |
| Puppeteer | 24.x | Génération Powerpoint |
| PapaParse | 5.x | Parsing CSV |
Modules principaux :
- Authentification : ProConnect (OAuth2 gouvernemental), gestion des sessions, impersonation admin
- Entités métier : EPCI, groupements, scénarios, simulations
- Calculs : coefficients, ratios, besoins flux/stock
- Exports : Excel, PowerPoint
| Technologie | Version | Usage |
|---|---|---|
| Next.js | 16.x | Framework React |
| React | 19.x | Librairie UI |
| DSFR | - | Design System de l'État |
| React Hook Form + Zod | - | Formulaires et validation |
| TanStack Query | 5.x | État serveur |
| Recharts | 3.x | Visualisation données |
| Leaflet | 1.9.x | Cartographie |
Types TypeScript et schémas Zod réutilisables entre le frontend et le backend :
- Schémas de validation pour les résultats de calcul
- Définitions des entités (utilisateurs, EPCI, scénarios)
- Enums métier (rôles, types d'utilisateurs, sources de données)
pnpm dev # Lancer tous les services en parallèle
pnpm dev:api # Serveur NestJS uniquement
pnpm dev:web # Serveur Next.js uniquement
pnpm build # Compiler tous les packages
pnpm lint # Vérifier le code avec Biome
pnpm lint:fix # Corriger automatiquement le style- Node.js 20+
- pnpm 10.28.2+
- PostgreSQL 15+