- Déplacement de toute la documentation v2 dans docs/FabNum v2/ - Ajout points 13 (bus d'impact, mémoire situationnelle, IA) et 14 (combinatoire) - Architecture structurel/situationnel, IVC dynamique inter-sectoriel - 4 vues architecturales progressives (vue1→vue4) pour présentation - Génération PNG de tous les diagrammes - .gitignore : docs/**/*.dot au lieu de docs/*.dot Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
461 lines
14 KiB
Markdown
461 lines
14 KiB
Markdown
# Architecture FabNum
|
|
|
|
## Vue d'ensemble
|
|
|
|
FabNum est une application Streamlit d'analyse de risques géopolitiques pour les chaînes d'approvisionnement numériques. Le projet permet de visualiser et d'analyser les vulnérabilités des chaînes d'approvisionnement à travers plusieurs indices de concentration et de criticité.
|
|
|
|
## Structure du projet
|
|
|
|
```
|
|
FabNum/
|
|
├── app/ # Modules applicatifs Streamlit
|
|
│ ├── analyse/ # Analyse des chaînes d'approvisionnement
|
|
│ ├── fiches/ # Génération et gestion des fiches techniques
|
|
│ ├── ia_nalyse/ # Interface d'analyse IA
|
|
│ ├── personnalisation/ # Personnalisation du graphe
|
|
│ ├── plan_d_action/ # Analyse de criticité et recommandations
|
|
│ └── visualisations/ # Visualisations (graphes, diagrammes)
|
|
├── utils/ # Utilitaires partagés
|
|
│ ├── gitea.py # Intégration API Gitea
|
|
│ ├── graph_utils.py # Manipulation des graphes NetworkX
|
|
│ ├── logger.py # Système de logging
|
|
│ ├── persistance.py # Gestion de la persistence (session Streamlit)
|
|
│ ├── translations.py # Système de traduction i18n
|
|
│ ├── visualisation.py # Visualisations Altair
|
|
│ └── widgets.py # Widgets HTML personnalisés
|
|
├── assets/ # Ressources statiques
|
|
├── tests/ # Tests unitaires et d'intégration
|
|
│ ├── unit/ # Tests unitaires
|
|
│ ├── integration/ # Tests d'intégration
|
|
│ └── conftest.py # Configuration pytest et fixtures
|
|
├── config.py # Configuration globale
|
|
└── main.py # Point d'entrée Streamlit
|
|
|
|
```
|
|
|
|
## Modules principaux
|
|
|
|
### 1. Module `analyse` - Analyse des chaînes d'approvisionnement
|
|
|
|
**Responsabilité:** Analyse et visualisation des chemins d'approvisionnement depuis un produit vers les minerais critiques.
|
|
|
|
**Fichiers clés:**
|
|
- `interface.py` : Interface utilisateur principale
|
|
- `sankey.py` : Génération de diagrammes de Sankey pour visualiser les flux
|
|
|
|
**Données manipulées:**
|
|
- Graphe NetworkX des dépendances produit → composant → minerai
|
|
- Indices IHH, ISG, ICS, IVC pour chaque nœud
|
|
|
|
**Flux de données:**
|
|
1. Utilisateur sélectionne un produit ou composant (niveau 0 ou 1)
|
|
2. Extraction des chemins via `graph_utils.extraire_chemins_depuis()`
|
|
3. Génération du diagramme de Sankey
|
|
4. Affichage des vulnérabilités détectées
|
|
|
|
---
|
|
|
|
### 2. Module `fiches` - Génération de fiches techniques
|
|
|
|
**Responsabilité:** Génération dynamique de fiches markdown pour chaque élément du graphe (produits, composants, minerais).
|
|
|
|
**Architecture:**
|
|
```
|
|
fiches/
|
|
├── generer.py # Génération des fiches markdown
|
|
├── interface.py # Interface de visualisation des fiches
|
|
└── utils/
|
|
├── dynamic/ # Générateurs de sections dynamiques
|
|
│ ├── indice/ # Sections pour IHH, ISG, ICS, IVC
|
|
│ ├── assemblage_fabrication/ # Sections production
|
|
│ ├── minerai/ # Sections spécifiques aux minerais
|
|
│ └── utils/ # Utilitaires (pastilles, formatage)
|
|
├── fiche_utils.py # Utilitaires génériques
|
|
└── tickets/ # Gestion des tickets Gitea
|
|
├── core.py # API Gitea (77% couverture)
|
|
├── display.py # Affichage des tickets
|
|
└── creation.py # Création de tickets
|
|
```
|
|
|
|
**Flux de génération de fiches:**
|
|
1. Parcours du graphe pour identifier tous les nœuds
|
|
2. Pour chaque nœud, génération de sections markdown dynamiques :
|
|
- Indicateurs de vulnérabilité (IHH, ISG)
|
|
- Données de concentration (ICS, IVC)
|
|
- Chemins critiques
|
|
- Recommandations
|
|
3. Commit des fiches sur Gitea (dépôt `DEPOT_FICHES`)
|
|
4. Synchronisation avec le système de tickets
|
|
|
|
**Intégration Gitea:**
|
|
- Stockage centralisé des fiches markdown
|
|
- Système de tickets pour le suivi des vulnérabilités
|
|
- Labels automatiques : opération (Extraction, Traitement, Fabrication, Assemblage) + item (Minerai, Composant)
|
|
|
|
---
|
|
|
|
### 3. Module `plan_d_action` - Analyse de criticité
|
|
|
|
**Responsabilité:** Calcul de criticité des chaînes d'approvisionnement et génération de recommandations.
|
|
|
|
**Architecture:**
|
|
```
|
|
plan_d_action/
|
|
├── interface.py # Interface utilisateur
|
|
└── utils/
|
|
├── data/
|
|
│ ├── plan_d_action.py # Logique métier de criticité
|
|
│ ├── pda_interface.py # Composants UI pour le plan d'action
|
|
│ ├── data_processing.py # Traitement des données
|
|
│ ├── data_utils.py # Utilitaires données
|
|
│ └── config.py # Configuration des seuils
|
|
└── interface/
|
|
├── selection.py # Sélection des chaînes
|
|
├── visualization.py # Visualisations
|
|
├── export.py # Export des résultats
|
|
└── ...
|
|
```
|
|
|
|
**Calcul de criticité:**
|
|
|
|
La criticité d'une chaîne est calculée selon les poids de vulnérabilité à chaque étape :
|
|
|
|
```python
|
|
def calcul_poids_chaine(
|
|
poids_A: int, # Assemblage (0-3)
|
|
poids_F: int, # Fabrication (0-3)
|
|
poids_T: int, # Traitement (0-3)
|
|
poids_E: int, # Extraction (0-3)
|
|
poids_M: int # Substitution minerai (0-3)
|
|
) -> tuple[str, dict, int]:
|
|
"""
|
|
Retourne: (criticite_chaine, niveau_criticite, poids_total)
|
|
|
|
Criticité:
|
|
- "Très critique" : poids_total >= 12
|
|
- "Critique" : 9 <= poids_total < 12
|
|
- "Moyenne" : 6 <= poids_total < 9
|
|
- "Faible" : poids_total < 6
|
|
"""
|
|
```
|
|
|
|
**Seuils de vulnérabilité** (définis dans `config.yaml`) :
|
|
- **IHH** (concentration géographique/acteurs) : < 15 (vert), 15-25 (orange), > 25 (rouge)
|
|
- **ISG** (instabilité géopolitique) : < 40 (vert), 40-70 (orange), > 70 (rouge)
|
|
- **ICS** (criticité supply-side) : < 15 (vert), 15-60 (orange), > 60 (rouge)
|
|
- **IVC** (vulnérabilité demand-side) : < 15 (vert), 15-60 (orange), > 60 (rouge)
|
|
|
|
**Tableau de bord:**
|
|
La fonction `tableau_de_bord()` permet de :
|
|
1. Sélectionner une chaîne d'approvisionnement
|
|
2. Afficher la criticité globale et par étape
|
|
3. Présenter les préconisations spécifiques et génériques
|
|
4. Exporter le plan d'action en markdown
|
|
|
|
---
|
|
|
|
### 4. Module `utils` - Utilitaires partagés
|
|
|
|
#### `gitea.py` - Intégration Gitea (100% couverture)
|
|
|
|
**API principale:**
|
|
```python
|
|
def charger_instructions_depuis_gitea(nom_fichier: str) -> str | None:
|
|
"""Charge un fichier depuis Gitea avec cache local basé sur timestamp."""
|
|
|
|
def charger_schema_depuis_gitea(fichier_local: str) -> str | None:
|
|
"""Charge le fichier DOT du graphe depuis Gitea."""
|
|
|
|
def charger_arborescence_fiches() -> dict:
|
|
"""Charge l'arborescence complète des fiches markdown."""
|
|
```
|
|
|
|
**Fonctionnalités:**
|
|
- Cache local avec vérification de timestamp (évite les téléchargements inutiles)
|
|
- Authentification automatique via `GITEA_TOKEN`
|
|
- Gestion des erreurs réseau avec fallback sur le cache
|
|
|
|
#### `graph_utils.py` - Manipulation de graphes (59% couverture)
|
|
|
|
**Fonctions principales:**
|
|
```python
|
|
def extraire_chemins_depuis(G: nx.DiGraph, noeud_depart: str) -> list[list[str]]:
|
|
"""Extrait tous les chemins depuis un nœud vers les feuilles."""
|
|
|
|
def extraire_chemins_vers(G: nx.DiGraph, noeud_cible: str, niveau_demande: int) -> list[list[str]]:
|
|
"""Extrait les chemins vers un nœud cible depuis le niveau demandé."""
|
|
|
|
def recuperer_donnees(graph: nx.DiGraph, noeuds: list[str]) -> pd.DataFrame:
|
|
"""Récupère les données IHH/ICS pour des nœuds operation-minerai."""
|
|
|
|
def recuperer_donnees_2(graph: nx.DiGraph, minerais: list[str]) -> list[dict]:
|
|
"""Récupère les données IVC/IHH pour les minerais (extraction + réserves)."""
|
|
|
|
def charger_graphe(dot_file: str = "schema_temp.txt") -> nx.DiGraph:
|
|
"""Charge le graphe depuis un fichier DOT."""
|
|
```
|
|
|
|
**Structure du graphe:**
|
|
- **Niveaux:**
|
|
- 0 : Produits finaux
|
|
- 1 : Composants
|
|
- 2 : Minerais
|
|
- 10 : Opérations (Assemblage, Fabrication, Traitement, Extraction, Réserves)
|
|
- 11 : Pays d'opération
|
|
- 99 : Pays géographiques
|
|
|
|
- **Attributs de nœuds:**
|
|
- `niveau` : Niveau hiérarchique
|
|
- `ihh_pays`, `ihh_acteurs` : Indices de concentration
|
|
- `isg` : Indice de stabilité géopolitique
|
|
- `ivc` : Indice de vulnérabilité (minerais)
|
|
- `ics` : Indice de criticité supply-side (arêtes)
|
|
|
|
#### `persistance.py` - Gestion de session (0% couverture)
|
|
|
|
Gère la persistence d'état via `st.session_state` :
|
|
```python
|
|
def get_session_id() -> str:
|
|
"""Récupère l'ID de session Streamlit."""
|
|
|
|
def update_session_paths(session_id: str, paths: list):
|
|
"""Met à jour les chemins en session."""
|
|
|
|
def get_champ_statut(cle: str) -> Any:
|
|
"""Récupère une valeur du session_state."""
|
|
```
|
|
|
|
#### `logger.py` - Système de logging (94% couverture)
|
|
|
|
```python
|
|
def setup_logger(name: str, level: str = "INFO", log_to_file: bool = False) -> logging.Logger:
|
|
"""Configure un logger avec handler console et optionnellement fichier."""
|
|
|
|
def get_logger(name: str) -> logging.Logger:
|
|
"""Récupère ou crée un logger."""
|
|
```
|
|
|
|
---
|
|
|
|
## Flux de données principal
|
|
|
|
### 1. Chargement initial
|
|
```
|
|
main.py
|
|
→ config.py (variables d'environnement)
|
|
→ utils/gitea.charger_schema_depuis_gitea()
|
|
→ utils/graph_utils.charger_graphe()
|
|
→ Graphe NetworkX en st.session_state
|
|
```
|
|
|
|
### 2. Navigation utilisateur (module Analyse)
|
|
```
|
|
app/analyse/interface.py
|
|
→ Sélection produit/composant
|
|
→ graph_utils.extraire_chemins_depuis()
|
|
→ analyse/sankey.py pour visualisation
|
|
→ Affichage des vulnérabilités (IHH, ICS, IVC)
|
|
```
|
|
|
|
### 3. Génération de fiches (module Fiches)
|
|
```
|
|
app/fiches/generer.py
|
|
→ Parcours du graphe
|
|
→ Pour chaque nœud:
|
|
→ utils/dynamic/indice/*.py (génération sections)
|
|
→ utils/dynamic/minerai/minerai.py (sections minerai)
|
|
→ Commit vers Gitea (DEPOT_FICHES)
|
|
→ Création/mise à jour tickets
|
|
```
|
|
|
|
### 4. Plan d'action (module Plan d'action)
|
|
```
|
|
app/plan_d_action/interface.py
|
|
→ utils/data/plan_d_action.analyser_chaines()
|
|
→ Calcul des poids A/F/T/E/M
|
|
→ Détermination de la criticité
|
|
→ utils/data/plan_d_action.tableau_de_bord()
|
|
→ Affichage préconisations et export
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration et environnement
|
|
|
|
### Fichiers de configuration
|
|
|
|
**`config.py`** - Configuration globale:
|
|
```python
|
|
GITEA_URL = os.getenv("GITEA_URL")
|
|
GITEA_TOKEN = os.getenv("GITEA_TOKEN")
|
|
ORGANISATION = os.getenv("ORGANISATION")
|
|
DEPOT_FICHES = os.getenv("DEPOT_FICHES")
|
|
DEPOT_CODE = os.getenv("DEPOT_CODE")
|
|
DOT_FILE = os.getenv("DOT_FILE")
|
|
ENV = os.getenv("ENV") # Branche Gitea (dev/main)
|
|
```
|
|
|
|
**`config.yaml`** - Seuils de vulnérabilité:
|
|
```yaml
|
|
seuils:
|
|
IHH:
|
|
vert: {max: 15}
|
|
orange: {min: 15, max: 25}
|
|
rouge: {min: 25}
|
|
ISG:
|
|
vert: {max: 40}
|
|
orange: {min: 40, max: 70}
|
|
rouge: {min: 70}
|
|
ICS:
|
|
vert: {max: 15}
|
|
orange: {min: 15, max: 60}
|
|
rouge: {min: 60}
|
|
IVC:
|
|
vert: {max: 15}
|
|
orange: {min: 15, max: 60}
|
|
rouge: {min: 60}
|
|
```
|
|
|
|
### Variables d'environnement requises
|
|
|
|
- `GITEA_URL` : URL de l'instance Gitea
|
|
- `GITEA_TOKEN` : Token d'authentification API
|
|
- `ORGANISATION` : Organisation Gitea
|
|
- `DEPOT_FICHES` : Nom du dépôt pour les fiches markdown
|
|
- `DEPOT_CODE` : Nom du dépôt contenant le graphe DOT
|
|
- `DOT_FILE` : Nom du fichier DOT du graphe
|
|
- `ENV` : Branche Gitea à utiliser (dev/main)
|
|
|
|
---
|
|
|
|
## Tests
|
|
|
|
### Couverture actuelle
|
|
|
|
**Couverture globale:** 16%
|
|
|
|
**Modules testés:**
|
|
- `utils/gitea.py` : 100% ✓
|
|
- `utils/widgets.py` : 100% ✓
|
|
- `utils/logger.py` : 94% ✓
|
|
- `app/fiches/utils/tickets/core.py` : 77% ✓
|
|
- `utils/graph_utils.py` : 59%
|
|
|
|
### Structure des tests
|
|
|
|
```
|
|
tests/
|
|
├── conftest.py # Fixtures globales
|
|
│ ├── simple_graph() # Graphe simple pour tests
|
|
│ ├── complex_graph() # Graphe avec chemins multiples
|
|
│ └── sample_config_yaml() # Config YAML de test
|
|
├── unit/
|
|
│ ├── test_gitea.py # Tests utils/gitea.py
|
|
│ ├── test_fiches_tickets.py # Tests app/fiches/utils/tickets/core.py
|
|
│ ├── test_graph_utils.py # Tests utils/graph_utils.py
|
|
│ ├── test_logger.py # Tests utils/logger.py
|
|
│ └── test_widgets.py # Tests utils/widgets.py
|
|
└── integration/
|
|
└── (à venir)
|
|
```
|
|
|
|
### Exécuter les tests
|
|
|
|
```bash
|
|
# Tous les tests
|
|
pytest -v
|
|
|
|
# Avec couverture
|
|
pytest --cov=utils --cov=app --cov-report=html
|
|
|
|
# Tests spécifiques
|
|
pytest tests/unit/test_gitea.py -v
|
|
```
|
|
|
|
---
|
|
|
|
## Dépendances principales
|
|
|
|
**Core:**
|
|
- `streamlit` : Framework web
|
|
- `networkx` : Manipulation de graphes
|
|
- `pandas` : Manipulation de données
|
|
- `altair` : Visualisations
|
|
- `pydot` : Parsing de fichiers DOT
|
|
|
|
**API:**
|
|
- `requests` : Requêtes HTTP pour Gitea
|
|
- `python-dateutil` : Parsing de dates ISO
|
|
|
|
**Tests:**
|
|
- `pytest` : Framework de tests
|
|
- `pytest-cov` : Couverture de code
|
|
- `pytest-mock` : Mocking
|
|
|
|
**Qualité de code:**
|
|
- `ruff` : Linter et formatter Python moderne
|
|
|
|
---
|
|
|
|
## Conventions de code
|
|
|
|
### Style
|
|
- **Formatage:** Ruff (line-length: 120)
|
|
- **Docstrings:** Google style
|
|
- **Langue:** Français pour les docstrings et l'UI, anglais pour le code
|
|
|
|
### Nommage
|
|
- **Fonctions:** `snake_case`
|
|
- **Classes:** `PascalCase`
|
|
- **Constantes:** `UPPER_SNAKE_CASE`
|
|
- **Fichiers:** `snake_case.py`
|
|
|
|
### Organisation des imports
|
|
```python
|
|
# 1. Standard library
|
|
import os
|
|
from datetime import datetime
|
|
|
|
# 2. Third-party
|
|
import pandas as pd
|
|
import streamlit as st
|
|
|
|
# 3. Local
|
|
from config import GITEA_URL
|
|
from utils.graph_utils import charger_graphe
|
|
```
|
|
|
|
---
|
|
|
|
## Roadmap et améliorations futures
|
|
|
|
### Tests
|
|
- [ ] Augmenter la couverture de `app/plan_d_action` (actuellement 0%)
|
|
- [ ] Tests d'intégration pour les flux complets
|
|
- [ ] Tests de performance pour les graphes volumineux
|
|
|
|
### Documentation
|
|
- [x] Documentation d'architecture (ce fichier)
|
|
- [ ] Guide utilisateur
|
|
- [ ] API documentation (docstrings complètes)
|
|
- [ ] Diagrammes de séquence pour les flux critiques
|
|
|
|
### Fonctionnalités
|
|
- [ ] Export des analyses en PDF
|
|
- [ ] Alertes automatiques sur nouvelles vulnérabilités
|
|
- [ ] Comparaison historique des indices
|
|
- [ ] Interface d'administration pour gérer les seuils
|
|
|
|
### Infrastructure
|
|
- [ ] CI/CD avec Gitea Actions
|
|
- [ ] Déploiement automatique
|
|
- [ ] Monitoring et logging centralisé
|
|
|
|
---
|
|
|
|
## Contact et contribution
|
|
|
|
Ce projet est développé par Stéphan Peccini Conseil dans le cadre de l'Observatoire des Polycrises.
|
|
|
|
Pour contribuer ou signaler des bugs, veuillez consulter le dépôt Gitea de l'organisation.
|