Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -16,8 +16,8 @@ os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
|
|
| 16 |
from tensorflow.keras.models import load_model
|
| 17 |
from tensorflow.keras.preprocessing.sequence import pad_sequences
|
| 18 |
|
| 19 |
-
nltk.download('punkt')
|
| 20 |
-
nltk.download('stopwords')
|
| 21 |
|
| 22 |
app = FastAPI()
|
| 23 |
|
|
@@ -75,11 +75,12 @@ def preprocessing(statement):
|
|
| 75 |
tokens = [token for token in tokens if token.isalpha() and len(token) > 2]
|
| 76 |
return ' '.join(tokens)
|
| 77 |
|
| 78 |
-
|
|
|
|
| 79 |
if mode not in ["fake_review", "spam"]: return {"error": "Mode invalid."}
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
|
| 84 |
if model_type == "lstm":
|
| 85 |
tokenizer = models_dict[mode]["tokenizer"]
|
|
@@ -88,10 +89,11 @@ def analyze_text(text: str, mode: str, model_type: str):
|
|
| 88 |
if not tokenizer or not lstm_model:
|
| 89 |
return {"error": f"File model LSTM untuk {mode} tidak ditemukan."}
|
| 90 |
|
| 91 |
-
|
| 92 |
-
padded = pad_sequences(
|
| 93 |
-
|
| 94 |
-
|
|
|
|
| 95 |
|
| 96 |
elif model_type in ["lr", "rf", "xgb"]:
|
| 97 |
tfidf = models_dict[mode]["tfidf"]
|
|
@@ -100,30 +102,34 @@ def analyze_text(text: str, mode: str, model_type: str):
|
|
| 100 |
if not tfidf or not clf_model:
|
| 101 |
return {"error": f"File model {model_type} untuk {mode} tidak ditemukan."}
|
| 102 |
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
else:
|
| 108 |
return {"error": "Model type tidak dikenali."}
|
| 109 |
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
label = "Spam" if is_flagged else "Safe (Ham)"
|
| 115 |
-
reason = "The text contains word order patterns similar to those found in promotional messages or scams." if is_flagged else "The sentence appears similar to ordinary conversational message."
|
| 116 |
-
else:
|
| 117 |
-
label = "Fake" if is_flagged else "Genuine"
|
| 118 |
-
reason = "The writing style bears a strong resemblance to paid review templates or no clear context." if is_flagged else "The review has a natural variety of vocabulary and recounts the experience in detail."
|
| 119 |
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
|
| 128 |
class TextRequest(BaseModel):
|
| 129 |
text: str
|
|
@@ -136,10 +142,11 @@ class TextRequest(BaseModel):
|
|
| 136 |
|
| 137 |
@app.post("/api/analyze/text")
|
| 138 |
def analyze_single(req: TextRequest):
|
| 139 |
-
|
| 140 |
-
|
|
|
|
| 141 |
raise HTTPException(status_code=500, detail=result["error"])
|
| 142 |
-
return {"status": "success", "result": result}
|
| 143 |
|
| 144 |
@app.post("/api/analyze/file")
|
| 145 |
async def analyze_file(mode: str = Form(...), model_type: str = Form(...), file: UploadFile = File(...)):
|
|
@@ -148,20 +155,19 @@ async def analyze_file(mode: str = Form(...), model_type: str = Form(...), file:
|
|
| 148 |
|
| 149 |
content = await file.read()
|
| 150 |
decoded = content.decode('utf-8')
|
| 151 |
-
|
| 152 |
|
| 153 |
if file.filename.endswith('.txt'):
|
| 154 |
-
|
| 155 |
-
for line in lines:
|
| 156 |
-
res = analyze_text(line, mode, model_type)
|
| 157 |
-
if "error" not in res: results.append(res)
|
| 158 |
|
| 159 |
elif file.filename.endswith('.csv'):
|
| 160 |
df = pd.read_csv(StringIO(decoded))
|
| 161 |
text_col = next((col for col in ['text', 'review', 'v2', 'text_'] if col in df.columns), df.columns[0])
|
|
|
|
| 162 |
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
|
|
|
| 167 |
return {"status": "success", "total": len(results), "results": results}
|
|
|
|
| 16 |
from tensorflow.keras.models import load_model
|
| 17 |
from tensorflow.keras.preprocessing.sequence import pad_sequences
|
| 18 |
|
| 19 |
+
nltk.download('punkt', quiet=True)
|
| 20 |
+
nltk.download('stopwords', quiet=True)
|
| 21 |
|
| 22 |
app = FastAPI()
|
| 23 |
|
|
|
|
| 75 |
tokens = [token for token in tokens if token.isalpha() and len(token) > 2]
|
| 76 |
return ' '.join(tokens)
|
| 77 |
|
| 78 |
+
# FUNGSI BARU: Menganalisis banyak teks sekaligus (Batch Processing)
|
| 79 |
+
def analyze_bulk(texts: list, mode: str, model_type: str):
|
| 80 |
if mode not in ["fake_review", "spam"]: return {"error": "Mode invalid."}
|
| 81 |
+
if not texts: return []
|
| 82 |
+
|
| 83 |
+
clean_texts = [preprocessing(t) for t in texts]
|
| 84 |
|
| 85 |
if model_type == "lstm":
|
| 86 |
tokenizer = models_dict[mode]["tokenizer"]
|
|
|
|
| 89 |
if not tokenizer or not lstm_model:
|
| 90 |
return {"error": f"File model LSTM untuk {mode} tidak ditemukan."}
|
| 91 |
|
| 92 |
+
seqs = tokenizer.texts_to_sequences(clean_texts)
|
| 93 |
+
padded = pad_sequences(seqs, maxlen=100, padding='post', truncating='post')
|
| 94 |
+
# Prediksi sekaligus dalam satu tarikan
|
| 95 |
+
predictions = lstm_model.predict(padded, batch_size=32, verbose=0).flatten()
|
| 96 |
+
scores = [float(p) * 100 for p in predictions]
|
| 97 |
|
| 98 |
elif model_type in ["lr", "rf", "xgb"]:
|
| 99 |
tfidf = models_dict[mode]["tfidf"]
|
|
|
|
| 102 |
if not tfidf or not clf_model:
|
| 103 |
return {"error": f"File model {model_type} untuk {mode} tidak ditemukan."}
|
| 104 |
|
| 105 |
+
vec_texts = tfidf.transform(clean_texts)
|
| 106 |
+
# Prediksi sekaligus dalam satu tarikan
|
| 107 |
+
probas = clf_model.predict_proba(vec_texts)
|
| 108 |
+
scores = [float(p[1]) * 100 for p in probas]
|
| 109 |
else:
|
| 110 |
return {"error": "Model type tidak dikenali."}
|
| 111 |
|
| 112 |
+
results = []
|
| 113 |
+
for text, score in zip(texts, scores):
|
| 114 |
+
is_flagged = score > 50
|
| 115 |
+
confidence = score if is_flagged else 100 - score
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
|
| 117 |
+
if mode == "spam":
|
| 118 |
+
label = "Spam" if is_flagged else "Safe (Ham)"
|
| 119 |
+
reason = "The text contains word order patterns similar to those found in promotional messages or scams." if is_flagged else "The sentence appears similar to ordinary conversational message."
|
| 120 |
+
else:
|
| 121 |
+
label = "Fake" if is_flagged else "Genuine"
|
| 122 |
+
reason = "The writing style bears a strong resemblance to paid review templates or no clear context." if is_flagged else "The review has a natural variety of vocabulary and recounts the experience in detail."
|
| 123 |
+
|
| 124 |
+
results.append({
|
| 125 |
+
"text": text,
|
| 126 |
+
"label": label,
|
| 127 |
+
"confidence": confidence,
|
| 128 |
+
"is_flagged": is_flagged,
|
| 129 |
+
"reason": reason
|
| 130 |
+
})
|
| 131 |
+
|
| 132 |
+
return results
|
| 133 |
|
| 134 |
class TextRequest(BaseModel):
|
| 135 |
text: str
|
|
|
|
| 142 |
|
| 143 |
@app.post("/api/analyze/text")
|
| 144 |
def analyze_single(req: TextRequest):
|
| 145 |
+
# Memanfaatkan fungsi bulk untuk 1 teks
|
| 146 |
+
result = analyze_bulk([req.text], req.mode, req.model_type)
|
| 147 |
+
if isinstance(result, dict) and "error" in result:
|
| 148 |
raise HTTPException(status_code=500, detail=result["error"])
|
| 149 |
+
return {"status": "success", "result": result[0]}
|
| 150 |
|
| 151 |
@app.post("/api/analyze/file")
|
| 152 |
async def analyze_file(mode: str = Form(...), model_type: str = Form(...), file: UploadFile = File(...)):
|
|
|
|
| 155 |
|
| 156 |
content = await file.read()
|
| 157 |
decoded = content.decode('utf-8')
|
| 158 |
+
texts = []
|
| 159 |
|
| 160 |
if file.filename.endswith('.txt'):
|
| 161 |
+
texts = [line.strip() for line in decoded.split('\n') if line.strip()]
|
|
|
|
|
|
|
|
|
|
| 162 |
|
| 163 |
elif file.filename.endswith('.csv'):
|
| 164 |
df = pd.read_csv(StringIO(decoded))
|
| 165 |
text_col = next((col for col in ['text', 'review', 'v2', 'text_'] if col in df.columns), df.columns[0])
|
| 166 |
+
texts = df[text_col].dropna().astype(str).tolist()
|
| 167 |
|
| 168 |
+
# Seluruh teks dikirim langsung ke fungsi bulk
|
| 169 |
+
results = analyze_bulk(texts, mode, model_type)
|
| 170 |
+
if isinstance(results, dict) and "error" in results:
|
| 171 |
+
raise HTTPException(status_code=500, detail=results["error"])
|
| 172 |
+
|
| 173 |
return {"status": "success", "total": len(results), "results": results}
|