Code/app/plan_d_action/utils/data/plan_d_action.py
Stéphan Peccini f812fac89e
feat: Amelioration structure - tests, documentation et qualite du code
Cette mise a jour complete ameliore significativement la qualite et la maintenabilite du projet.

1. Extension de la couverture de tests

Couverture globale passee de 8% a 16% (+100%)
- Ajout de 25 nouveaux tests (total: 67 tests, 100% passent)
- Nouveaux fichiers de tests:
  * tests/unit/test_gitea.py (17 tests)
  * tests/unit/test_fiches_tickets.py (8 tests)

Etat de la couverture par module:
- utils/gitea.py: 100%
- utils/widgets.py: 100%
- utils/logger.py: 94%
- app/fiches/utils/tickets/core.py: 77%
- utils/graph_utils.py: 59%

2. Documentation d'architecture complete

Creation de 3 nouveaux documents (30 Ko total):
- docs/ARCHITECTURE.md (15 Ko)
  * Architecture complete du projet
  * Flux de donnees detailles
  * Indices de vulnerabilite (IHH, ISG, ICS, IVC)
  * Structure du graphe NetworkX

- docs/MODULES.md (15 Ko)
  * Guide des 11 modules principaux
  * Exemples de code (15+ snippets)
  * Bonnes pratiques
  * Guide de depannage

- docs/README.md (4 Ko)
  * Index de toute la documentation

Contenu documente:
- 5 modules applicatifs
- 6 modules utilitaires
- 4 indices de vulnerabilite avec formules et seuils
- Conventions de code

3. Reorganisation de la documentation

Structure finale optimisee:
- Racine: README.md (mis a jour) + Instructions.md
- docs/: 11 documents organises par categorie

Fichiers deplaces vers docs/:
- README_connexion.md -> docs/CONNEXION.md
- GUIDE_LOGS.md -> docs/
- GUIDE_RUFF.md -> docs/
- RAPPORT_RUFF.md -> docs/
- RAPPORT_CORRECTIONS_AUTO.md -> docs/
- REFACTORING_REPORT.md -> docs/
- VERIFICATION_LOGS.md -> docs/
- TODO_IA_BATCH.md -> docs/

4. Ajout de docstrings

52 fonctions documentees en style Google (100%)
Documentation en francais avec Args, Returns, Raises

5. Corrections automatiques Ruff

Application de 347 corrections automatiques:
- Formatage du code (line-length: 120)
- Organisation des imports
- Simplifications syntaxiques
- Suppressions de code mort
- Ameliorations de performance

6. Configuration qualite du code

Nouveaux fichiers:
- pyproject.toml: configuration Ruff complete
- .vscode/settings.json: integration Ruff avec formatOnSave
- GUIDE_RUFF.md: documentation du linter
- GUIDE_LOGS.md: documentation du logging
- .gitignore: ajout htmlcov/ pour rapports de couverture

Etat final du projet:
- Linter: Ruff configure (15 regles actives)
- Tests: 67 tests (100% passent)
- Couverture de code: 16%
- Docstrings: 52/52 (100%)
- Documentation: 11 fichiers organises

Impact:
- Tests plus robustes et maintenables
- Documentation technique complete
- Meilleure organisation des fichiers
- Workflow optimise avec Ruff
- Code pret pour integration continue

References:
- Architecture: docs/ARCHITECTURE.md
- Guide modules: docs/MODULES.md
- Tests: tests/unit/
- Configuration: pyproject.toml

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-07 19:00:49 +01:00

