DavidJyes commited on
Commit
4368d52
·
verified ·
1 Parent(s): 1ddbc11

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +56 -141
app.py CHANGED
@@ -1,153 +1,68 @@
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
- from fastapi.responses import JSONResponse, RedirectResponse
11
 
12
- # Initialisation
13
- app = FastAPI(
14
- title="API Détection de Fraude",
15
- docs_url="/docs",
16
- redoc_url="/redoc",
17
- description="API de détection de fraude dans les transactions bancaires",
18
- version="1.0.0"
19
- )
20
 
21
- # Chargement du modèle et des encodeurs
22
- model = joblib.load('fraud_model.pkl')
23
- le_category = joblib.load('le_category.pkl')
24
- le_gender = joblib.load('le_gender.pkl')
25
- le_state = joblib.load('le_state.pkl')
26
 
27
- with open('mappings.json', 'r') as f:
28
- mappings = json.load(f)
 
 
29
 
30
- # Modèle de données
31
  class Transaction(BaseModel):
32
- amt: float = Field(..., description="Montant de la transaction", example=125.50)
33
- category: str = Field(..., description="Catégorie du marchand", example="personal_care")
34
- merchant: str = Field(..., description="Nom du marchand", example="fraud_Kirlin and Sons")
35
- trans_date_trans_time: str = Field(..., description="Date et heure", example="2020-06-21 12:14:25")
36
- gender: str = Field(..., description="Genre (M/F)", example="M")
37
- state: str = Field(..., description="État (code à 2 lettres)", example="SC")
38
- lat: float = Field(..., description="Latitude du client", example=33.9659)
39
- long: float = Field(..., description="Longitude du client", example=-80.9355)
40
- city_pop: int = Field(..., description="Population de la ville", example=333497)
41
- dob: str = Field(..., description="Date de naissance", example="1968-03-19")
42
- merch_lat: float = Field(..., description="Latitude du marchand", example=33.986391)
43
- merch_long: float = Field(..., description="Longitude du marchand", example=-81.200714)
44
- cc_num: Optional[str] = Field(None, description="Numéro de carte (optionnel)")
45
- avg_amt: Optional[float] = Field(50.0, description="Montant moyen historique")
46
- std_amt: Optional[float] = Field(30.0, description="Écart-type historique")
47
- nb_trans: Optional[int] = Field(10, description="Nombre de transactions historiques")
48
 
49
- class PredictionResponse(BaseModel):
50
- is_fraud: bool
51
- fraud_probability: float
52
- risk_level: str
53
- details: dict
 
 
 
 
 
 
 
 
 
 
 
54
 
55
- # Routes
56
- @app.get("/")
57
- def root():
58
- return RedirectResponse(url="/docs")
59
 
60
-
61
- @app.get("/categories")
62
- def get_categories():
63
- return {
64
- "categories": list(mappings['categories'].keys()),
65
- "states": list(mappings['states'].keys()),
66
- "genders": list(mappings['genders'].keys())
67
- }
68
 
69
- @app.post("/predict", response_model=PredictionResponse)
70
- def predict_fraud(transaction: Transaction):
71
- try:
72
- # Parsing des dates
73
- trans_dt = datetime.strptime(transaction.trans_date_trans_time, "%Y-%m-%d %H:%M:%S")
74
- dob_dt = datetime.strptime(transaction.dob, "%Y-%m-%d")
75
-
76
- # Features temporelles
77
- hour = trans_dt.hour
78
- day_of_week = trans_dt.weekday()
79
- day = trans_dt.day
80
- month = trans_dt.month
81
- age = (trans_dt - dob_dt).days // 365
82
-
83
- # Distance
84
- distance = np.sqrt(
85
- (transaction.lat - transaction.merch_lat)**2 +
86
- (transaction.long - transaction.merch_long)**2
87
- ) * 111
88
-
89
- # Encodage
90
- if transaction.category not in mappings['categories']:
91
- raise HTTPException(400, f"Catégorie inconnue: {transaction.category}")
92
- if transaction.gender not in mappings['genders']:
93
- raise HTTPException(400, f"Genre inconnu: {transaction.gender}")
94
- if transaction.state not in mappings['states']:
95
- raise HTTPException(400, f"État inconnu: {transaction.state}")
96
-
97
- category_encoded = mappings['categories'][transaction.category]
98
- gender_encoded = mappings['genders'][transaction.gender]
99
- state_encoded = mappings['states'][transaction.state]
100
-
101
- # Construction du vecteur de features
102
- features = np.array([[
103
- transaction.amt,
104
- hour,
105
- day_of_week,
106
- day,
107
- month,
108
- age,
109
- category_encoded,
110
- gender_encoded,
111
- state_encoded,
112
- transaction.lat,
113
- transaction.long,
114
- transaction.city_pop,
115
- distance,
116
- transaction.avg_amt,
117
- transaction.std_amt,
118
- transaction.nb_trans
119
- ]])
120
-
121
- # Prédiction
122
- fraud_proba = model.predict_proba(features)[0][1]
123
- is_fraud = fraud_proba > 0.5
124
-
125
- # Niveau de risque
126
- if fraud_proba < 0.3:
127
- risk_level = "Faible"
128
- elif fraud_proba < 0.7:
129
- risk_level = "Moyen"
130
- else:
131
- risk_level = "Élevé"
132
-
133
- return PredictionResponse(
134
- is_fraud=bool(is_fraud),
135
- fraud_probability=float(fraud_proba),
136
- risk_level=risk_level,
137
- details={
138
- "montant": transaction.amt,
139
- "categorie": transaction.category,
140
- "heure": hour,
141
- "age_client": age,
142
- "distance_km": round(distance, 2)
143
- }
144
- )
145
-
146
- except ValueError as e:
147
- raise HTTPException(status_code=400, detail=f"Erreur de format: {str(e)}")
148
- except Exception as e:
149
- raise HTTPException(status_code=500, detail=f"Erreur interne: {str(e)}")
150
 
