klydekushy commited on
Commit
628dd14
·
verified ·
1 Parent(s): efaca3a

Update src/modules/ml_dashboard.py

Browse files
Files changed (1) hide show
  1. src/modules/ml_dashboard.py +159 -29
src/modules/ml_dashboard.py CHANGED
@@ -5,35 +5,165 @@ import plotly.graph_objects as go
5
  from datetime import datetime, timedelta
6
  import numpy as np
7
 
8
- def show_ml_features(client, sheet_name):
9
- # === En-tête et métriques principales ===
10
- st.header("OBJECT EXPLORER")
11
- st.success(f"✅ Liaison Directe établie avec '{sheet_name}'")
12
-
13
- col1, col2, col3 = st.columns(3)
14
- col1.metric("CAPITAL DEHORS", "646K XOF", "▲ 5%")
15
- col2.metric("FLUX ATTENDU (J+7)", "FCFA 2,2M XOF", "▼ 2%")
16
- col3.metric("SCORE LIQUIDITÉ", "8.5/10")
17
-
18
- st.divider()
19
- st.subheader("VUE ONTOLOGIQUE : CLIENTS")
20
-
21
- # Fonction helper pour charger les données
22
- def get_data_from_sheet(sheet_name_tab):
23
- try:
24
- sh = client.open(sheet_name)
25
- ws = sh.worksheet(sheet_name_tab)
26
- return pd.DataFrame(ws.get_all_records())
27
- except:
28
- return pd.DataFrame()
29
-
30
- df_clients = get_data_from_sheet("Clients_KYC")
31
- if not df_clients.empty:
32
- st.dataframe(df_clients, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  else:
34
- st.info("ℹ️ L'onglet 'Clients_KYC' est vide ou n'existe pas encore.")
35
-
36
- st.divider()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
  # === GOTHAM SURVEILLANCE THEME CSS ===
39
  st.markdown("""
@@ -430,7 +560,7 @@ def show_ml_features(client, sheet_name):
430
 
431
  # 6. FEATURES TEMPORELLES
432
  with st.expander("Features Temporelles", expanded=True):
433
- df_full['Date_Creation'] = pd.to_datetime(df_full['Date_Creation'], errors='coerce')
434
  df_full['Anciennete_Client_Jours'] = (datetime.now() - df_full['Date_Creation']).dt.days
435
  df_full['Anciennete_Client_Mois'] = (df_full['Anciennete_Client_Jours'] / 30.44).round(1)
436
 
 
5
  from datetime import datetime, timedelta
6
  import numpy as np
7
 
8
+
9
+ # Fonction helper pour charger les données
10
+ def get_data_from_sheet(sheet_name_tab):
11
+ try:
12
+ sh = client.open(sheet_name)
13
+ ws = sh.worksheet(sheet_name_tab)
14
+ return pd.DataFrame(ws.get_all_records())
15
+ except:
16
+ return pd.DataFrame()
17
+
18
+ # Chargement des données nécessaires
19
+ df_prets_master = get_data_from_sheet("Prets_Master")
20
+ df_prets_update = get_data_from_sheet("Prets_Update")
21
+ df_capital_invest = get_data_from_sheet("Capital_Investissement")
22
+ df_clients = get_data_from_sheet("Clients_KYC")
23
+
24
+ # === En-tête ===
25
+ st.header("OBJECT EXPLORER")
26
+ st.success(f"✅ Liaison Directe établie avec '{sheet_name}'")
27
+
28
+ # === CALCULS POUR LES MÉTRIQUES ===
29
+
30
+ # 1. Capital de l'entreprise (somme de Capital_Investissement)
31
+ capital_entreprise = 0
32
+ capital_variation = 0
33
+ if not df_capital_invest.empty and 'Capital' in df_capital_invest.columns:
34
+ df_capital_invest['Capital_Num'] = pd.to_numeric(
35
+ df_capital_invest['Capital'].astype(str).str.replace('FCFA', '').str.replace(' ', '').str.strip(),
36
+ errors='coerce'
37
+ ).fillna(0)
38
+ capital_entreprise = df_capital_invest['Capital_Num'].sum()
39
+ # Variation = dernier vs précédent
40
+ if len(df_capital_invest) > 1:
41
+ derniere_entree = df_capital_invest.iloc[-1]['Capital_Num']
42
+ avant_derniere = df_capital_invest.iloc[-2]['Capital_Num']
43
+ if avant_derniere > 0:
44
+ capital_variation = ((derniere_entree - avant_derniere) / avant_derniere) * 100
45
+
46
+ # 2. Capital dehors (prêts non remboursés)
47
+ capital_dehors = 0
48
+ if not df_prets_master.empty and 'Montant_Capital' in df_prets_master.columns:
49
+ df_prets_master['Montant_Capital_Num'] = pd.to_numeric(df_prets_master['Montant_Capital'], errors='coerce').fillna(0)
50
+ capital_dehors = df_prets_master[df_prets_master['Statut'] != 'REMBOURSE']['Montant_Capital_Num'].sum()
51
+
52
+ # 3. Flux attendu (Montant_Total des prêts non remboursés)
53
+ flux_attendu = 0
54
+ flux_variation = 0
55
+ if not df_prets_master.empty and 'Montant_Total' in df_prets_master.columns:
56
+ df_prets_master['Montant_Total_Num'] = pd.to_numeric(df_prets_master['Montant_Total'], errors='coerce').fillna(0)
57
+ flux_attendu = df_prets_master[df_prets_master['Statut'] != 'REMBOURSE']['Montant_Total_Num'].sum()
58
+ # Variation = différence avec capital dehors
59
+ if capital_dehors > 0:
60
+ flux_variation = ((flux_attendu - capital_dehors) / capital_dehors) * 100
61
+
62
+ # 4. Score de liquidité
63
+ score_liquidite = 0
64
+ score_tendance = "Stable"
65
+ if capital_entreprise > 0:
66
+ score_liquidite = (capital_dehors / capital_entreprise) * 10
67
+ score_liquidite = min(score_liquidite, 10)
68
+ # Tendance basée sur le ratio
69
+ ratio = (capital_dehors / capital_entreprise) * 100
70
+ if ratio > 70:
71
+ score_tendance = "▲ Élevé"
72
+ elif ratio > 40:
73
+ score_tendance = "→ Moyen"
74
  else:
75
+ score_tendance = "▼ Faible"
76
+
77
+ # 5. Bénéfices nets 2026
78
+ benefices_nets = 0
79
+ benefices_variation = 0
80
+ if not df_prets_master.empty and 'Cout_Credit' in df_prets_master.columns:
81
+ df_prets_master['Cout_Credit_Num'] = pd.to_numeric(df_prets_master['Cout_Credit'], errors='coerce').fillna(0)
82
+ benefices_nets = df_prets_master['Cout_Credit_Num'].sum()
83
+ # Simulation variation (peut être calculée vs mois précédent si date disponible)
84
+ benefices_variation = 5.2 # Placeholder - à calculer avec historique
85
+
86
+ # 6. Objectif 2026
87
+ objectif_2026 = 2_200_000
88
+ progression_objectif = (benefices_nets / objectif_2026) * 100 if objectif_2026 > 0 else 0
89
+
90
+ # 7. Reste à générer
91
+ reste_a_generer = max(0, objectif_2026 - benefices_nets)
92
+ reste_variation = -((reste_a_generer / objectif_2026) * 100) if objectif_2026 > 0 else 0
93
+
94
+ # 8. Reste à payer
95
+ reste_a_payer = 0
96
+ nb_prets_actifs = 0
97
+ if not df_prets_master.empty and 'Montant_Total' in df_prets_master.columns:
98
+ prets_actifs_master = df_prets_master[df_prets_master['Statut'] != 'REMBOURSE']
99
+ nb_prets_actifs = len(prets_actifs_master)
100
+ reste_a_payer = prets_actifs_master['Montant_Total_Num'].sum()
101
+
102
+ # Ajouter prets_update (sans doublon)
103
+ if not df_prets_update.empty and 'ID_Pret' in df_prets_update.columns:
104
+ df_prets_update['Montant_Total_Num'] = pd.to_numeric(df_prets_update['Montant_Total'], errors='coerce').fillna(0)
105
+ prets_update_uniques = df_prets_update[
106
+ (~df_prets_update['ID_Pret'].isin(prets_actifs_master['ID_Pret'].tolist())) &
107
+ (df_prets_update['Statut'] != 'REMBOURSE')
108
+ ]
109
+ reste_a_payer += prets_update_uniques['Montant_Total_Num'].sum()
110
+ nb_prets_actifs += len(prets_update_uniques)
111
+
112
+ # === AFFICHAGE DES MÉTRIQUES (2 lignes de 4) ===
113
+
114
+ # Ligne 1
115
+ col1, col2, col3, col4 = st.columns(4)
116
+
117
+ col1.metric(
118
+ "CAPITAL D'INVESTISSEMENT",
119
+ f"{capital_entreprise:,.0f} XOF".replace(',', ' '),
120
+ delta=f"{'▲' if capital_variation > 0 else '▼'} {abs(capital_variation):.1f}%" if capital_variation != 0 else "Stable"
121
+ )
122
+
123
+ col2.metric(
124
+ "CAPITAL DEHORS",
125
+ f"{capital_dehors:,.0f} XOF".replace(',', ' '),
126
+ delta=f"{(capital_dehors/capital_entreprise*100 if capital_entreprise > 0 else 0):.1f}% du capital investi"
127
+ )
128
+
129
+ col3.metric(
130
+ "FLUX ATTENDU",
131
+ f"{flux_attendu:,.0f} XOF".replace(',', ' '),
132
+ delta=f"{'▲' if flux_variation > 0 else '▼'} {abs(flux_variation):.1f}% vs capital" if flux_variation != 0 else "Stable"
133
+ )
134
+
135
+ col4.metric(
136
+ "SCORE LIQUIDITÉ",
137
+ f"{score_liquidite:.1f}/10",
138
+ delta=score_tendance
139
+ )
140
+
141
+ # Ligne 2
142
+ col5, col6, col7, col8 = st.columns(4)
143
+
144
+ col5.metric(
145
+ "BÉNÉFICES NETS 2026",
146
+ f"{benefices_nets:,.0f} XOF".replace(',', ' '),
147
+ delta=f"▲ {benefices_variation:.1f}%" if benefices_variation > 0 else "Stable"
148
+ )
149
+
150
+ col6.metric(
151
+ "OBJECTIF 2026",
152
+ f"{objectif_2026:,.0f} XOF".replace(',', ' '),
153
+ delta=f"{progression_objectif:.1f}% atteint"
154
+ )
155
+
156
+ col7.metric(
157
+ "RESTE À GÉNÉRER",
158
+ f"{reste_a_generer:,.0f} XOF".replace(',', ' '),
159
+ delta=f"▼ {abs(reste_variation):.1f}%" if reste_a_generer > 0 else "✓ Objectif atteint !"
160
+ )
161
+
162
+ col8.metric(
163
+ "RESTE À PAYER",
164
+ f"{reste_a_payer:,.0f} XOF".replace(',', ' '),
165
+ delta=f"{nb_prets_actifs} prêt{'s' if nb_prets_actifs > 1 else ''} actif{'s' if nb_prets_actifs > 1 else ''}"
166
+ )
167
 
168
  # === GOTHAM SURVEILLANCE THEME CSS ===
169
  st.markdown("""
 
560
 
561
  # 6. FEATURES TEMPORELLES
562
  with st.expander("Features Temporelles", expanded=True):
563
+ df_full['Date_Creation'] = pd.to_datetime(df_full.get('Date_Creation', pd.Series()), errors='coerce')
564
  df_full['Anciennete_Client_Jours'] = (datetime.now() - df_full['Date_Creation']).dt.days
565
  df_full['Anciennete_Client_Mois'] = (df_full['Anciennete_Client_Jours'] / 30.44).round(1)
566