Modifications mineures
This commit is contained in:
parent
91bb36fb8b
commit
120f5a7af8
3
.gitignore
vendored
3
.gitignore
vendored
@ -19,7 +19,8 @@ __pycache__/
|
||||
venv/
|
||||
.venv/
|
||||
Local/
|
||||
HTML
|
||||
HTML/
|
||||
static/
|
||||
|
||||
# Ignorer données Fiches (adapté à ton projet)
|
||||
Instructions.md
|
||||
|
||||
@ -152,6 +152,7 @@ fabnum-dev/
|
||||
│ │ └── README.md # Documentation du module
|
||||
│ └── visualisations/ # Visualisations graphiques
|
||||
│ ├── interface.py # Interface des visualisations
|
||||
│ ├── graphes.py # Gestion des graphes à visualiser
|
||||
│ └── README.md # Documentation du module
|
||||
├── components/ # Composants d'interface réutilisables
|
||||
│ ├── sidebar.py # Barre latérale de navigation
|
||||
|
||||
@ -121,8 +121,19 @@ def configurer_filtres_vulnerabilite():
|
||||
|
||||
|
||||
def interface_analyse(G_temp):
|
||||
st.markdown("# Analyse du graphe")
|
||||
with st.expander("Comment utiliser cet onglet ?", expanded=False):
|
||||
st.markdown("""
|
||||
1. Sélectionnez le niveau de départ (produit final, composant ou minerai)
|
||||
2. Choisissez le niveau d'arrivée souhaité
|
||||
3. Affinez votre sélection en spécifiant soit un ou des minerais à cibler spécifiquement ou des items précis à chaque niveau (optionnel)
|
||||
4. Définissez les critères d'analyse en sélectionnant les indices de vulnérabilité pertinents
|
||||
5. Choisissez le mode de combinaison des indices (ET/OU) selon votre besoin d'analyse
|
||||
6. Explorez le graphique généré en utilisant les contrôles de zoom et de déplacement ; vous pouvez basculer en mode plein écran pour le graphe
|
||||
""")
|
||||
st.markdown("---")
|
||||
|
||||
try:
|
||||
st.markdown("# Analyse")
|
||||
|
||||
# Préparation du graphe
|
||||
G_temp, niveaux_temp = preparer_graphe(G_temp)
|
||||
|
||||
@ -5,6 +5,8 @@ import markdown
|
||||
from bs4 import BeautifulSoup
|
||||
from latex2mathml.converter import convert as latex_to_mathml
|
||||
from .utils.fiche_utils import render_fiche_markdown
|
||||
import pypandoc
|
||||
import streamlit as st
|
||||
|
||||
from .utils.dynamic import (
|
||||
build_dynamic_sections,
|
||||
@ -113,13 +115,31 @@ def generer_fiche(md_source, dossier, nom_fichier, seuils):
|
||||
elif type_fiche == "minerai":
|
||||
md_source = build_minerai_sections(md_source)
|
||||
|
||||
contenu_md = render_fiche_markdown(md_source, seuils)
|
||||
contenu_md = render_fiche_markdown(md_source, seuils, license_path="assets/licence.md")
|
||||
|
||||
md_path = os.path.join("Fiches", dossier, nom_fichier)
|
||||
os.makedirs(os.path.dirname(md_path), exist_ok=True)
|
||||
with open(md_path, "w", encoding="utf-8") as f:
|
||||
f.write(contenu_md)
|
||||
|
||||
# Génération automatique du PDF
|
||||
pdf_dir = os.path.join("static", "Fiches", dossier)
|
||||
os.makedirs(pdf_dir, exist_ok=True)
|
||||
|
||||
# Construire le chemin PDF correspondant (même nom que .md, mais .pdf)
|
||||
nom_pdf = os.path.splitext(nom_fichier)[0] + ".pdf"
|
||||
pdf_path = os.path.join(pdf_dir, nom_pdf)
|
||||
|
||||
try:
|
||||
pypandoc.convert_file(
|
||||
md_path,
|
||||
to="pdf",
|
||||
outputfile=pdf_path,
|
||||
extra_args=["--pdf-engine=xelatex", "-V", "geometry:margin=2cm"]
|
||||
)
|
||||
except Exception as e:
|
||||
st.error(f"[ERREUR] Génération PDF échouée pour {md_path}: {e}")
|
||||
|
||||
html_output = rendu_html(contenu_md)
|
||||
|
||||
html_dir = os.path.join("HTML", dossier)
|
||||
|
||||
@ -16,8 +16,21 @@ from .utils.fiche_utils import load_seuils, doit_regenerer_fiche
|
||||
from .generer import generer_fiche
|
||||
|
||||
def interface_fiches():
|
||||
st.markdown("# Affichage des fiches")
|
||||
st.markdown("Sélectionner d'abord l'opération que vous souhaitez examiner et ensuite choisisez la fiche à lire.")
|
||||
st.markdown("# Découverte des fiches")
|
||||
with st.expander("Comment utiliser cet onglet ?", expanded=False):
|
||||
st.markdown("""
|
||||
1. Parcourez la liste des fiches disponibles par catégorie
|
||||
2. Sélectionnez une fiche pour afficher son contenu complet
|
||||
3. Consultez les données détaillées, graphiques et analyses supplémentaires
|
||||
4. Utilisez ces informations pour approfondir votre compréhension des vulnérabilités identifiées
|
||||
|
||||
Les catégories sont les suivantes :
|
||||
* Assemblage : opération d'assemblage des produits finaux à partir des composants
|
||||
* Connexe : opérations diverses nécessaires pour fabriquer le numérique, mais n'entrant pas directement dans sa composition
|
||||
* Criticités : indices utilisés pour identifier et évaluer les vulnérabilités
|
||||
* Fabrication : opération de fabrication des composants à partir de minerais
|
||||
* Minerai : description et opérations d'extraction et de traitement des minerais
|
||||
""")
|
||||
st.markdown("---")
|
||||
|
||||
if "fiches_arbo" not in st.session_state:
|
||||
@ -29,7 +42,7 @@ def interface_fiches():
|
||||
return
|
||||
|
||||
dossiers = sorted(arbo.keys(), key=lambda x: x.lower())
|
||||
dossier_choisi = st.selectbox("Choisissez un dossier", ["-- Sélectionner un dossier --"] + dossiers)
|
||||
dossier_choisi = st.selectbox("Choisissez une catégorie de fiches", ["-- Sélectionner un dossier --"] + dossiers)
|
||||
|
||||
if dossier_choisi and dossier_choisi != "-- Sélectionner un dossier --":
|
||||
fiches = arbo.get(dossier_choisi, [])
|
||||
|
||||
@ -38,10 +38,11 @@ def _migrate_metadata(meta: Dict) -> Dict:
|
||||
return meta
|
||||
|
||||
|
||||
def render_fiche_markdown(md_text: str, seuils: Dict) -> str:
|
||||
"""Renvoie la fiche rendue (Markdown) :
|
||||
– placeholders Jinja2 remplacés ({{ … }})
|
||||
– table seuils injectée via dict 'seuils'.
|
||||
def render_fiche_markdown(md_text: str, seuils: Dict, license_path: str = "assets/licence.md") -> str:
|
||||
"""Renvoie la fiche rendue (Markdown) :
|
||||
– placeholders Jinja2 remplacés ({{ … }})
|
||||
– table seuils injectée via dict 'seuils'.
|
||||
- licence ajoutée après le tableau de version et avant le premier titre de niveau 2
|
||||
"""
|
||||
post = frontmatter.loads(md_text)
|
||||
meta = _migrate_metadata(dict(post.metadata))
|
||||
@ -64,6 +65,24 @@ def render_fiche_markdown(md_text: str, seuils: Dict) -> str:
|
||||
|
||||
{rendered_body}"""
|
||||
|
||||
# Charger le contenu de la licence
|
||||
try:
|
||||
license_content = pathlib.Path(license_path).read_text(encoding="utf-8")
|
||||
|
||||
# Insérer la licence après le tableau de version et avant le premier titre h2
|
||||
# Trouver la position du premier titre h2
|
||||
h2_match = re.search(r"^## ", rendered_body, flags=re.M)
|
||||
|
||||
if h2_match:
|
||||
h2_position = h2_match.start()
|
||||
rendered_body = f"{rendered_body[:h2_position]}\n\n{license_content}\n\n{rendered_body[h2_position:]}"
|
||||
else:
|
||||
# S'il n'y a pas de titre h2, ajouter la licence à la fin
|
||||
rendered_body = f"{rendered_body}\n\n{license_content}"
|
||||
except Exception as e:
|
||||
# En cas d'erreur lors de la lecture du fichier de licence, continuer sans l'ajouter
|
||||
pass
|
||||
|
||||
return rendered_body
|
||||
|
||||
|
||||
|
||||
@ -6,16 +6,17 @@ from .modification import modifier_produit
|
||||
from .import_export import importer_exporter_graph
|
||||
|
||||
def interface_personnalisation(G):
|
||||
st.markdown("# Personnalisation des produits finaux")
|
||||
with st.expander("Comment utiliser cet onglet ?", expanded=False):
|
||||
st.markdown("""
|
||||
# Personnalisation des produits finaux
|
||||
|
||||
Dans cette section, vous pouvez ajouter des produits finaux qui ne sont pas présents dans la liste,
|
||||
par exemple des produits que vous concevez vous-même.
|
||||
|
||||
Vous pouvez aussi enregistrer ou recharger vos modifications.
|
||||
|
||||
---
|
||||
1. Cliquez sur "Ajouter un produit final" pour créer un nouveau produit
|
||||
2. Donnez un nom à votre produit
|
||||
3. Sélectionnez une opération d'assemblage appropriée (si pertinent)
|
||||
4. Choisissez les composants qui constituent votre produit dans la liste proposée
|
||||
5. Sauvegardez votre configuration pour une réutilisation future
|
||||
6. Vous pourrez par la suite modifier ou supprimer vos produits personnalisés
|
||||
""")
|
||||
st.markdown("---")
|
||||
|
||||
G = ajouter_produit(G)
|
||||
G = modifier_produit(G)
|
||||
|
||||
178
app/visualisations/graphes.py
Normal file
178
app/visualisations/graphes.py
Normal file
@ -0,0 +1,178 @@
|
||||
import streamlit as st
|
||||
import altair as alt
|
||||
import numpy as np
|
||||
from collections import Counter
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def afficher_graphique_altair(df):
|
||||
ordre_personnalise = ['Assemblage', 'Fabrication', 'Traitement', 'Extraction']
|
||||
categories = [cat for cat in ordre_personnalise if cat in df['categorie'].unique()]
|
||||
for cat in categories:
|
||||
st.markdown(f"### {cat}")
|
||||
df_cat = df[df['categorie'] == cat].copy()
|
||||
|
||||
coord_pairs = list(zip(df_cat['ihh_pays'].round(1), df_cat['ihh_acteurs'].round(1)))
|
||||
counts = Counter(coord_pairs)
|
||||
|
||||
offset_x = []
|
||||
offset_y = {}
|
||||
seen = Counter()
|
||||
for pair in coord_pairs:
|
||||
rank = seen[pair]
|
||||
seen[pair] += 1
|
||||
if counts[pair] > 1:
|
||||
angle = rank * 1.5
|
||||
radius = 0.8 + 0.4 * rank
|
||||
offset_x.append(radius * np.cos(angle))
|
||||
offset_y[pair] = radius * np.sin(angle)
|
||||
else:
|
||||
offset_x.append(0)
|
||||
offset_y[pair] = 0
|
||||
|
||||
df_cat['ihh_pays'] += offset_x
|
||||
df_cat['ihh_acteurs'] += [offset_y[p] for p in coord_pairs]
|
||||
df_cat['ihh_pays_text'] = df_cat['ihh_pays'] + 0.5
|
||||
df_cat['ihh_acteurs_text'] = df_cat['ihh_acteurs'] + 0.5
|
||||
|
||||
base = alt.Chart(df_cat).encode(
|
||||
x=alt.X('ihh_pays:Q', title='IHH Pays (%)'),
|
||||
y=alt.Y('ihh_acteurs:Q', title='IHH Acteurs (%)'),
|
||||
size=alt.Size('criticite_cat:Q', scale=alt.Scale(domain=[1, 2, 3], range=[50, 500, 1000]), legend=None),
|
||||
color=alt.Color('criticite_cat:N', scale=alt.Scale(domain=[1, 2, 3], range=['darkgreen', 'orange', 'darkred']))
|
||||
)
|
||||
|
||||
points = base.mark_circle(opacity=0.6)
|
||||
lines = alt.Chart(df_cat).mark_rule(strokeWidth=0.5, color='gray').encode(
|
||||
x='ihh_pays:Q', x2='ihh_pays_text:Q',
|
||||
y='ihh_acteurs:Q', y2='ihh_acteurs_text:Q'
|
||||
)
|
||||
|
||||
labels = alt.Chart(df_cat).mark_text(
|
||||
align='left', dx=3, dy=-3, fontSize=8, font='Arial', angle=335
|
||||
).encode(
|
||||
x='ihh_pays_text:Q',
|
||||
y='ihh_acteurs_text:Q',
|
||||
text='nom:N'
|
||||
)
|
||||
|
||||
hline_15 = alt.Chart(df_cat).mark_rule(strokeDash=[2,2], color='green').encode(y=alt.datum(15))
|
||||
hline_25 = alt.Chart(df_cat).mark_rule(strokeDash=[2,2], color='red').encode(y=alt.datum(25))
|
||||
hline_100 = alt.Chart(df_cat).mark_rule(strokeDash=[2,2], color='white').encode(y=alt.datum(100))
|
||||
vline_15 = alt.Chart(df_cat).mark_rule(strokeDash=[2,2], color='green').encode(x=alt.datum(15))
|
||||
vline_25 = alt.Chart(df_cat).mark_rule(strokeDash=[2,2], color='red').encode(x=alt.datum(25))
|
||||
vline_100 = alt.Chart(df_cat).mark_rule(strokeDash=[2,2], color='white').encode(x=alt.datum(100))
|
||||
|
||||
chart = (points + lines + labels + hline_15 + hline_25 + hline_100 + vline_15 + vline_25 + vline_100).properties(
|
||||
width=500,
|
||||
height=400,
|
||||
title=f"Concentration et criticité – {cat}"
|
||||
).interactive()
|
||||
|
||||
st.altair_chart(chart, use_container_width=True)
|
||||
|
||||
|
||||
def creer_graphes(donnees):
|
||||
if not donnees:
|
||||
st.warning("Aucune donnée à afficher.")
|
||||
return
|
||||
|
||||
try:
|
||||
df = pd.DataFrame(donnees)
|
||||
df['ivc_cat'] = df['ivc'].apply(lambda x: 1 if x <= 15 else (2 if x <= 30 else 3))
|
||||
|
||||
from collections import Counter
|
||||
coord_pairs = list(zip(df['ihh_extraction'].round(1), df['ihh_reserves'].round(1)))
|
||||
counts = Counter(coord_pairs)
|
||||
|
||||
offset_x, offset_y = [], {}
|
||||
seen = Counter()
|
||||
for pair in coord_pairs:
|
||||
rank = seen[pair]
|
||||
seen[pair] += 1
|
||||
if counts[pair] > 1:
|
||||
angle = rank * 1.5
|
||||
radius = 0.8 + 0.4 * rank
|
||||
offset_x.append(radius * np.cos(angle))
|
||||
offset_y[pair] = radius * np.sin(angle)
|
||||
else:
|
||||
offset_x.append(0)
|
||||
offset_y[pair] = 0
|
||||
|
||||
df['ihh_extraction'] += offset_x
|
||||
df['ihh_reserves'] += [offset_y[p] for p in coord_pairs]
|
||||
df['ihh_extraction_text'] = df['ihh_extraction'] + 0.5
|
||||
df['ihh_reserves_text'] = df['ihh_reserves'] + 0.5
|
||||
|
||||
base = alt.Chart(df).encode(
|
||||
x=alt.X('ihh_extraction:Q', title='IHH Extraction (%)'),
|
||||
y=alt.Y('ihh_reserves:Q', title='IHH Réserves (%)'),
|
||||
size=alt.Size('ivc_cat:Q', scale=alt.Scale(domain=[1, 2, 3], range=[50, 500, 1000]), legend=None),
|
||||
color=alt.Color('ivc_cat:N', scale=alt.Scale(domain=[1, 2, 3], range=['darkgreen', 'orange', 'darkred'])),
|
||||
tooltip=['nom:N', 'ivc:Q', 'ihh_extraction:Q', 'ihh_reserves:Q']
|
||||
)
|
||||
|
||||
points = base.mark_circle(opacity=0.6)
|
||||
lines = alt.Chart(df).mark_rule(strokeWidth=0.5, color='gray').encode(
|
||||
x='ihh_extraction:Q', x2='ihh_extraction_text:Q',
|
||||
y='ihh_reserves:Q', y2='ihh_reserves_text:Q'
|
||||
)
|
||||
|
||||
labels = alt.Chart(df).mark_text(
|
||||
align='left', dx=10, dy=-10, fontSize=10, font='Arial', angle=335
|
||||
).encode(
|
||||
x='ihh_extraction_text:Q',
|
||||
y='ihh_reserves_text:Q',
|
||||
text='nom:N'
|
||||
)
|
||||
|
||||
hline_15 = alt.Chart(df).mark_rule(strokeDash=[2,2], color='green').encode(y=alt.datum(15))
|
||||
hline_25 = alt.Chart(df).mark_rule(strokeDash=[2,2], color='red').encode(y=alt.datum(25))
|
||||
hline_100 = alt.Chart(df).mark_rule(strokeDash=[2,2], color='red').encode(y=alt.datum(100))
|
||||
vline_15 = alt.Chart(df).mark_rule(strokeDash=[2,2], color='green').encode(x=alt.datum(15))
|
||||
vline_25 = alt.Chart(df).mark_rule(strokeDash=[2,2], color='red').encode(x=alt.datum(25))
|
||||
vline_100 = alt.Chart(df).mark_rule(strokeDash=[2,2], color='red').encode(x=alt.datum(100))
|
||||
|
||||
chart = (points + lines + labels + hline_15 + hline_25 + hline_100 + vline_15 + vline_25 + vline_100).properties(
|
||||
width=600,
|
||||
height=500,
|
||||
title="Concentration des ressources critiques vs vulnérabilité IVC"
|
||||
).interactive()
|
||||
|
||||
st.altair_chart(chart, use_container_width=True)
|
||||
|
||||
except Exception as e:
|
||||
st.error(f"Erreur lors de la création du graphique : {e}")
|
||||
|
||||
|
||||
def lancer_visualisation_ihh_criticite(graph):
|
||||
try:
|
||||
import networkx as nx
|
||||
from utils.graph_utils import recuperer_donnees
|
||||
|
||||
niveaux = nx.get_node_attributes(graph, "niveau")
|
||||
noeuds = [n for n, v in niveaux.items() if v == "10" and "Reserves" not in n]
|
||||
noeuds.sort()
|
||||
|
||||
df = recuperer_donnees(graph, noeuds)
|
||||
if df.empty:
|
||||
st.warning("Aucune donnée à visualiser.")
|
||||
else:
|
||||
afficher_graphique_altair(df)
|
||||
except Exception as e:
|
||||
st.error(f"Erreur dans la visualisation IHH vs Criticité : {e}")
|
||||
|
||||
|
||||
def lancer_visualisation_ihh_ivc(graph):
|
||||
try:
|
||||
from utils.graph_utils import recuperer_donnees_2
|
||||
noeuds_niveau_2 = [
|
||||
n for n, data in graph.nodes(data=True)
|
||||
if data.get("niveau") == "2" and "ivc" in data
|
||||
]
|
||||
if not noeuds_niveau_2:
|
||||
return
|
||||
data = recuperer_donnees_2(graph, noeuds_niveau_2)
|
||||
creer_graphes(data)
|
||||
except Exception as e:
|
||||
st.error(f"Erreur dans la visualisation IHH vs IVC : {e}")
|
||||
@ -1,185 +1,31 @@
|
||||
import streamlit as st
|
||||
import altair as alt
|
||||
import numpy as np
|
||||
from collections import Counter
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def afficher_graphique_altair(df):
|
||||
ordre_personnalise = ['Assemblage', 'Fabrication', 'Traitement', 'Extraction']
|
||||
categories = [cat for cat in ordre_personnalise if cat in df['categorie'].unique()]
|
||||
for cat in categories:
|
||||
st.markdown(f"### {cat}")
|
||||
df_cat = df[df['categorie'] == cat].copy()
|
||||
|
||||
coord_pairs = list(zip(df_cat['ihh_pays'].round(1), df_cat['ihh_acteurs'].round(1)))
|
||||
counts = Counter(coord_pairs)
|
||||
|
||||
offset_x = []
|
||||
offset_y = {}
|
||||
seen = Counter()
|
||||
for pair in coord_pairs:
|
||||
rank = seen[pair]
|
||||
seen[pair] += 1
|
||||
if counts[pair] > 1:
|
||||
angle = rank * 1.5
|
||||
radius = 0.8 + 0.4 * rank
|
||||
offset_x.append(radius * np.cos(angle))
|
||||
offset_y[pair] = radius * np.sin(angle)
|
||||
else:
|
||||
offset_x.append(0)
|
||||
offset_y[pair] = 0
|
||||
|
||||
df_cat['ihh_pays'] += offset_x
|
||||
df_cat['ihh_acteurs'] += [offset_y[p] for p in coord_pairs]
|
||||
df_cat['ihh_pays_text'] = df_cat['ihh_pays'] + 0.5
|
||||
df_cat['ihh_acteurs_text'] = df_cat['ihh_acteurs'] + 0.5
|
||||
|
||||
base = alt.Chart(df_cat).encode(
|
||||
x=alt.X('ihh_pays:Q', title='IHH Pays (%)'),
|
||||
y=alt.Y('ihh_acteurs:Q', title='IHH Acteurs (%)'),
|
||||
size=alt.Size('criticite_cat:Q', scale=alt.Scale(domain=[1, 2, 3], range=[50, 500, 1000]), legend=None),
|
||||
color=alt.Color('criticite_cat:N', scale=alt.Scale(domain=[1, 2, 3], range=['darkgreen', 'orange', 'darkred']))
|
||||
from .graphes import (
|
||||
lancer_visualisation_ihh_criticite,
|
||||
lancer_visualisation_ihh_ivc
|
||||
)
|
||||
|
||||
points = base.mark_circle(opacity=0.6)
|
||||
lines = alt.Chart(df_cat).mark_rule(strokeWidth=0.5, color='gray').encode(
|
||||
x='ihh_pays:Q', x2='ihh_pays_text:Q',
|
||||
y='ihh_acteurs:Q', y2='ihh_acteurs_text:Q'
|
||||
)
|
||||
|
||||
labels = alt.Chart(df_cat).mark_text(
|
||||
align='left', dx=3, dy=-3, fontSize=8, font='Arial', angle=335
|
||||
).encode(
|
||||
x='ihh_pays_text:Q',
|
||||
y='ihh_acteurs_text:Q',
|
||||
text='nom:N'
|
||||
)
|
||||
|
||||
hline_15 = alt.Chart(df_cat).mark_rule(strokeDash=[2,2], color='green').encode(y=alt.datum(15))
|
||||
hline_25 = alt.Chart(df_cat).mark_rule(strokeDash=[2,2], color='red').encode(y=alt.datum(25))
|
||||
vline_15 = alt.Chart(df_cat).mark_rule(strokeDash=[2,2], color='green').encode(x=alt.datum(15))
|
||||
vline_25 = alt.Chart(df_cat).mark_rule(strokeDash=[2,2], color='red').encode(x=alt.datum(25))
|
||||
|
||||
chart = (points + lines + labels + hline_15 + hline_25 + vline_15 + vline_25).properties(
|
||||
width=500,
|
||||
height=400,
|
||||
title=f"Concentration et criticité – {cat}"
|
||||
).interactive()
|
||||
|
||||
st.altair_chart(chart, use_container_width=True)
|
||||
|
||||
|
||||
def creer_graphes(donnees):
|
||||
if not donnees:
|
||||
st.warning("Aucune donnée à afficher.")
|
||||
return
|
||||
|
||||
try:
|
||||
df = pd.DataFrame(donnees)
|
||||
df['ivc_cat'] = df['ivc'].apply(lambda x: 1 if x <= 15 else (2 if x <= 30 else 3))
|
||||
|
||||
from collections import Counter
|
||||
coord_pairs = list(zip(df['ihh_extraction'].round(1), df['ihh_reserves'].round(1)))
|
||||
counts = Counter(coord_pairs)
|
||||
|
||||
offset_x, offset_y = [], {}
|
||||
seen = Counter()
|
||||
for pair in coord_pairs:
|
||||
rank = seen[pair]
|
||||
seen[pair] += 1
|
||||
if counts[pair] > 1:
|
||||
angle = rank * 1.5
|
||||
radius = 0.8 + 0.4 * rank
|
||||
offset_x.append(radius * np.cos(angle))
|
||||
offset_y[pair] = radius * np.sin(angle)
|
||||
else:
|
||||
offset_x.append(0)
|
||||
offset_y[pair] = 0
|
||||
|
||||
df['ihh_extraction'] += offset_x
|
||||
df['ihh_reserves'] += [offset_y[p] for p in coord_pairs]
|
||||
df['ihh_extraction_text'] = df['ihh_extraction'] + 0.5
|
||||
df['ihh_reserves_text'] = df['ihh_reserves'] + 0.5
|
||||
|
||||
base = alt.Chart(df).encode(
|
||||
x=alt.X('ihh_extraction:Q', title='IHH Extraction (%)'),
|
||||
y=alt.Y('ihh_reserves:Q', title='IHH Réserves (%)'),
|
||||
size=alt.Size('ivc_cat:Q', scale=alt.Scale(domain=[1, 2, 3], range=[50, 500, 1000]), legend=None),
|
||||
color=alt.Color('ivc_cat:N', scale=alt.Scale(domain=[1, 2, 3], range=['darkgreen', 'orange', 'darkred'])),
|
||||
tooltip=['nom:N', 'ivc:Q', 'ihh_extraction:Q', 'ihh_reserves:Q']
|
||||
)
|
||||
|
||||
points = base.mark_circle(opacity=0.6)
|
||||
lines = alt.Chart(df).mark_rule(strokeWidth=0.5, color='gray').encode(
|
||||
x='ihh_extraction:Q', x2='ihh_extraction_text:Q',
|
||||
y='ihh_reserves:Q', y2='ihh_reserves_text:Q'
|
||||
)
|
||||
|
||||
labels = alt.Chart(df).mark_text(
|
||||
align='left', dx=10, dy=-10, fontSize=10, font='Arial', angle=335
|
||||
).encode(
|
||||
x='ihh_extraction_text:Q',
|
||||
y='ihh_reserves_text:Q',
|
||||
text='nom:N'
|
||||
)
|
||||
|
||||
hline_15 = alt.Chart(df).mark_rule(strokeDash=[2,2], color='green').encode(y=alt.datum(15))
|
||||
hline_25 = alt.Chart(df).mark_rule(strokeDash=[2,2], color='red').encode(y=alt.datum(25))
|
||||
vline_15 = alt.Chart(df).mark_rule(strokeDash=[2,2], color='green').encode(x=alt.datum(15))
|
||||
vline_25 = alt.Chart(df).mark_rule(strokeDash=[2,2], color='red').encode(x=alt.datum(25))
|
||||
|
||||
chart = (points + lines + labels + hline_15 + hline_25 + vline_15 + vline_25).properties(
|
||||
width=600,
|
||||
height=500,
|
||||
title="Concentration des ressources critiques vs vulnérabilité IVC"
|
||||
).interactive()
|
||||
|
||||
st.altair_chart(chart, use_container_width=True)
|
||||
|
||||
except Exception as e:
|
||||
st.error(f"Erreur lors de la création du graphique : {e}")
|
||||
|
||||
|
||||
def lancer_visualisation_ihh_criticite(graph):
|
||||
try:
|
||||
import networkx as nx
|
||||
from utils.graph_utils import recuperer_donnees
|
||||
|
||||
niveaux = nx.get_node_attributes(graph, "niveau")
|
||||
noeuds = [n for n, v in niveaux.items() if v == "10" and "Reserves" not in n]
|
||||
noeuds.sort()
|
||||
|
||||
df = recuperer_donnees(graph, noeuds)
|
||||
if df.empty:
|
||||
st.warning("Aucune donnée à visualiser.")
|
||||
else:
|
||||
afficher_graphique_altair(df)
|
||||
except Exception as e:
|
||||
st.error(f"Erreur dans la visualisation IHH vs Criticité : {e}")
|
||||
|
||||
|
||||
def lancer_visualisation_ihh_ivc(graph):
|
||||
try:
|
||||
from utils.graph_utils import recuperer_donnees_2
|
||||
noeuds_niveau_2 = [
|
||||
n for n, data in graph.nodes(data=True)
|
||||
if data.get("niveau") == "2" and "ivc" in data
|
||||
]
|
||||
if not noeuds_niveau_2:
|
||||
return
|
||||
data = recuperer_donnees_2(graph, noeuds_niveau_2)
|
||||
creer_graphes(data)
|
||||
except Exception as e:
|
||||
st.error(f"Erreur dans la visualisation IHH vs IVC : {e}")
|
||||
|
||||
def interface_visualisations(G_temp, G_temp_ivc):
|
||||
st.markdown("# Visualisations")
|
||||
st.markdown("# Analyse du graphe")
|
||||
with st.expander("Comment utiliser cet onglet ?", expanded=False):
|
||||
st.markdown("""
|
||||
1. Explorez les graphiques présentant l'Indice de Herfindahl-Hirschmann (IHH)
|
||||
2. Analysez sa relation avec la criticité moyenne des minerais ou leur Indice de Vulnérabilité Concurrentielle (IVC)
|
||||
3. Zoomer dans les graphes pour mieux découvrir les informations
|
||||
|
||||
Il est important de se rappeler que l'IHH a deux seuils :
|
||||
* en-dessous de 15, la concentration est considérée comme étant faible
|
||||
* au-dessus de 25, elle est considérée comme étant forte
|
||||
|
||||
Ainsi plus le positionnement d'un point est en haut à droite des graphiques, plus les risques sont élevés.
|
||||
Les graphiques présentent 2 droites horizontales et vetrticales pour matérialiser ces seuils.
|
||||
""")
|
||||
st.markdown("---")
|
||||
|
||||
st.markdown("""## Indice de Herfindahl-Hirschmann - IHH vs Criticité
|
||||
|
||||
Entre 0 et 15%, concentration faible, entre 15 et 25%, modérée, au-delà, forte.
|
||||
|
||||
Taille des points = criticité substituabilité du minerai
|
||||
La taille des points donne l'indication de la criticité de substituabilité du minerai.
|
||||
""")
|
||||
if st.button("Lancer", key="btn_ihh_criticite"):
|
||||
try:
|
||||
@ -189,9 +35,7 @@ Taille des points = criticité substituabilité du minerai
|
||||
|
||||
st.markdown("""## Indice de Herfindahl-Hirschmann - IHH vs IVC
|
||||
|
||||
Entre 0 et 15%, concentration faible, entre 15 et 25%, modérée, au-delà, forte.
|
||||
|
||||
Taille des points = criticité concurrentielle du minerai
|
||||
La taille des points donne l'indication de la criticité concurrentielle du minerai.
|
||||
""")
|
||||
|
||||
if st.button("Lancer", key="btn_ihh_ivc"):
|
||||
|
||||
3
assets/licence.md
Normal file
3
assets/licence.md
Normal file
@ -0,0 +1,3 @@
|
||||
Document généré avec [FabNum](https://fabnum.peccini.fr) par [Stéphan Peccini](mailto:stephan-pro@peccini.fr)
|
||||
|
||||
Licence : [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/deed.fr) - Attribution - Utilisation non commerciale - Pas d’Œuvre dérivée
|
||||
@ -10,7 +10,7 @@ def afficher_pied_de_page():
|
||||
<div role='contentinfo' aria-labelledby='footer-appli' class='wide-footer'>
|
||||
<div class='info-footer'>
|
||||
<p id='footer-appli' class='info-footer'>
|
||||
Fabnum © 2025 – <a href='mailto:stephan-pro@peccini.fr'>Contact</a> – Licence <a href='https://creativecommons.org/licenses/by-nc-sa/4.0/' target='_blank'>CC BY-NC-SA</a>
|
||||
Fabnum © 2025 – <a href='mailto:stephan-pro@peccini.fr'>Contact</a> – Licence <a href='https://creativecommons.org/licenses/by-nc-nd/4.0/deed.fr' target='_blank'>CC BY-NC-ND</a>
|
||||
</p>
|
||||
<p class='footer-note'>
|
||||
🌱 Calculs CO₂ via <a href='https://www.thegreenwebfoundation.org/' target='_blank'>The Green Web Foundation</a><br>
|
||||
|
||||
43
fabnum.py
43
fabnum.py
@ -8,6 +8,47 @@ from utils.gitea import (
|
||||
charger_instructions_depuis_gitea
|
||||
)
|
||||
|
||||
def afficher_instructions_avec_expanders(markdown_content):
|
||||
"""
|
||||
Affiche le contenu markdown avec les sections de niveau 2 (## Titre) dans des expanders
|
||||
"""
|
||||
# Extraction du titre principal (niveau 1)
|
||||
titre_pattern = r'^# (.+)$'
|
||||
titre_match = re.search(titre_pattern, markdown_content, re.MULTILINE)
|
||||
|
||||
# Affichage du titre principal
|
||||
if titre_match:
|
||||
titre_principal = titre_match.group(1)
|
||||
st.markdown(f"# {titre_principal}")
|
||||
|
||||
# Division en sections de niveau 2
|
||||
sections = re.split(r'^## ', markdown_content, flags=re.MULTILINE)
|
||||
|
||||
# Affichage du contenu avant la première section de niveau 2 (sans le titre principal)
|
||||
introduction = sections[0]
|
||||
if titre_match:
|
||||
# Supprimer le titre principal de l'introduction car on l'a déjà affiché
|
||||
introduction = re.sub(titre_pattern, '', introduction, flags=re.MULTILINE).strip()
|
||||
|
||||
if introduction:
|
||||
st.markdown(introduction, unsafe_allow_html=True)
|
||||
|
||||
# Affichage des sections dans des expanders
|
||||
for i, section in enumerate(sections[1:], 1):
|
||||
# Extraction du titre de la section
|
||||
lignes = section.split('\n', 1)
|
||||
titre_section = lignes[0].strip()
|
||||
|
||||
# Garder le titre dans le contenu
|
||||
contenu_section = f"## {titre_section}"
|
||||
if len(lignes) > 1:
|
||||
contenu_section += "\n\n" + lignes[1].strip()
|
||||
|
||||
# Affichage dans un expander
|
||||
status = True if i == 1 else False
|
||||
with st.expander(f"## {titre_section}", expanded=status):
|
||||
st.markdown(contenu_section, unsafe_allow_html=True)
|
||||
|
||||
from utils.graph_utils import (
|
||||
charger_graphe
|
||||
)
|
||||
@ -105,7 +146,7 @@ dot_file_path = None
|
||||
if st.session_state.onglet == "Instructions":
|
||||
markdown_content = charger_instructions_depuis_gitea(INSTRUCTIONS)
|
||||
if markdown_content:
|
||||
st.markdown(markdown_content)
|
||||
afficher_instructions_avec_expanders(markdown_content)
|
||||
|
||||
elif st.session_state.onglet == "Fiches":
|
||||
interface_fiches()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user