ericjedha commited on
Commit
b920103
·
verified ·
1 Parent(s): 4e8729a

Update app.py

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