425 lines
24 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import matplotlib.pyplot as plt
import streamlit as st
from app.plan_d_action.utils.data import (
INDICATEURS,
PRECONISATIONS,
afficher_bloc_ihh_isg,
afficher_caracteristiques_minerai,
afficher_description,
colorer_couleurs,
initialiser_seuils,
parse_chains_md,
poids_operation,
set_vulnerability,
)
def calcul_poids_chaine(poids_A: int, poids_F: int, poids_T: int, poids_E: int, poids_M: int) -> tuple[str,dict,int]:
"""Calcule le poids total et la criticite d'une chaine d'approvisionnement.
Args:
poids_A: Poids vulnerabilite assemblage (0-3).
poids_F: Poids vulnerabilite fabrication (0-3).
poids_T: Poids vulnerabilite traitement (0-3).
poids_E: Poids vulnerabilite extraction (0-3).
poids_M: Poids vulnerabilite substitution (0-3).
Returns:
tuple: (criticite_chaine: str, niveau_criticite: set, poids_total: int).
"""
poids_total = (\
poids_A * poids_operation["Assemblage"] + \
poids_F * poids_operation["Fabrication"] + \
poids_T * poids_operation["Traitement"] + \
poids_E * poids_operation["Extraction"] + \
poids_M * poids_operation["Substitution"] \
) / sum(poids_operation.values())
if poids_total < 3:
criticite_chaine = "Modérée"
niveau_criticite = {"Facile"}
elif poids_total < 6:
criticite_chaine = "Élevée"
niveau_criticite = {"Facile", "Modérée"}
else:
criticite_chaine = "Critique"
niveau_criticite = {"Facile", "Modérée", "Difficile"}
return criticite_chaine, niveau_criticite, poids_total
def analyser_chaines(chaines: list[dict], produits: dict, composants: dict, mineraux: dict, seuils: dict, top_n: int = 0) -> list[tuple[str, str, int]]:
"""Analyse toutes les chaines d'approvisionnement et calcule leur criticite.
Args:
chaines: Liste de dictionnaires {produit, composant, minerai}.
produits: Donnees IHH/ISG par produit.
composants: Donnees IHH/ISG par composant.
mineraux: Donnees IHH/ISG/ICS/IVC par minerai.
seuils: Seuils de vulnerabilite depuis config.yaml.
top_n: Nombre de resultats a retourner (0 = tous). Defaut: 0.
Returns:
list[dict]: Chaines avec criticite, triees par poids decroissant.
"""
resultats = []
for chaine in chaines:
sel_prod = chaine["produit"]
sel_comp = chaine["composant"]
sel_miner = chaine["minerai"]
poids_A, *_ = set_vulnerability(produits[sel_prod]["IHH_Assemblage"], produits[sel_prod]["ISG_Assemblage"], "IHH", "ISG", seuils)
poids_F, *_ = set_vulnerability(composants[sel_comp]["IHH_Fabrication"], composants[sel_comp]["ISG_Fabrication"], "IHH", "ISG", seuils)
poids_T, *_ = set_vulnerability(mineraux[sel_miner]["IHH_Traitement"], mineraux[sel_miner]["ISG_Traitement"], "IHH", "ISG", seuils)
poids_E, *_ = set_vulnerability(mineraux[sel_miner]["IHH_Extraction"], mineraux[sel_miner]["ISG_Extraction"], "IHH", "ISG", seuils)
poids_M, *_ = set_vulnerability(mineraux[sel_miner]["ICS"], mineraux[sel_miner]["IVC"], "ICS", "IVC", seuils)
criticite_chaine, niveau_criticite, poids_total = calcul_poids_chaine(
poids_A, poids_F, poids_T, poids_E, poids_M
)
resultats.append({
"chaine": chaine,
"criticite_chaine": criticite_chaine,
"niveau_criticite": niveau_criticite,
"poids_total": poids_total
})
# Tri décroissant
resultats.sort(key=lambda x: x["poids_total"], reverse=True)
# Si top_n n'est pas spécifié, tout est retourné
if top_n == 0 or top_n >= len(resultats):
return resultats
# Déterminer le seuil de coupure
seuil_poids = resultats[top_n - 1]["poids_total"]
# Inclure tous ceux dont le poids est égal au seuil
top_resultats = [r for r in resultats if r["poids_total"] >= seuil_poids]
return top_resultats
def tableau_de_bord(chains: list[dict], produits: dict, composants: dict, mineraux: dict, seuils: dict):
"""Affiche le tableau de bord interactif pour selectionner et analyser une chaine.
Args:
chains: Liste de toutes les chaines d'approvisionnement.
produits: Donnees IHH/ISG par produit.
composants: Donnees IHH/ISG par composant.
mineraux: Donnees IHH/ISG/ICS/IVC par minerai.
seuils: Seuils de vulnerabilite depuis config.yaml.
Returns:
tuple: Selectionsutilisateur et toutes les donnees de criticite calculees.
"""
col_left, col_right = st.columns([2, 3], gap="small", border=True)
with col_left:
st.markdown("**<u>Panneau de sélection</u>**", unsafe_allow_html=True)
produits_disponibles = sorted({c["produit"] for c in chains})
sel_prod = st.selectbox("Produit", produits_disponibles, index=produits_disponibles.index(st.session_state.sel_prod) if st.session_state.sel_prod else 0)
if sel_prod != st.session_state.sel_prod:
st.session_state.sel_prod = sel_prod
st.session_state.sel_comp = None
st.session_state.sel_miner = None
st.rerun()
composants_dispo = sorted({c["composant"] for c in chains if c["produit"] == sel_prod})
sel_comp = st.selectbox("Composant", composants_dispo, index=composants_dispo.index(st.session_state.sel_comp) if st.session_state.sel_comp else 0)
if sel_comp != st.session_state.sel_comp:
st.session_state.sel_comp = sel_comp
st.session_state.sel_miner = None
st.rerun()
mineraux_dispo = sorted({c["minerai"] for c in chains if c["produit"] == sel_prod and c["composant"] == sel_comp})
sel_miner = st.selectbox("Minerai", mineraux_dispo, index=mineraux_dispo.index(st.session_state.sel_miner) if st.session_state.sel_miner else 0)
if sel_miner != st.session_state.sel_miner:
st.session_state.sel_miner = sel_miner
st.rerun()
with col_right:
top_chains = analyser_chaines(chains, produits, composants, mineraux, seuils, top_n=5)
st.markdown("**<u>Top chaînes critiques pour sélection rapide</u>**", unsafe_allow_html=True)
for i, entry in enumerate(top_chains):
ch = entry["chaine"]
poids = entry["poids_total"]
criticite = entry["criticite_chaine"]
if st.button(f"**{ch['produit']} <-> {ch['composant']} <-> {ch['minerai']}** : {poids:.2f}{criticite}", key=f"select_{i}"):
st.session_state.sel_prod = ch["produit"]
st.session_state.sel_comp = ch["composant"]
st.session_state.sel_miner = ch["minerai"]
st.rerun()
c1, c2 = st.columns([3, 2], gap="small", border=True, vertical_alignment='center')
with c1:
st.markdown("**<u>Synthèse des criticités</u>**", unsafe_allow_html=True)
poids_A, couleur_A, couleur_A_ihh, couleur_A_isg = set_vulnerability(produits[sel_prod]["IHH_Assemblage"], produits[sel_prod]["ISG_Assemblage"], "IHH", "ISG", seuils)
poids_F, couleur_F, couleur_F_ihh, couleur_F_isg = set_vulnerability(composants[sel_comp]["IHH_Fabrication"], composants[sel_comp]["ISG_Fabrication"], "IHH", "ISG", seuils)
poids_T, couleur_T, couleur_T_ihh, couleur_T_isg = set_vulnerability(mineraux[sel_miner]["IHH_Traitement"], mineraux[sel_miner]["ISG_Traitement"], "IHH", "ISG", seuils)
poids_E, couleur_E, couleur_E_ihh, couleur_E_isg = set_vulnerability(mineraux[sel_miner]["IHH_Extraction"], mineraux[sel_miner]["ISG_Extraction"], "IHH", "ISG", seuils)
poids_M, couleur_M, couleur_M_ics, couleur_M_ivc = set_vulnerability(mineraux[sel_miner]["ICS"], mineraux[sel_miner]["IVC"], "ICS", "IVC", seuils)
st.markdown(f"* **{sel_prod} - Assemblage** : {colorer_couleurs(couleur_A)} ({poids_A})")
st.markdown(f"* **{sel_comp} - Fabrication** : {colorer_couleurs(couleur_F)} ({poids_F})")
st.markdown(f"* **{sel_miner} - Traitement** : {colorer_couleurs(couleur_T)} ({poids_T})")
st.markdown(f"* **{sel_miner} - Extraction** : {colorer_couleurs(couleur_E)} ({poids_E})")
st.markdown(f"* **{sel_miner} - Minerai** : {colorer_couleurs(couleur_M)} ({poids_M})")
criticite_chaine, niveau_criticite, poids_total = calcul_poids_chaine(poids_A, poids_F, poids_T, poids_E, poids_M)
with c2:
st.error(f"**Criticité globale : {criticite_chaine} ({poids_total})**")
return (
sel_prod, sel_comp, sel_miner, niveau_criticite,
couleur_A, poids_A, couleur_F, poids_F, couleur_T, poids_T, couleur_E, poids_E, couleur_M, poids_M,
couleur_A_ihh, couleur_A_isg, couleur_F_ihh, couleur_F_isg, couleur_T_ihh, couleur_T_isg,couleur_E_ihh, couleur_E_isg, couleur_M_ics, couleur_M_ivc
)
def afficher_criticites(produits: dict, composants: dict, mineraux: dict, sel_prod: str, sel_comp: str, sel_miner: str, seuils: dict) -> None:
"""Affiche les graphiques de criticite IHH/ISG et ICS/IVC pour la chaine selectionnee."""
with st.expander("Vue d'ensemble des criticités", expanded=True):
st.markdown("## Vue densemble des criticités", unsafe_allow_html=True)
col_left, col_right = st.columns([1, 1], gap="small", border=True)
with col_left:
fig1, ax1 = plt.subplots(figsize=(2, 2.4))
ax1.scatter([produits[sel_prod]["ISG_Assemblage"]], [produits[sel_prod]["IHH_Assemblage"]], label="Assemblage", s=5)
ax1.scatter([composants[sel_comp]["ISG_Fabrication"]], [composants[sel_comp]["IHH_Fabrication"]], label="Fabrication", s=5)
ax1.scatter([mineraux[sel_miner]["ISG_Extraction"]], [mineraux[sel_miner]["IHH_Extraction"]], label="Extraction", s=5)
ax1.scatter([mineraux[sel_miner]["ISG_Traitement"]], [mineraux[sel_miner]["IHH_Traitement"]], label="Traitement", s=5)
# Seuils ISG (vertical)
ax1.axvline(seuils["ISG"]["vert"]["max"], linestyle='--', color='green', alpha=0.7, linewidth=0.5) # Seuil vert-orange
ax1.axvline(seuils["ISG"]["rouge"]["min"], linestyle='--', color='red', alpha=0.7, linewidth=0.5) # Seuil orange-rouge
# Seuils IHH (horizontal)
ax1.axhline(seuils["IHH"]["vert"]["max"], linestyle='--', color='green', alpha=0.7, linewidth=0.5) # Seuil vert-orange
ax1.axhline(seuils["IHH"]["rouge"]["min"], linestyle='--', color='red', alpha=0.7, linewidth=0.5) # Seuil orange-rouge
ax1.set_xlim(0, 100)
ax1.set_ylim(0, 100)
ax1.set_xlabel("ISG", fontsize=4)
ax1.set_ylabel("IHH", fontsize=4)
ax1.tick_params(axis='both', which='major', labelsize=4)
ax1.legend(bbox_to_anchor=(0.5, -0.25), loc='upper center', fontsize=4)
plt.tight_layout()
st.pyplot(fig1)
with col_right:
fig2, ax2 = plt.subplots(figsize=(2, 2.1))
ax2.scatter([mineraux[sel_miner]["IVC"]], [mineraux[sel_miner]["ICS"]], color='green', s=5, label=sel_miner)
# Seuils IVC (vertical)
ax2.axvline(seuils["IVC"]["vert"]["max"], linestyle='--', color='green', alpha=0.7, linewidth=0.5) # Seuil vert-orange
ax2.axvline(seuils["IVC"]["rouge"]["min"], linestyle='--', color='red', alpha=0.7, linewidth=0.5) # Seuil orange-rouge
# Seuils ICS (horizontal)
ax2.axhline(seuils["ICS"]["vert"]["max"], linestyle='--', color='green', alpha=0.7, linewidth=0.5) # Seuil vert-orange
ax2.axhline(seuils["ICS"]["rouge"]["min"], linestyle='--', color='red', alpha=0.7, linewidth=0.5) # Seuil orange-rouge
ax2.set_xlim(0, max(100, mineraux[sel_miner]["IVC"]))
ax2.set_ylim(0, 1)
ax2.set_xlabel("IVC", fontsize=4)
ax2.set_ylabel("ICS", fontsize=4)
ax2.tick_params(axis='both', which='major', labelsize=4)
ax2.legend(bbox_to_anchor=(0.5, -0.25), loc='upper center', fontsize=4)
plt.tight_layout()
st.pyplot(fig2)
st.markdown(f"""
Les lignes pointillées en {colorer_couleurs("vert")} ou {colorer_couleurs("rouge")} représentent les seuils des indices concernés.\n
Les indices ISG (stabilité géopolitique) et IVC (concurrence intersectorielle) influent sur la probabilité de survenance d'un risque.\n
Les indices IHH (concentration géographique) et ICS (capacité de substitution) influent sur le niveau d'impact d'un risque.\n
Une opération se trouvant au-dessus des deux seuils a donc une forte probabilité d'être impactée avec un niveau élevé sur l'incapacité à continuer la production.
""")
def afficher_explications_et_details(
couleur_A, poids_A, couleur_F, poids_F, couleur_T, poids_T, couleur_E, poids_E, couleur_M, poids_M,
produits, composants, mineraux, sel_prod, sel_comp, sel_miner,
couleur_A_ihh, couleur_A_isg, couleur_F_ihh, couleur_F_isg, couleur_T_ihh, couleur_T_isg,couleur_E_ihh, couleur_E_isg, couleur_M_ics, couleur_M_ivc, ui = True) -> str|None:
"""Affiche les explications detaillees des indices et ponderations pour chaque operation."""
with st.expander("Explications et détails", expanded = True):
from collections import Counter
couleurs = [couleur_A, couleur_F, couleur_T, couleur_E, couleur_M]
compte = Counter(couleurs)
nb_rouge = compte["Rouge"]
nb_orange = compte["Orange"]
nb_vert = compte["Vert"]
contenu_md = f"""
Pour cette chaîne :blue-background[**{sel_prod} <-> {sel_comp} <-> {sel_miner}**], avec {nb_rouge} criticité(s) de niveau {colorer_couleurs("Rouge")}, {nb_orange} {colorer_couleurs("Orange")} et {nb_vert} {colorer_couleurs("Vert")}, les indices individuels par opération sont :
* **{sel_prod} - Assemblage** : {colorer_couleurs(couleur_A)} ({poids_A})
* IHH = {produits[sel_prod]["IHH_Assemblage"]} ({colorer_couleurs(couleur_A_ihh)}) <-> ISG = {produits[sel_prod]["ISG_Assemblage"]} ({colorer_couleurs(couleur_A_isg)})
* pondération de l'Assemblage dans le calcul de la criticité globale : 1,5
* se référer à **{sel_prod} et Assemblage** plus bas pour le détail complet
* **{sel_comp} - Fabrication** : {colorer_couleurs(couleur_F)} ({poids_F})
* IHH = {composants[sel_comp]["IHH_Fabrication"]} ({colorer_couleurs(couleur_F_ihh)}) <-> ISG = {composants[sel_comp]["ISG_Fabrication"]} ({colorer_couleurs(couleur_F_isg)})
* pondération de la Fabrication dans le calcul de la criticité globale : 2
* se référer à **{sel_comp} et Fabrication** plus bas pour le détail complet
* **{sel_miner} - Traitement** : {colorer_couleurs(couleur_A)} ({poids_A})
* IHH = {mineraux[sel_miner]["IHH_Traitement"]} ({colorer_couleurs(couleur_T_ihh)}) <-> ISG = {mineraux[sel_miner]["ISG_Traitement"]} ({colorer_couleurs(couleur_T_isg)})
* pondération du Traitement dans le calcul de la criticité globale : 1,5
* se référer à **{sel_miner} — Vue globale** plus bas pour le détail complet de l'ensemble du minerai
* **{sel_miner} - Extraction** : {colorer_couleurs(couleur_E)} ({poids_E})
* IHH = {mineraux[sel_miner]["IHH_Extraction"]} ({colorer_couleurs(couleur_E_ihh)}) <-> ISG = {mineraux[sel_miner]["ISG_Extraction"]} ({colorer_couleurs(couleur_E_isg)})
* pondération de l'Extraction dans le calcul de la criticité globale : 1
* **{sel_miner} - Minerai** : {colorer_couleurs(couleur_M)} ({poids_M})
* ICS = {mineraux[sel_miner]["ICS"]} ({colorer_couleurs(couleur_M_ics)}) <-> IVC = {mineraux[sel_miner]["IVC"]} ({colorer_couleurs(couleur_M_ivc)})
* pondération de la Substitution dans le calcul de la criticité globale : 2
"""
contenu_md += "\n"
if ui:
st.markdown(contenu_md)
return None
return contenu_md
def afficher_preconisations_et_indicateurs_generiques(niveau_criticite: dict, poids_A: int, poids_F: int, poids_T: int, poids_E: int, poids_M: int, ui: bool = True) -> tuple[str|None,str|None]:
"""Affiche les preconisations et indicateurs generiques selon le niveau de criticite global."""
contenu_md_left = "### Préconisations :\n\n"
contenu_md_left += "Mise en œuvre : \n"
for niveau, contenu in PRECONISATIONS.items():
if niveau in niveau_criticite:
contenu_md_left += f"* {colorer_couleurs(niveau)}\n"
for p in PRECONISATIONS[niveau]:
contenu_md_left += f" - {p}\n"
contenu_md_right = "### Indicateurs :\n\n"
contenu_md_right += "Mise en œuvre : \n"
for niveau, contenu in INDICATEURS.items():
if niveau in niveau_criticite:
contenu_md_right += f"* {colorer_couleurs(niveau)}\n"
for p in INDICATEURS[niveau]:
contenu_md_right += f" - {p}\n"
if ui:
with st.expander("Préconisations et indicateurs génériques"):
col_left, col_right = st.columns([1, 1], gap="small", border=True)
with col_left:
st.markdown(contenu_md_left)
with col_right:
st.markdown(contenu_md_right)
return None, None
return contenu_md_left, contenu_md_right
def afficher_preconisations_specifiques(operation: str, niveau_criticite_operation: dict) -> str:
"""Genere les preconisations specifiques a une operation selon son niveau de criticite."""
contenu_md = "#### Préconisations :\n\n"
contenu_md += "Mise en œuvre : \n"
for niveau, contenu in PRECONISATIONS[operation].items():
if niveau in niveau_criticite_operation[operation]:
contenu_md += f"* {colorer_couleurs(niveau)}\n"
for p in PRECONISATIONS[operation][niveau]:
contenu_md += f" - {p}\n"
return(contenu_md)
def afficher_indicateurs_specifiques(operation: str, niveau_criticite_operation: dict) -> str:
"""Genere les indicateurs specifiques a une operation selon son niveau de criticite."""
contenu_md = "#### Indicateurs :\n\n"
contenu_md += "Mise en œuvre : \n"
for niveau, contenu in INDICATEURS[operation].items():
if niveau in niveau_criticite_operation[operation]:
contenu_md += f"* {colorer_couleurs(niveau)}\n"
for p in INDICATEURS[operation][niveau]:
contenu_md += f" - {p}\n"
return(contenu_md)
def afficher_preconisations_et_indicateurs_specifiques(sel_prod: str, sel_comp: str, sel_miner: str, niveau_criticite_operation: dict) -> None:
"""Affiche les preconisations et indicateurs specifiques pour chaque operation de la chaine."""
for operation in ["Assemblage", "Fabrication", "Traitement", "Extraction"]:
if operation == "Assemblage":
item = sel_prod
elif operation == "Fabrication":
item = sel_comp
else:
item = sel_miner
with st.expander(f"Préconisations et indicateurs spécifiques - {operation}"):
st.markdown(f"### {operation} -> :blue-background[{item}]")
col_left, col_right = st.columns([1, 1], gap="small", border=True)
with col_left:
st.markdown(afficher_preconisations_specifiques(operation, niveau_criticite_operation))
with col_right:
st.markdown(afficher_indicateurs_specifiques(operation, niveau_criticite_operation))
def afficher_preconisations_et_indicateurs(niveau_criticite: dict, sel_prod: str, sel_comp: str, sel_miner: str, poids_A: int, poids_F: int, poids_T: int, poids_E: int, poids_M: int) -> None:
"""Affiche la section complete preconisations et indicateurs (generiques + specifiques)."""
st.markdown("## Préconisations et indicateurs")
afficher_preconisations_et_indicateurs_generiques(niveau_criticite, poids_A, poids_F, poids_T, poids_E, poids_M)
def affectation_poids(poids_operation):
if poids_operation < 3:
niveau_criticite = {"Facile"}
elif poids_operation < 6:
niveau_criticite = {"Facile", "Modérée"}
else:
niveau_criticite = {"Facile", "Modérée", "Difficile"}
return niveau_criticite
niveau_criticite_operation = {}
niveau_criticite_operation["Assemblage"] = affectation_poids(poids_A)
niveau_criticite_operation["Fabrication"] = affectation_poids(poids_F)
niveau_criticite_operation["Traitement"] = affectation_poids(poids_T)
niveau_criticite_operation["Extraction"] = affectation_poids(poids_E)
afficher_preconisations_et_indicateurs_specifiques(sel_prod, sel_comp, sel_miner, niveau_criticite_operation)
def afficher_details_operations(produits, composants, mineraux, sel_prod, sel_comp, sel_miner, details_sections) -> None:
"""Affiche les details complets (descriptions + IHH/ISG) pour chaque operation de la chaine."""
st.markdown("## Détails des opérations")
with st.expander(f"{sel_prod} et Assemblage"):
assemblage_details = details_sections.get(f"{sel_prod}_assemblage", "")
afficher_description(f"{sel_prod} et Assemblage", assemblage_details)
afficher_bloc_ihh_isg("Assemblage", produits[sel_prod]["IHH_Assemblage"], produits[sel_prod]["ISG_Assemblage"], assemblage_details)
with st.expander(f"{sel_comp} et Fabrication"):
fabrication_details = details_sections.get(f"{sel_comp}_fabrication", "")
afficher_description(f"{sel_comp} et Fabrication", fabrication_details)
afficher_bloc_ihh_isg("Fabrication", composants[sel_comp]["IHH_Fabrication"], composants[sel_comp]["ISG_Fabrication"], fabrication_details)
with st.expander(f"{sel_miner} — Vue globale"):
minerai_general = details_sections.get(f"{sel_miner}_general", "")
afficher_description(f"{sel_miner} — Vue globale", minerai_general)
extraction_details = details_sections.get(f"{sel_miner}_extraction", "")
afficher_bloc_ihh_isg("Extraction", mineraux[sel_miner]["IHH_Extraction"], mineraux[sel_miner]["ISG_Extraction"], extraction_details)
traitement_details = details_sections.get(f"{sel_miner}_traitement", "").removesuffix("\n---\n")
afficher_bloc_ihh_isg("Traitement", mineraux[sel_miner]["IHH_Traitement"], mineraux[sel_miner]["ISG_Traitement"], traitement_details)
afficher_caracteristiques_minerai(sel_miner, mineraux[sel_miner], minerai_general)
def initialiser_interface(filepath: str, config_path: str = "assets/config.yaml") -> None:
"""Point d'entree principal pour l'interface du plan d'action : charge donnees et affiche toutes sections."""
produits, composants, mineraux, chains, descriptions, details_sections = parse_chains_md(filepath)
if not chains:
st.warning("Aucune chaîne critique trouvée dans le fichier.")
return
seuils = initialiser_seuils(config_path)
sel_prod, sel_comp, sel_miner, niveau_criticite, \
couleur_A, poids_A, couleur_F, poids_F, couleur_T, poids_T, couleur_E, poids_E, couleur_M, poids_M, \
couleur_A_ihh, couleur_A_isg, couleur_F_ihh, couleur_F_isg, couleur_T_ihh, couleur_T_isg,couleur_E_ihh, couleur_E_isg, couleur_M_ics, couleur_M_ivc \
= tableau_de_bord(chains, produits, composants, mineraux, seuils)
afficher_criticites(produits, composants, mineraux, sel_prod, sel_comp, sel_miner, seuils)
afficher_explications_et_details(
couleur_A, poids_A, couleur_F, poids_F, couleur_T, poids_T, couleur_E, poids_E, couleur_M, poids_M,
produits, composants, mineraux, sel_prod, sel_comp, sel_miner,
couleur_A_ihh, couleur_A_isg, couleur_F_ihh, couleur_F_isg, couleur_T_ihh, couleur_T_isg,couleur_E_ihh, couleur_E_isg, couleur_M_ics, couleur_M_ivc)
afficher_preconisations_et_indicateurs(niveau_criticite, sel_prod, sel_comp, sel_miner, poids_A, poids_F, poids_T, poids_E, poids_M)
afficher_details_operations(produits, composants, mineraux, sel_prod, sel_comp, sel_miner, details_sections)