151
- if __name__ == "__main__":
152
- import uvicorn
153
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
1
+ from fastapi import FastAPI
2
+ from fastapi.responses import RedirectResponse
 
3
  import joblib
4
+ import pandas as pd
5
  import numpy as np
6
+ from pydantic import BaseModel
 
 
 
7
 
8
+ # 1. Configuration de l'API
9
+ app = FastAPI(title="Fraud Detection API")
 
 
 
 
 
 
10
 
11
+ # 2. Chargement du modèle (assure-toi que le nom correspond)
12
+ model = joblib.load('ton_modele.pkl')
 
 
 
13
 
14
+ # 3. Redirection automatique vers /docs
15
+ @app.get("/", include_in_schema=False)
16
+ def root():
17
+ return RedirectResponse(url="/docs")
18
 
19
+ # 4. Schéma des données d'entrée
20
  class Transaction(BaseModel):
21
+ amt: float
22
+ trans_date_trans_time: str
23
+ dob: str
24
+ lat: float
25
+ long: float
26
+ merch_lat: float
27
+ merch_long: float
28
+ city_pop: float
29
+ category: str
30
+ gender: str
31
+ state: str
32
+ merchant: str
33
+ job: str
34
+ cc_num: int
 
 
35
 
36
+ # 5. Ta fonction de préparation (adaptée pour une seule ligne)
37
+ def prepare_input(data: dict):
38
+ df = pd.DataFrame([data])
39
+ # Calculs rapides (similaires à ton make_features)
40
+ dt = pd.to_datetime(df["trans_date_trans_time"])
41
+ df["hour"], df["day_of_week"] = dt.dt.hour, dt.dt.dayofweek
42
+ df["day"], df["month"] = dt.dt.day, dt.dt.month
43
+
44
+ dob = pd.to_datetime(df["dob"])
45
+ df["age"] = ((dt - dob).dt.days / 365.25).astype("float32")
46
+
47
+ # Distance Haversine
48
+ lat1, lon1 = np.radians(df["lat"]), np.radians(df["long"])
49
+ lat2, lon2 = np.radians(df["merch_lat"]), np.radians(df["merch_long"])
50
+ d = np.sin((lat2-lat1)/2)**2 + np.cos(lat1)*np.cos(lat2)*np.sin((lon2-lon1)/2)**2
51
+ df["distance"] = (6371 * 2 * np.arcsin(np.sqrt(d))).astype("float32")
52
 
53
+ # Valeurs par défaut pour les agrégats (car une API reçoit souvent 1 seule transaction)
54
+ df["avg_amt"] = df["amt"]
55
+ df["std_amt"] = 0.0
56
+ df["nb_trans"] = 1.0
57
 
58
+ cols = ['amt', 'hour', 'day_of_week', 'day', 'month', 'age', 'lat', 'long',
59
+ 'city_pop', 'distance', 'avg_amt', 'std_amt', 'nb_trans',
60
+ 'category', 'gender', 'state', 'merchant', 'job']
61
+ return df[cols]
 
 
 
 
62
 
63
+ @app.post("/predict")
64
+ def predict(data: Transaction):
65
+ X = prepare_input(data.dict())
66
+ prediction = model.predict(X)
67
+ return {"is_fraud": int(prediction[0])}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68