klydekushy commited on
Commit
5da6a2a
·
verified ·
1 Parent(s): d8acc89

Update src/modules/ontology_graph.py

Browse files
Files changed (1) hide show
  1. src/modules/ontology_graph.py +181 -0
src/modules/ontology_graph.py CHANGED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import networkx as nx
4
+ from pyvis.network import Network
5
+ import tempfile
6
+ import streamlit.components.v1 as components
7
+ import hashlib
8
+
9
+ # --- GÉNÉRATEUR DE COULEURS GOTHAM AUTOMATIQUE ---
10
+ def get_auto_gotham_color(entity_type):
11
+ """
12
+ Génère une couleur unique et déterministe pour chaque type d'entité.
13
+ Ne nécessite aucune définition manuelle.
14
+ Assure un look 'Gotham' (Teintes froides, saturées, lisibles sur fond noir).
15
+ """
16
+ # 1. On hache le nom du type (ex: "Client", "Pret") pour avoir un nombre unique
17
+ hash_object = hashlib.md5(entity_type.encode())
18
+ hash_hex = hash_object.hexdigest()
19
+
20
+ # 2. On utilise le hash pour choisir une teinte (Hue)
21
+ # Gotham Palette Logic : On favorise les Bleus, Cyans, Violets, Ors, Verts d'eau
22
+ # On évite les marrons ou roses bonbons trop vifs.
23
+
24
+ # Liste de palettes "Tech/Safe"
25
+ gotham_palettes = [
26
+ "#25C1F7", # Cyan
27
+ "#D4AF37", # Gold
28
+ "#54BD4B", # Green Tech
29
+ "#9B59B6", # Purple
30
+ "#E74C3C", # Red Alert
31
+ "#34495E", # Steel Blue
32
+ "#1ABC9C", # Turquoise
33
+ "#F39C12", # Orange
34
+ "#7F8C8D", # Grey
35
+ "#2980B9" # Deep Blue
36
+ ]
37
+
38
+ # On prend une couleur dans la liste modulo la taille de la liste
39
+ # Cela garantit que tous les "Clients" auront toujours la même couleur, idem pour "Prêts"
40
+ idx = int(hash_hex, 16) % len(gotham_palettes)
41
+ return gotham_palettes[idx]
42
+
43
+ def show_ontology_graph(client, sheet_name):
44
+ st.header("🕸️ ONTOLOGIE : LE SYSTÈME NERVEUX")
45
+
46
+ # 1. Chargement des Données
47
+ try:
48
+ sh = client.open(sheet_name)
49
+ # On utilise get_all_records pour avoir des dicts
50
+ df_clients = pd.DataFrame(sh.worksheet("Clients_KYC").get_all_records())
51
+ df_prets = pd.DataFrame(sh.worksheet("Prets_Master").get_all_records())
52
+ except Exception as e:
53
+ st.error(f"Erreur de lecture des données : {e}")
54
+ return
55
+
56
+ # 2. Construction du Graphe (NetworkX)
57
+ G = nx.Graph()
58
+
59
+ # --- NOEUD CENTRAL : HQ ---
60
+ hq_color = get_auto_gotham_color("HQ")
61
+ # shape="box" pour faire une carte
62
+ G.add_node("VORTEX_HQ", label="🏢 VORTEX HQ\n(Siège Social)", title="Centre Névralgique", color=hq_color, shape="box", font={'color': 'white'})
63
+
64
+ # --- COUCHE 1 : LES CLIENTS (Cartes) ---
65
+ if not df_clients.empty:
66
+ client_color = get_auto_gotham_color("Client") # Automatique
67
+
68
+ for _, row in df_clients.iterrows():
69
+ c_id = str(row['ID_Client'])
70
+ c_nom = str(row['Nom_Complet'])
71
+ c_ville = str(row.get('Ville', 'Inconnu'))
72
+ c_rev = str(row.get('Revenus_Mensuels', '0'))
73
+
74
+ # FORMATTAGE TYPE "CARTE" AVEC SAUTS DE LIGNE
75
+ # Pyvis supporte le multi-ligne dans les labels
76
+ card_label = f"👤 {c_nom}\n🆔 {c_id}\n📍 {c_ville}"
77
+
78
+ # Métadonnées complètes au survol
79
+ hover_html = f"""
80
+ <b>CLIENT CARD</b><br>
81
+ ----------------<br>
82
+ Nom: {c_nom}<br>
83
+ ID: {c_id}<br>
84
+ Revenus: {c_rev} XOF
85
+ """
86
+
87
+ G.add_node(c_id, label=card_label, title=hover_html, color=client_color, shape="box", font={'color': 'white'})
88
+
89
+ # Lien HQ
90
+ G.add_edge("VORTEX_HQ", c_id, color="#404854", weight=2)
91
+
92
+ # --- COUCHE 2 : LES PRÊTS (Cartes) ---
93
+ if not df_prets.empty:
94
+ # Ici on peut varier la couleur selon le STATUT automatiquement
95
+ # ex: "Pret_ACTIF", "Pret_CLOTURE"
96
+
97
+ for _, row in df_prets.iterrows():
98
+ p_id = str(row['ID_Pret'])
99
+ c_ref = str(row['ID_Client'])
100
+ montant = str(row['Montant_Capital'])
101
+ statut = str(row['Statut'])
102
+ duree = str(row['Duree_Semaines'])
103
+
104
+ # Couleur auto basée sur le statut (ex: Pret_ACTIF aura une couleur, Pret_CLOSE une autre)
105
+ loan_color = get_auto_gotham_color(f"Pret_{statut}")
106
+
107
+ # FORMATTAGE CARTE FINANCIÈRE
108
+ card_label = f"📄 CONTRAT {statut}\n💰 {montant} XOF\n⏱️ {duree} Semaines"
109
+
110
+ hover_html = f"""
111
+ <b>LOAN CARD</b><br>
112
+ ----------------<br>
113
+ Ref: {p_id}<br>
114
+ Montant: {montant}<br>
115
+ Status: {statut}
116
+ """
117
+
118
+ G.add_node(p_id, label=card_label, title=hover_html, color=loan_color, shape="box", font={'color': 'white'})
119
+
120
+ # Lien Relationnel Client <-> Prêt
121
+ # (Si le client existe dans le graphe)
122
+ if c_ref in list(G.nodes):
123
+ G.add_edge(c_ref, p_id, color="#404854", weight=1, label="Possède")
124
+
125
+ # 3. Visualisation Interactive (PyVis)
126
+ nt = Network(height="750px", width="100%", bgcolor="#10161A", font_color="white", notebook=False)
127
+ nt.from_nx(G)
128
+
129
+ # OPTIONS PHYSIQUES POUR "EFFET CARTE"
130
+ # On force les noeuds à s'écarter davantage car ce sont des rectangles
131
+ nt.options = {
132
+ "nodes": {
133
+ "borderWidth": 2,
134
+ "borderWidthSelected": 4,
135
+ "font": {"size": 16, "face": "Space Grotesk", "align": "center"},
136
+ "shadow": {"enabled": True, "color": "rgba(0,0,0,0.5)", "size": 10}
137
+ },
138
+ "edges": {
139
+ "color": {"color": "#404854", "highlight": "#25C1F7"},
140
+ "smooth": {"type": "continuous"},
141
+ "width": 1
142
+ },
143
+ "physics": {
144
+ "forceAtlas2Based": {
145
+ "gravitationalConstant": -100, # Répulsion forte pour ne pas chevaucher les cartes
146
+ "centralGravity": 0.01,
147
+ "springLength": 200, # Liens longs
148
+ "springConstant": 0.05
149
+ },
150
+ "solver": "forceAtlas2Based",
151
+ "stabilization": {"enabled": True, "iterations": 100}
152
+ },
153
+ "interaction": {
154
+ "hover": True,
155
+ "tooltipDelay": 200
156
+ }
157
+ }
158
+
159
+ # Rendu
160
+ try:
161
+ path = tempfile.gettempdir() + "/ontology.html"
162
+ nt.save_graph(path)
163
+ with open(path, 'r', encoding='utf-8') as f:
164
+ source_code = f.read()
165
+
166
+ # Injection CSS pour forcer le style Gotham sur les tooltips
167
+ custom_css = """
168
+ <style>
169
+ div.vis-tooltip {
170
+ background-color: #182026 !important;
171
+ color: #F5F8FA !important;
172
+ border: 1px solid #25C1F7 !important;
173
+ font-family: 'Space Grotesk', sans-serif !important;
174
+ box-shadow: 0px 0px 15px rgba(37, 193, 247, 0.3) !important;
175
+ }
176
+ </style>
177
+ """
178
+ components.html(custom_css + source_code, height=800)
179
+
180
+ except Exception as e:
181
+ st.error(f"Erreur de rendu graphique : {e}")