ericjedha commited on
Commit
e6c131d
·
verified ·
1 Parent(s): 4f52d42

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +32 -283
app.py CHANGED
@@ -6,29 +6,6 @@ from sqlalchemy import create_engine, text
6
  from datetime import datetime, timedelta
7
  import os
8
 
9
- import streamlit as st
10
-
11
- import streamlit as st
12
-
13
- # Lire les query params avec la nouvelle API
14
- query_params = st.query_params
15
- page_param = query_params.get("page", ["dashboard"])[0] # valeur par défaut = dashboard
16
-
17
- # Associer la query param aux fonctions de pages
18
- page_mapping = {
19
- "dashboard": page_dashboard,
20
- "fraudes": page_frauds,
21
- "non-fraudes": page_no_frauds
22
- }
23
-
24
- # Fonction utilitaire pour appeler la page correspondante
25
- def open_page_from_query_param():
26
- page_func = page_mapping.get(page_param.lower(), page_dashboard)
27
- page_func()
28
-
29
-
30
-
31
-
32
  # ========================== CONFIGURATION ==========================
33
  st.set_page_config(
34
  page_title="Fraud Detection Dashboard",
@@ -47,7 +24,6 @@ COLOR_SAVED = "#FFD700" # Or pour l'argent économisé
47
  def get_db_connection():
48
  """Connexion à Neon DB via Hugging Face Secret"""
49
  try:
50
- # Récupérer l'URL depuis les secrets Hugging Face
51
  database_url = os.environ.get("NEON_DB_FRAUD_URL")
52
  if not database_url:
53
  st.error("❌ Variable NEON_DB_FRAUD_URL non trouvée dans les secrets Hugging Face")
@@ -60,301 +36,79 @@ def get_db_connection():
60
  st.stop()
61
 
62
  # ========================== REQUÊTES SQL ==========================
63
- @st.cache_data(ttl=60) # Cache de 60 secondes
64
  def load_all_data():
65
- """Charge toutes les transactions"""
66
  engine = get_db_connection()
67
  query = text("""
68
  SELECT
69
- trans_num,
70
- merchant,
71
- category,
72
- amt,
73
- gender,
74
- city,
75
- zip,
76
- city_pop,
77
- job,
78
- hour,
79
- day,
80
- month,
81
- year,
82
- pred_is_fraud,
83
- is_fraud_ground_truth,
84
- transaction_time,
85
- created_at
86
  FROM fraud_predictions
87
  ORDER BY created_at DESC
88
  """)
89
-
90
  with engine.connect() as conn:
91
  df = pd.read_sql(query, conn)
92
-
93
- # Convertir created_at en datetime
94
  df['created_at'] = pd.to_datetime(df['created_at'])
95
  return df
96
 
97
  def load_last_24h_data():
98
- """Charge les transactions des dernières 24h"""
99
  engine = get_db_connection()
100
  query = text("""
101
- SELECT
102
- trans_num,
103
- merchant,
104
- category,
105
- amt,
106
- gender,
107
- city,
108
- pred_is_fraud,
109
- created_at
110
  FROM fraud_predictions
111
  WHERE created_at >= NOW() - INTERVAL '24 HOURS'
112
  ORDER BY created_at DESC
113
  """)
114
-
115
  with engine.connect() as conn:
116
  df = pd.read_sql(query, conn)
117
-
118
  df['created_at'] = pd.to_datetime(df['created_at'])
119
  return df
120
 
121
  def load_last_7_days_stats():
122
- """Charge les stats agrégées des 7 derniers jours"""
123
  engine = get_db_connection()
124
  query = text("""
125
- SELECT
126
- DATE(created_at) as date,
127
- SUM(CASE WHEN pred_is_fraud = 1 THEN 1 ELSE 0 END) as frauds,
128
- SUM(CASE WHEN pred_is_fraud = 0 THEN 1 ELSE 0 END) as no_frauds
129
  FROM fraud_predictions
130
  WHERE created_at >= NOW() - INTERVAL '7 DAYS'
131
  GROUP BY DATE(created_at)
132
  ORDER BY date ASC
133
  """)
134
-
135
  with engine.connect() as conn:
136
  df = pd.read_sql(query, conn)
137
-
138
  return df
139
 
140
- # ========================== PAGE: DASHBOARD ==========================
141
  def page_dashboard():
142
- st.title("🕵🏻 Fraud Detection Dashboard")
143
-
144
- # Bouton refresh
145
- if st.button("🔄 Refresh Data", type="primary"):
146
- st.cache_data.clear()
147
- st.rerun()
148
-
149
- # Charger les données
150
- with st.spinner("Chargement des données..."):
151
- df_all = load_all_data()
152
- df_7days = load_last_7_days_stats()
153
-
154
- if df_all.empty:
155
- st.warning("⚠️ Aucune donnée disponible dans la base de données")
156
- return
157
-
158
- # ========================== MÉTRIQUES ==========================
159
- total_frauds = (df_all['pred_is_fraud'] == 1).sum()
160
- total_no_frauds = (df_all['pred_is_fraud'] == 0).sum()
161
-
162
- # Calcul du montant économisé (fraude détectée * montant * 1.5)
163
- saved_amount = df_all[df_all['pred_is_fraud'] == 1]['amt'].sum() * 1.5
164
-
165
- col1, col2, col3 = st.columns(3)
166
-
167
- with col1:
168
- st.markdown(f"""
169
- <div style="background-color: {COLOR_FRAUD}; padding: 20px; border-radius: 10px; text-align: center;">
170
- <h3 style="color: white; margin: 0;">🚨 Frauds</h3>
171
- <h1 style="color: white; margin: 10px 0;">{total_frauds}</h1>
172
- </div>
173
- """, unsafe_allow_html=True)
174
-
175
- with col2:
176
- st.markdown(f"""
177
- <div style="background-color: {COLOR_NO_FRAUD}; padding: 20px; border-radius: 10px; text-align: center;">
178
- <h3 style="color: white; margin: 0;">✅ No Frauds</h3>
179
- <h1 style="color: white; margin: 10px 0;">{total_no_frauds}</h1>
180
- </div>
181
- """, unsafe_allow_html=True)
182
-
183
- with col3:
184
- st.markdown(f"""
185
- <div style="background-color: {COLOR_SAVED}; padding: 20px; border-radius: 10px; text-align: center;">
186
- <h3 style="color: white; margin: 0;">💰 Saved Amount</h3>
187
- <h1 style="color: white; margin: 10px 0;">${saved_amount:,.2f}</h1>
188
- </div>
189
- """, unsafe_allow_html=True)
190
-
191
- st.markdown("<br>", unsafe_allow_html=True)
192
-
193
- # ========================== GRAPHIQUES ==========================
194
- col_pie, col_saved_detail = st.columns([1, 1])
195
-
196
- with col_pie:
197
- # Camembert Fraud vs No Fraud
198
- fig_pie = go.Figure(data=[go.Pie(
199
- labels=['Frauds', 'No Frauds'],
200
- values=[total_frauds, total_no_frauds],
201
- marker=dict(colors=[COLOR_FRAUD, COLOR_NO_FRAUD]),
202
- hole=0.4,
203
- textinfo='label+percent',
204
- textfont_size=14
205
- )])
206
- fig_pie.update_layout(
207
- title="Distribution Fraud vs No Fraud",
208
- showlegend=True,
209
- height=400
210
- )
211
- st.plotly_chart(fig_pie, use_container_width=True)
212
-
213
- with col_saved_detail:
214
- # Détails du montant économisé
215
- total_fraud_amount = df_all[df_all['pred_is_fraud'] == 1]['amt'].sum()
216
- additional_costs = total_fraud_amount * 0.5 # 50% de frais supplémentaires
217
-
218
- st.markdown("### 💵 Breakdown of Saved Amount")
219
- st.markdown(f"""
220
- - **Total Fraud Amounts**: ${total_fraud_amount:,.2f}
221
- - **Estimated Additional Costs** (chargebacks, fees): ${additional_costs:,.2f}
222
- - **Total Saved**: ${saved_amount:,.2f}
223
- """)
224
-
225
- # Mini barchart
226
- fig_breakdown = go.Figure(data=[
227
- go.Bar(name='Fraud Amount', x=['Saved'], y=[total_fraud_amount], marker_color=COLOR_FRAUD),
228
- go.Bar(name='Additional Costs', x=['Saved'], y=[additional_costs], marker_color=COLOR_SAVED)
229
- ])
230
- fig_breakdown.update_layout(
231
- barmode='stack',
232
- showlegend=True,
233
- height=250,
234
- yaxis_title="Amount ($)"
235
- )
236
- st.plotly_chart(fig_breakdown, use_container_width=True)
237
-
238
- # ========================== GRAPHIQUE EMPILÉ 7 JOURS ==========================
239
- st.markdown("### 📊 Fraud Trend - Last 7 Days")
240
-
241
- if not df_7days.empty:
242
- fig_trend = go.Figure()
243
-
244
- fig_trend.add_trace(go.Bar(
245
- name='Frauds',
246
- x=df_7days['date'],
247
- y=df_7days['frauds'],
248
- marker_color=COLOR_FRAUD
249
- ))
250
-
251
- fig_trend.add_trace(go.Bar(
252
- name='No Frauds',
253
- x=df_7days['date'],
254
- y=df_7days['no_frauds'],
255
- marker_color=COLOR_NO_FRAUD
256
- ))
257
-
258
- fig_trend.update_layout(
259
- barmode='stack',
260
- xaxis_title="Date",
261
- yaxis_title="Number of Transactions",
262
- height=400,
263
- showlegend=True,
264
- hovermode='x unified'
265
- )
266
-
267
- st.plotly_chart(fig_trend, use_container_width=True)
268
- else:
269
- st.info("Pas encore de données sur 7 jours")
270
 
271
- # ========================== PAGE: FRAUDES (J-1) ==========================
272
  def page_frauds():
273
- st.title("🚨 Fraudes Détectées (Dernières 24h)")
274
-
275
- # Bouton refresh
276
- if st.button("🔄 Refresh Data", type="primary"):
277
- st.cache_data.clear()
278
- st.rerun()
279
-
280
- with st.spinner("Chargement des fraudes..."):
281
- df = load_last_24h_data()
282
- df_frauds = df[df['pred_is_fraud'] == 1]
283
-
284
- # Métrique
285
- st.markdown(f"""
286
- <div style="background-color: {COLOR_FRAUD}; padding: 15px; border-radius: 10px; text-align: center; margin-bottom: 20px;">
287
- <h2 style="color: white; margin: 0;">🚨 {len(df_frauds)} Fraudes détectées</h2>
288
- </div>
289
- """, unsafe_allow_html=True)
290
-
291
- if df_frauds.empty:
292
- st.success("✅ Aucune fraude détectée dans les dernières 24h !")
293
- else:
294
- # Afficher le tableau
295
- st.dataframe(
296
- df_frauds[[
297
- 'trans_num', 'merchant', 'category', 'amt',
298
- 'city', 'gender', 'created_at'
299
- ]].sort_values('created_at', ascending=False),
300
- use_container_width=True,
301
- height=600
302
- )
303
-
304
- # Stats supplémentaires
305
- col1, col2, col3 = st.columns(3)
306
- with col1:
307
- st.metric("Montant total", f"${df_frauds['amt'].sum():,.2f}")
308
- with col2:
309
- st.metric("Montant moyen", f"${df_frauds['amt'].mean():,.2f}")
310
- with col3:
311
- st.metric("Montant max", f"${df_frauds['amt'].max():,.2f}")
312
 
313
- # ========================== PAGE: NON FRAUDES (J-1) ==========================
314
  def page_no_frauds():
315
- st.title("✅ Transactions Légitimes (Dernières 24h)")
316
-
317
- # Bouton refresh
318
- if st.button("🔄 Refresh Data", type="primary"):
319
- st.cache_data.clear()
320
- st.rerun()
321
-
322
- with st.spinner("Chargement des transactions légitimes..."):
323
- df = load_last_24h_data()
324
- df_no_frauds = df[df['pred_is_fraud'] == 0]
325
-
326
- # Métrique
327
- st.markdown(f"""
328
- <div style="background-color: {COLOR_NO_FRAUD}; padding: 15px; border-radius: 10px; text-align: center; margin-bottom: 20px;">
329
- <h2 style="color: white; margin: 0;">✅ {len(df_no_frauds)} Transactions légitimes</h2>
330
- </div>
331
- """, unsafe_allow_html=True)
332
-
333
- if df_no_frauds.empty:
334
- st.warning("⚠️ Aucune transaction légitime dans les dernières 24h")
335
- else:
336
- # Afficher le tableau
337
- st.dataframe(
338
- df_no_frauds[[
339
- 'trans_num', 'merchant', 'category', 'amt',
340
- 'city', 'gender', 'created_at'
341
- ]].sort_values('created_at', ascending=False),
342
- use_container_width=True,
343
- height=600
344
- )
345
-
346
- # Stats supplémentaires
347
- col1, col2, col3 = st.columns(3)
348
- with col1:
349
- st.metric("Montant total", f"${df_no_frauds['amt'].sum():,.2f}")
350
- with col2:
351
- st.metric("Montant moyen", f"${df_no_frauds['amt'].mean():,.2f}")
352
- with col3:
353
- st.metric("Montant max", f"${df_no_frauds['amt'].max():,.2f}")
354
 
355
  # ========================== NAVIGATION ==========================
356
  def main():
357
- # Sidebar
358
  st.sidebar.title("Navigation")
359
  page = st.sidebar.radio(
360
  "Go to",
@@ -371,13 +125,8 @@ def main():
371
  **Données** : Dernières 24h pour les pages de détail.
372
  """)
373
 
374
- # Router
375
- if page == "🏠 Dashboard":
376
- page_dashboard()
377
- elif page == "🚨 Fraudes (24h)":
378
- page_frauds()
379
- elif page == "✅ Non Fraudes (24h)":
380
- page_no_frauds()
381
 
382
  if __name__ == "__main__":
383
- main()
 
6
  from datetime import datetime, timedelta
7
  import os
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  # ========================== CONFIGURATION ==========================
10
  st.set_page_config(
11
  page_title="Fraud Detection Dashboard",
 
24
  def get_db_connection():
25
  """Connexion à Neon DB via Hugging Face Secret"""
26
  try:
 
27
  database_url = os.environ.get("NEON_DB_FRAUD_URL")
28
  if not database_url:
29
  st.error("❌ Variable NEON_DB_FRAUD_URL non trouvée dans les secrets Hugging Face")
 
36
  st.stop()
37
 
38
  # ========================== REQUÊTES SQL ==========================
39
+ @st.cache_data(ttl=60)
40
  def load_all_data():
 
41
  engine = get_db_connection()
42
  query = text("""
43
  SELECT
44
+ trans_num, merchant, category, amt, gender, city, zip, city_pop, job,
45
+ hour, day, month, year, pred_is_fraud, is_fraud_ground_truth,
46
+ transaction_time, created_at
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  FROM fraud_predictions
48
  ORDER BY created_at DESC
49
  """)
 
50
  with engine.connect() as conn:
51
  df = pd.read_sql(query, conn)
 
 
52
  df['created_at'] = pd.to_datetime(df['created_at'])
53
  return df
54
 
55
  def load_last_24h_data():
 
56
  engine = get_db_connection()
57
  query = text("""
58
+ SELECT trans_num, merchant, category, amt, gender, city, pred_is_fraud, created_at
 
 
 
 
 
 
 
 
59
  FROM fraud_predictions
60
  WHERE created_at >= NOW() - INTERVAL '24 HOURS'
61
  ORDER BY created_at DESC
62
  """)
 
63
  with engine.connect() as conn:
64
  df = pd.read_sql(query, conn)
 
65
  df['created_at'] = pd.to_datetime(df['created_at'])
66
  return df
67
 
68
  def load_last_7_days_stats():
 
69
  engine = get_db_connection()
70
  query = text("""
71
+ SELECT DATE(created_at) as date,
72
+ SUM(CASE WHEN pred_is_fraud = 1 THEN 1 ELSE 0 END) as frauds,
73
+ SUM(CASE WHEN pred_is_fraud = 0 THEN 1 ELSE 0 END) as no_frauds
 
74
  FROM fraud_predictions
75
  WHERE created_at >= NOW() - INTERVAL '7 DAYS'
76
  GROUP BY DATE(created_at)
77
  ORDER BY date ASC
78
  """)
 
79
  with engine.connect() as conn:
80
  df = pd.read_sql(query, conn)
 
81
  return df
82
 
83
+ # ========================== PAGES ==========================
84
  def page_dashboard():
85
+ # ... ton code existant pour le dashboard ...
86
+ pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
 
88
  def page_frauds():
89
+ # ... ton code existant pour la page fraudes ...
90
+ pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
 
92
  def page_no_frauds():
93
+ # ... ton code existant pour la page non-fraudes ...
94
+ pass
95
+
96
+ # ========================== ROUTING VIA QUERY PARAM ==========================
97
+ query_params = st.query_params
98
+ page_param = query_params.get("page", ["dashboard"])[0]
99
+
100
+ page_mapping = {
101
+ "dashboard": page_dashboard,
102
+ "fraudes": page_frauds,
103
+ "non-fraudes": page_no_frauds
104
+ }
105
+
106
+ def open_page_from_query_param():
107
+ page_func = page_mapping.get(page_param.lower(), page_dashboard)
108
+ page_func()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
  # ========================== NAVIGATION ==========================
111
  def main():
 
112
  st.sidebar.title("Navigation")
113
  page = st.sidebar.radio(
114
  "Go to",
 
125
  **Données** : Dernières 24h pour les pages de détail.
126
  """)
127
 
128
+ # Router avec query param
129
+ open_page_from_query_param()
 
 
 
 
 
130
 
131
  if __name__ == "__main__":
132
+ main()