import networkx as nx import pandas as pd import logging import streamlit as st import json from networkx.drawing.nx_agraph import read_dot # Configuration Gitea from config import DOT_FILE from utils.gitea import ( charger_schema_depuis_gitea ) def extraire_chemins_depuis(G, source): chemins = [] stack = [(source, [source])] while stack: (node, path) = stack.pop() voisins = list(G.successors(node)) if not voisins: chemins.append(path) else: for voisin in voisins: if voisin not in path: stack.append((voisin, path + [voisin])) return chemins def extraire_chemins_vers(G, target, niveau_demande): chemins = [] reverse_G = G.reverse() niveaux = nx.get_node_attributes(G, "niveau") stack = [(target, [target])] while stack: (node, path) = stack.pop() voisins = list(reverse_G.successors(node)) if not voisins: chemin_inverse = list(reversed(path)) contient_niveau = any( int(niveaux.get(n, -1)) == niveau_demande for n in chemin_inverse ) if contient_niveau: chemins.append(chemin_inverse) else: for voisin in voisins: if voisin not in path: stack.append((voisin, path + [voisin])) return chemins def recuperer_donnees(graph, noeuds): donnees = [] ics = {} for noeud in noeuds: try: operation, minerai = noeud.split('_', 1) except ValueError: logging.warning(f"Nom de nœud inattendu : {noeud}") continue if operation == "Traitement": try: fabrications = list(graph.predecessors(minerai)) valeurs = [ int(float(graph.get_edge_data(f, minerai)[0].get('ics', 0)) * 100) for f in fabrications if graph.get_edge_data(f, minerai) ] if valeurs: ics[minerai] = round(sum(valeurs) / len(valeurs)) except Exception as e: logging.warning(f"Erreur criticité pour {noeud} : {e}") ics[minerai] = 50 for noeud in noeuds: try: operation, minerai = noeud.split('_', 1) ihh_pays = int(graph.nodes[noeud].get('ihh_pays', 0)) ihh_acteurs = int(graph.nodes[noeud].get('ihh_acteurs', 0)) ics_val = ics.get(minerai, 50) ics_cat = 1 if ics_val <= 33 else (2 if ics_val <= 66 else 3) donnees.append({ 'categorie': operation, 'nom': minerai, 'ihh_pays': ihh_pays, 'ihh_acteurs': ihh_acteurs, 'ics_minerai': ics_val, 'ics_cat': ics_cat }) except Exception as e: logging.error(f"Erreur sur le nœud {noeud} : {e}", exc_info=True) return pd.DataFrame(donnees) def recuperer_donnees_2(graph, noeuds_2): donnees = [] for minerai in noeuds_2: try: missing = [] if not graph.has_node(minerai): missing.append(minerai) if not graph.has_node(f"Extraction_{minerai}"): missing.append(f"Extraction_{minerai}") if not graph.has_node(f"Reserves_{minerai}"): missing.append(f"Reserves_{minerai}") if missing: print(f"⚠️ Nœuds manquants pour {minerai} : {', '.join(missing)} — Ignoré.") continue ivc = int(graph.nodes[minerai].get('ivc', 0)) ihh_extraction_pays = int(graph.nodes[f"Extraction_{minerai}"].get('ihh_pays', 0)) ihh_reserves_pays = int(graph.nodes[f"Reserves_{minerai}"].get('ihh_pays', 0)) donnees.append({ 'nom': minerai, 'ivc': ivc, 'ihh_extraction': ihh_extraction_pays, 'ihh_reserves': ihh_reserves_pays }) except Exception as e: print(f"Erreur avec le nœud {minerai} : {e}") return donnees def couleur_noeud(n, niveaux, G): niveau = niveaux.get(n, 99) attrs = G.nodes[n] # Niveau 99 : pays géographique avec isg if niveau == 99: isg = int(attrs.get("isg", -1)) return ( "darkred" if isg >= 60 else "orange" if isg >= 31 else "darkgreen" if isg >= 0 else "gray" ) # Niveau 11 ou 12 connecté à un pays géographique if niveau in (11, 12, 1011, 1012): for succ in G.successors(n): if niveaux.get(succ) == 99: isg = int(G.nodes[succ].get("isg", -1)) return ( "darkred" if isg >= 60 else "orange" if isg >= 31 else "darkgreen" if isg >= 0 else "gray" ) # Logique existante pour IHH / IVC if niveau in (10, 1010) and attrs.get("ihh_pays"): ihh = int(attrs["ihh_pays"]) return ( "darkgreen" if ihh <= 15 else "orange" if ihh <= 25 else "darkred" ) elif niveau == 2 and attrs.get("ivc"): ivc = int(attrs["ivc"]) return ( "darkgreen" if ivc <= 15 else "orange" if ivc <= 30 else "darkred" ) return "lightblue" def charger_graphe(): if "G_temp" not in st.session_state: try: if charger_schema_depuis_gitea(DOT_FILE): st.session_state["G_temp"] = read_dot(DOT_FILE) st.session_state["G_temp_ivc"] = st.session_state["G_temp"].copy() dot_file_path = True else: dot_file_path = False except Exception as e: st.error(f"Erreur de lecture du fichier DOT : {e}") dot_file_path = False else: dot_file_path = True if dot_file_path: return dot_file_path else: st.error("Impossible de charger le graphe pour cet onglet.") return dot_file_path