DavidJyes commited on
Commit
9772d68
·
verified ·
1 Parent(s): 6131296

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +166 -0
app.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from fastapi import FastAPI, HTTPException
3
+ from pydantic import BaseModel, Field
4
+ import joblib
5
+ import json
6
+ import numpy as np
7
+ from datetime import datetime
8
+ from typing import Optional
9
+ import os
10
+
11
+ # Initialisation
12
+ app = FastAPI(
13
+ title="API Détection de Fraude",
14
+ description="API de détection de fraude dans les transactions bancaires",
15
+ version="1.0.0"
16
+ )
17
+
18
+ # Chargement du modèle et des encodeurs
19
+ model = joblib.load('fraud_model.pkl')
20
+ le_category = joblib.load('le_category.pkl')
21
+ le_gender = joblib.load('le_gender.pkl')
22
+ le_state = joblib.load('le_state.pkl')
23
+
24
+ with open('mappings.json', 'r') as f:
25
+ mappings = json.load(f)
26
+
27
+ # Modèle de données
28
+ class Transaction(BaseModel):
29
+ amt: float = Field(..., description="Montant de la transaction", example=125.50)
30
+ category: str = Field(..., description="Catégorie du marchand", example="personal_care")
31
+ merchant: str = Field(..., description="Nom du marchand", example="fraud_Kirlin and Sons")
32
+ trans_date_trans_time: str = Field(..., description="Date et heure", example="2020-06-21 12:14:25")
33
+ gender: str = Field(..., description="Genre (M/F)", example="M")
34
+ state: str = Field(..., description="État (code à 2 lettres)", example="SC")
35
+ lat: float = Field(..., description="Latitude du client", example=33.9659)
36
+ long: float = Field(..., description="Longitude du client", example=-80.9355)
37
+ city_pop: int = Field(..., description="Population de la ville", example=333497)
38
+ dob: str = Field(..., description="Date de naissance", example="1968-03-19")
39
+ merch_lat: float = Field(..., description="Latitude du marchand", example=33.986391)
40
+ merch_long: float = Field(..., description="Longitude du marchand", example=-81.200714)
41
+ cc_num: Optional[str] = Field(None, description="Numéro de carte (optionnel)")
42
+ avg_amt: Optional[float] = Field(50.0, description="Montant moyen historique")
43
+ std_amt: Optional[float] = Field(30.0, description="Écart-type historique")
44
+ nb_trans: Optional[int] = Field(10, description="Nombre de transactions historiques")
45
+
46
+ class PredictionResponse(BaseModel):
47
+ is_fraud: bool
48
+ fraud_probability: float
49
+ risk_level: str
50
+ details: dict
51
+
52
+ # Routes
53
+ @app.get("/")
54
+ def read_root():
55
+ return {
56
+ "message": "API de Détection de Fraude",
57
+ "version": "1.0.0",
58
+ "endpoints": {
59
+ "/predict": "POST - Prédire une transaction",
60
+ "/health": "GET - Statut de l'API",
61
+ "/categories": "GET - Liste des catégories",
62
+ "/docs": "GET - Documentation interactive"
63
+ }
64
+ }
65
+
66
+ @app.get("/health")
67
+ def health_check():
68
+ return {
69
+ "status": "healthy",
70
+ "model_loaded": model is not None,
71
+ "timestamp": datetime.now().isoformat()
72
+ }
73
+
74
+ @app.get("/categories")
75
+ def get_categories():
76
+ return {
77
+ "categories": list(mappings['categories'].keys()),
78
+ "states": list(mappings['states'].keys()),
79
+ "genders": list(mappings['genders'].keys())
80
+ }
81
+
82
+ @app.post("/predict", response_model=PredictionResponse)
83
+ def predict_fraud(transaction: Transaction):
84
+ try:
85
+ # Parsing des dates
86
+ trans_dt = datetime.strptime(transaction.trans_date_trans_time, "%Y-%m-%d %H:%M:%S")
87
+ dob_dt = datetime.strptime(transaction.dob, "%Y-%m-%d")
88
+
89
+ # Features temporelles
90
+ hour = trans_dt.hour
91
+ day_of_week = trans_dt.weekday()
92
+ day = trans_dt.day
93
+ month = trans_dt.month
94
+ age = (trans_dt - dob_dt).days // 365
95
+
96
+ # Distance
97
+ distance = np.sqrt(
98
+ (transaction.lat - transaction.merch_lat)**2 +
99
+ (transaction.long - transaction.merch_long)**2
100
+ ) * 111
101
+
102
+ # Encodage
103
+ if transaction.category not in mappings['categories']:
104
+ raise HTTPException(400, f"Catégorie inconnue: {transaction.category}")
105
+ if transaction.gender not in mappings['genders']:
106
+ raise HTTPException(400, f"Genre inconnu: {transaction.gender}")
107
+ if transaction.state not in mappings['states']:
108
+ raise HTTPException(400, f"État inconnu: {transaction.state}")
109
+
110
+ category_encoded = mappings['categories'][transaction.category]
111
+ gender_encoded = mappings['genders'][transaction.gender]
112
+ state_encoded = mappings['states'][transaction.state]
113
+
114
+ # Construction du vecteur de features
115
+ features = np.array([[
116
+ transaction.amt,
117
+ hour,
118
+ day_of_week,
119
+ day,
120
+ month,
121
+ age,
122
+ category_encoded,
123
+ gender_encoded,
124
+ state_encoded,
125
+ transaction.lat,
126
+ transaction.long,
127
+ transaction.city_pop,
128
+ distance,
129
+ transaction.avg_amt,
130
+ transaction.std_amt,
131
+ transaction.nb_trans
132
+ ]])
133
+
134
+ # Prédiction
135
+ fraud_proba = model.predict_proba(features)[0][1]
136
+ is_fraud = fraud_proba > 0.5
137
+
138
+ # Niveau de risque
139
+ if fraud_proba < 0.3:
140
+ risk_level = "Faible"
141
+ elif fraud_proba < 0.7:
142
+ risk_level = "Moyen"
143
+ else:
144
+ risk_level = "Élevé"
145
+
146
+ return PredictionResponse(
147
+ is_fraud=bool(is_fraud),
148
+ fraud_probability=float(fraud_proba),
149
+ risk_level=risk_level,
150
+ details={
151
+ "montant": transaction.amt,
152
+ "categorie": transaction.category,
153
+ "heure": hour,
154
+ "age_client": age,
155
+ "distance_km": round(distance, 2)
156
+ }
157
+ )
158
+
159
+ except ValueError as e:
160
+ raise HTTPException(status_code=400, detail=f"Erreur de format: {str(e)}")
161
+ except Exception as e:
162
+ raise HTTPException(status_code=500, detail=f"Erreur interne: {str(e)}")
163
+
164
+ if __name__ == "__main__":
165
+ import uvicorn
166
+ uvicorn.run(app, host="0.0.0.0", port=7860)