deacher commited on
Commit
b8f4945
·
verified ·
1 Parent(s): 3239a4a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +84 -26
app.py CHANGED
@@ -187,30 +187,81 @@ def get_unified_recommendations_api(
187
 
188
 
189
  # JALUR 2 & 3: Pengguna Baru (dengan atau tanpa probe)
190
- elif preferred_categories:
191
- valid_categories = [cat for cat in preferred_categories if cat in existing_list_kategori]
192
- if not valid_categories: return []
193
-
 
 
 
 
 
 
 
194
  mask = df_wisata[valid_categories].sum(axis=1) > 0
195
- filtered_wisata = df_wisata[mask].copy()
196
- if filtered_wisata.empty: return []
197
-
198
- # Ini adalah jalur fallback sederhana (Jalur 3)
199
- sort_cols = []
200
- if 'Overall Rating (Google Maps)' in filtered_wisata.columns:
201
- sort_cols.append('Overall Rating (Google Maps)')
202
- if 'Jumlah Ulasan (Google Maps)' in filtered_wisata.columns:
203
- sort_cols.append('Jumlah Ulasan (Google Maps)')
204
-
205
- if not sort_cols:
206
- ranked_wisata = filtered_wisata.head(n)
207
  else:
208
- ranked_wisata = filtered_wisata.sort_values(by=sort_cols, ascending=False).head(n)
209
-
210
- # Hanya kembalikan kolom yang relevan
211
- display_cols = ['ID Tempat', 'Nama Wisata'] + [col for col in sort_cols if col in ranked_wisata.columns]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  return ranked_wisata[display_cols].to_dict(orient='records')
213
-
214
  else:
215
  # Jika tidak ada input yang valid
216
  return []
@@ -219,6 +270,7 @@ def get_unified_recommendations_api(
219
  class RecommendationRequest(BaseModel):
220
  user_id: Optional[str] = Field(None, description="ID unik pengguna yang sudah ada di sistem.")
221
  preferred_categories: Optional[List[str]] = Field(None, description="List kategori yang disukai pengguna baru, cth: ['budaya', 'seni'].")
 
222
  n: int = Field(10, gt=0, le=50, description="Jumlah rekomendasi yang diinginkan.")
223
 
224
  class Config:
@@ -237,14 +289,15 @@ class RecommendationRequest(BaseModel):
237
  @app.post("/recommendations", summary="Dapatkan Rekomendasi Wisata")
238
  async def get_recommendations(request: RecommendationRequest):
239
  """
240
- Endpoint ini memberikan rekomendasi wisata berdasarkan dua skenario:
241
 
242
  1. **Untuk Pengguna yang Sudah Ada**: Sediakan `user_id`.
243
- 2. **Untuk Pengguna Baru**: Sediakan list `preferred_categories`.
 
244
 
245
- Jika `user_id` disediakan, `preferred_categories` akan diabaikan.
246
  """
247
-
248
  if not request.user_id and not request.preferred_categories:
249
  raise HTTPException(
250
  status_code=400,
@@ -252,14 +305,19 @@ async def get_recommendations(request: RecommendationRequest):
252
  )
253
 
254
  if request.user_id:
255
- # Cek apakah user sudah ada DI DATA YANG SEDANG BERJALAN
256
  if request.user_id not in MODEL_DATA['user_encoder'].classes_:
257
  print(f"API mendeteksi user_id baru: '{request.user_id}'. Menjalankan proses penambahan...")
 
 
258
  add_new_user_to_artifacts(request.user_id)
259
 
260
  recommendations = get_unified_recommendations_api(
261
  raw_user_id=request.user_id,
262
  preferred_categories=request.preferred_categories,
 
 
 
 
263
  n=request.n
264
  )
265
 
 
187
 
188
 
189
  # JALUR 2 & 3: Pengguna Baru (dengan atau tanpa probe)
190
+ elif preferred_categories and probe_user_raw_ids:
191
+ # Ini adalah jalur yang benar untuk probing (Logika dari model asli)
192
+ print(f"Membuat rekomendasi untuk pengguna baru dengan preferensi: {preferred_categories} (NCF probe)")
193
+
194
+ # 1. Validasi Kategori
195
+ valid_categories = [cat for cat in preferred_categories if cat in df_wisata.columns and cat in list_kategori_wisata_valid_passed]
196
+ if not valid_categories:
197
+ print(f"Tidak ada kategori valid dari preferensi: {preferred_categories}")
198
+ return []
199
+
200
+ # 2. Filter Kandidat Wisata Berdasarkan Kategori
201
  mask = df_wisata[valid_categories].sum(axis=1) > 0
202
+ candidate_wisata_df = df_wisata[mask].copy()
203
+ if candidate_wisata_df.empty:
204
+ print(f"Tidak ditemukan tempat wisata yang cocok dengan kategori: {valid_categories}")
205
+ return []
206
+
207
+ # 3. Normalisasi Rating Google
208
+ if 'Overall Rating (Google Maps)' in candidate_wisata_df.columns:
209
+ candidate_wisata_df['google_rating_norm'] = normalize_series_min_max(candidate_wisata_df['Overall Rating (Google Maps)'])
 
 
 
 
210
  else:
211
+ candidate_wisata_df['google_rating_norm'] = 0.0
212
+
213
+ # 4. Validasi Probe User IDs
214
+ valid_probe_user_ids_int = []
215
+ for raw_id in probe_user_raw_ids:
216
+ try:
217
+ valid_probe_user_ids_int.append(user_encoder_passed.transform([raw_id])[0])
218
+ except ValueError:
219
+ print(f"Warning: Probe user ID '{raw_id}' tidak dikenal.")
220
+
221
+ # 5. Hitung Skor NCF untuk setiap kandidat wisata
222
+ if not valid_probe_user_ids_int:
223
+ print("Warning: Tidak ada probe user ID yang valid. NCF appeal score akan 0.")
224
+ candidate_wisata_df['avg_ncf_appeal_score'] = 0.0
225
+ else:
226
+ avg_ncf_appeal_scores = []
227
+ for _, row_cand in candidate_wisata_df.iterrows():
228
+ item_id_raw = row_cand['ID Tempat']
229
+ try:
230
+ item_id_int = item_encoder_passed.transform([item_id_raw])[0]
231
+ item_features = df_wisata_features_passed[df_wisata_features_passed['item_id_int'] == item_id_int][feature_columns_ncf_passed].iloc[[0]].values.astype(np.float32)
232
+
233
+ if item_features.size == 0:
234
+ avg_ncf_appeal_scores.append(0.0)
235
+ continue
236
+
237
+ ncf_preds_for_item = []
238
+ for user_id_int in valid_probe_user_ids_int:
239
+ model_input = {
240
+ 'user_input': np.array([user_id_int]),
241
+ 'item_input': np.array([item_id_int]),
242
+ 'features_input': item_features
243
+ }
244
+ pred = ncf_model.predict(model_input, verbose=0)[0][0]
245
+ ncf_preds_for_item.append(pred)
246
+
247
+ avg_ncf_appeal_scores.append(np.mean(ncf_preds_for_item) if ncf_preds_for_item else 0.0)
248
+
249
+ except ValueError:
250
+ avg_ncf_appeal_scores.append(0.0) # Item tidak dikenal oleh encoder
251
+
252
+ candidate_wisata_df['avg_ncf_appeal_score'] = avg_ncf_appeal_scores
253
+
254
+ # 6. Hitung Skor Akhir dan Lakukan Ranking
255
+ candidate_wisata_df['final_score %'] = (
256
+ (weight_google_rating_new * candidate_wisata_df['google_rating_norm'].fillna(0) +
257
+ weight_ncf_appeal_new * candidate_wisata_df['avg_ncf_appeal_score']) * 100
258
+ )
259
+ ranked_wisata = candidate_wisata_df.sort_values(by='final_score %', ascending=False).head(n)
260
+
261
+ # 7. Kembalikan hasil dalam format yang diinginkan
262
+ display_cols = ['ID Tempat', 'Nama Wisata', 'final_score %']
263
  return ranked_wisata[display_cols].to_dict(orient='records')
264
+
265
  else:
266
  # Jika tidak ada input yang valid
267
  return []
 
270
  class RecommendationRequest(BaseModel):
271
  user_id: Optional[str] = Field(None, description="ID unik pengguna yang sudah ada di sistem.")
272
  preferred_categories: Optional[List[str]] = Field(None, description="List kategori yang disukai pengguna baru, cth: ['budaya', 'seni'].")
273
+ probe_user_ids: Optional[List[str]] = Field(None, description="(Opsional) Daftar ID pengguna 'probe' untuk NCF hybrid.")
274
  n: int = Field(10, gt=0, le=50, description="Jumlah rekomendasi yang diinginkan.")
275
 
276
  class Config:
 
289
  @app.post("/recommendations", summary="Dapatkan Rekomendasi Wisata")
290
  async def get_recommendations(request: RecommendationRequest):
291
  """
292
+ Endpoint ini memberikan rekomendasi wisata berdasarkan tiga skenario:
293
 
294
  1. **Untuk Pengguna yang Sudah Ada**: Sediakan `user_id`.
295
+ 2. **Untuk Pengguna Baru (Hybrid Probing)**: Sediakan `preferred_categories` DAN `probe_user_ids`. Ini akan memberikan hasil yang lebih cerdas.
296
+ 3. **Untuk Pengguna Baru (Fallback Sederhana)**: Sediakan HANYA `preferred_categories`. Rekomendasi akan didasarkan pada popularitas dalam kategori tersebut.
297
 
298
+ Jika `user_id` disediakan, input lain akan diabaikan.
299
  """
300
+
301
  if not request.user_id and not request.preferred_categories:
302
  raise HTTPException(
303
  status_code=400,
 
305
  )
306
 
307
  if request.user_id:
 
308
  if request.user_id not in MODEL_DATA['user_encoder'].classes_:
309
  print(f"API mendeteksi user_id baru: '{request.user_id}'. Menjalankan proses penambahan...")
310
+ # Fungsi ini mungkin perlu Anda tinjau kembali, apakah pengguna baru seharusnya langsung ditambahkan
311
+ # atau hanya ditangani sebagai pengguna anonim.
312
  add_new_user_to_artifacts(request.user_id)
313
 
314
  recommendations = get_unified_recommendations_api(
315
  raw_user_id=request.user_id,
316
  preferred_categories=request.preferred_categories,
317
+
318
+ # Teruskan probe_user_ids ke fungsi utama
319
+ probe_user_raw_ids=request.probe_user_ids,
320
+
321
  n=request.n
322
  )
323