github-actions[bot] commited on
Commit
bb0fa8a
Β·
1 Parent(s): e79ce4a

Sync Flask API from GitHub 782d7c9418536a8a930bf3f588b6f5d5b1fd593d

Browse files
Files changed (4) hide show
  1. README.md +0 -1
  2. app.py +8 -8
  3. data/check_ids.py +3 -3
  4. models/recommender.py +13 -13
README.md CHANGED
@@ -1,6 +1,5 @@
1
  ---
2
  title: Bogor Xplore API
3
- emoji: 🏞️
4
  colorFrom: green
5
  colorTo: blue
6
  sdk: docker
 
1
  ---
2
  title: Bogor Xplore API
 
3
  colorFrom: green
4
  colorTo: blue
5
  sdk: docker
app.py CHANGED
@@ -9,9 +9,9 @@ CORS(app) # Enable CORS for Laravel
9
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
10
 
11
  # Initialize recommender
12
- print("πŸš€ Initializing Tourism Recommender...")
13
  recommender = TourismRecommender(data_path=os.path.join(BASE_DIR, 'data'))
14
- print("βœ… Ready!")
15
 
16
  @app.route('/')
17
  def index():
@@ -103,7 +103,7 @@ def get_recommendations():
103
  place_details = None
104
  top_n = data.get('top_n', 10)
105
 
106
- print(f"πŸ” REQUEST: {data}")
107
 
108
  # 1. Try lookup by NAME first (Most reliable if IDs don't match)
109
  if 'place_name' in data and data['place_name']:
@@ -113,7 +113,7 @@ def get_recommendations():
113
  place_idx = search_result['query_idx']
114
  # Get details using this index
115
  place_details = recommender.get_place_by_id(place_idx)
116
- print(f" βœ… Found by name: Index {place_idx} -> {place_details['nama']}")
117
 
118
  # 2. Fallback to ID if provided and Name lookup failed
119
  if place_idx is None and 'place_id' in data:
@@ -125,14 +125,14 @@ def get_recommendations():
125
  if 0 <= potential_idx < len(recommender.df):
126
  place_idx = potential_idx
127
  place_details = recommender.get_place_by_id(place_idx)
128
- print(f" βœ… Using ID as Index: Index {place_idx} -> {place_details['nama']}")
129
  else:
130
- print(f" ❌ ID {potential_idx} is out of bounds (0-{len(recommender.df)-1})")
131
  except ValueError:
132
  pass
133
 
134
  if place_idx is None:
135
- print(" ❌ Place not found")
136
  return jsonify({
137
  'status': 'error',
138
  'message': 'Place not found. Provide valid place_name or place_id.'
@@ -151,7 +151,7 @@ def get_recommendations():
151
  })
152
  except Exception as e:
153
  import traceback
154
- print(f"❌ API ERROR: {str(e)}")
155
  traceback.print_exc()
156
  return jsonify({
157
  'status': 'error',
 
9
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
10
 
11
  # Initialize recommender
12
+ print("Initializing Tourism Recommender...")
13
  recommender = TourismRecommender(data_path=os.path.join(BASE_DIR, 'data'))
14
+ print("Ready!")
15
 
16
  @app.route('/')
17
  def index():
 
103
  place_details = None
104
  top_n = data.get('top_n', 10)
105
 
106
+ print(f"REQUEST: {data}")
107
 
108
  # 1. Try lookup by NAME first (Most reliable if IDs don't match)
109
  if 'place_name' in data and data['place_name']:
 
113
  place_idx = search_result['query_idx']
114
  # Get details using this index
115
  place_details = recommender.get_place_by_id(place_idx)
116
+ print(f" Found by name: Index {place_idx} -> {place_details['nama']}")
117
 
118
  # 2. Fallback to ID if provided and Name lookup failed
119
  if place_idx is None and 'place_id' in data:
 
125
  if 0 <= potential_idx < len(recommender.df):
126
  place_idx = potential_idx
127
  place_details = recommender.get_place_by_id(place_idx)
128
+ print(f" Using ID as Index: Index {place_idx} -> {place_details['nama']}")
129
  else:
130
+ print(f" ID {potential_idx} is out of bounds (0-{len(recommender.df)-1})")
131
  except ValueError:
132
  pass
133
 
134
  if place_idx is None:
135
+ print(" Place not found")
136
  return jsonify({
137
  'status': 'error',
138
  'message': 'Place not found. Provide valid place_name or place_id.'
 
151
  })
152
  except Exception as e:
153
  import traceback
154
+ print(f"API ERROR: {str(e)}")
155
  traceback.print_exc()
156
  return jsonify({
157
  'status': 'error',
data/check_ids.py CHANGED
@@ -18,14 +18,14 @@ try:
18
  # Check alignment
19
  mismatch = df[df.index != df['id']]
20
  if not mismatch.empty:
21
- print(f"\n⚠️ Mismatch found! {len(mismatch)} rows have Index != ID")
22
  print("Example Mismatch:")
23
  print(mismatch[['id', 'nama']].head(3))
24
  else:
25
- print("\nβœ… Index matches ID exactly!")
26
 
27
  else:
28
- print("\n⚠️ 'id' column NOT FOUND in CSV. Pure index usage.")
29
 
30
  except Exception as e:
31
  print(f"Error: {e}")
 
18
  # Check alignment
19
  mismatch = df[df.index != df['id']]
20
  if not mismatch.empty:
21
+ print(f"\nMismatch found! {len(mismatch)} rows have Index != ID")
22
  print("Example Mismatch:")
23
  print(mismatch[['id', 'nama']].head(3))
24
  else:
25
+ print("\nIndex matches ID exactly!")
26
 
27
  else:
28
+ print("\n'id' column NOT FOUND in CSV. Pure index usage.")
29
 
30
  except Exception as e:
31
  print(f"Error: {e}")
models/recommender.py CHANGED
@@ -24,7 +24,7 @@ class TourismRecommender:
24
  """
25
 
26
  def __init__(self, data_path='data/'):
27
- print("πŸš€ Initializing Tourism Recommender...")
28
 
29
  self.data_path = data_path
30
 
@@ -37,7 +37,7 @@ class TourismRecommender:
37
  indobert_embeddings_path = os.path.join(data_path, 'indobert_embeddings.npy')
38
 
39
  # Load data
40
- print(" πŸ“‚ Loading pre-computed data from dataset...")
41
  self.df = pd.read_csv(data_csv_path)
42
 
43
  if 'deskripsi_clean' in self.df.columns:
@@ -62,8 +62,8 @@ class TourismRecommender:
62
  # Load similarity matrices
63
  self.ngram_sim = self._expand_square_matrix(np.load(ngram_similarity_path), 'ngram_similarity')
64
  self.indobert_sim = self._expand_square_matrix(np.load(indobert_similarity_path), 'indobert_similarity')
65
- print(f" βœ… Loaded ngram_similarity: {self.ngram_sim.shape}")
66
- print(f" βœ… Loaded indobert_similarity: {self.indobert_sim.shape}")
67
 
68
  # Load TF-IDF untuk query search (if needed)
69
  self.tfidf_matrix = self._expand_rows(np.load(tfidf_matrix_path), 'tfidf_matrix')
@@ -75,15 +75,15 @@ class TourismRecommender:
75
  self.indobert_embeddings = self._expand_rows(np.load(indobert_embeddings_path), 'indobert_embeddings')
76
 
77
  # Load IndoBERT model untuk query embedding
78
- print(" πŸ“₯ Loading IndoBERT model for query encoding...")
79
  self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
80
  self.tokenizer = AutoTokenizer.from_pretrained("indobenchmark/indobert-base-p1")
81
  self.model = AutoModel.from_pretrained("indobenchmark/indobert-base-p1")
82
  self.model.eval()
83
  self.model.to(self.device)
84
- print(f" βœ… IndoBERT model loaded! Device: {self.device}")
85
 
86
- print(f"\nβœ… Recommender ready!")
87
  print(f" Total destinations: {len(self.df)}")
88
  print(f" Method: IndoBERT (Search) & N-Gram (Detail Recommendations)")
89
 
@@ -99,7 +99,7 @@ class TourismRecommender:
99
  expanded = np.zeros((target_len, target_len), dtype=matrix.dtype)
100
  expanded[np.ix_(self.valid_text_indices, self.valid_text_indices)] = matrix
101
  np.fill_diagonal(expanded, 1.0)
102
- print(f" ↔️ Expanded {name}: {matrix.shape} -> {expanded.shape}")
103
  return expanded
104
 
105
  raise ValueError(
@@ -120,7 +120,7 @@ class TourismRecommender:
120
  expanded_shape = (target_len, *values.shape[1:])
121
  expanded = np.zeros(expanded_shape, dtype=values.dtype)
122
  expanded[self.valid_text_indices] = values
123
- print(f" ↔️ Expanded {name}: {values.shape} -> {expanded.shape}")
124
  return expanded
125
 
126
  raise ValueError(
@@ -183,7 +183,7 @@ class TourismRecommender:
183
  """
184
  # Validate index
185
  if place_idx is None or place_idx < 0 or place_idx >= len(self.df):
186
- print(f"❌ Invalid Place Index: {place_idx}")
187
  return []
188
 
189
  # Use pre-computed N-Gram similarity matrix directly
@@ -316,14 +316,14 @@ class TourismRecommender:
316
 
317
  if place_idx is not None:
318
  # Use pre-computed IndoBERT similarity matrix (like notebook)
319
- print(f" πŸ“Š Using IndoBERT similarity matrix for place: {self.df.iloc[place_idx]['nama']}")
320
  sim_scores = self.indobert_sim[place_idx]
321
 
322
  # Get top N (excluding itself at index 0)
323
  top_indices = sim_scores.argsort()[::-1][1:top_n+1]
324
  else:
325
  # Fall back to on-the-fly query encoding
326
- print(f" πŸ” Query not found as place name, using query encoding...")
327
  query_emb = self._get_query_embedding(query)
328
  query_emb = query_emb.reshape(1, -1)
329
  sim_scores = cosine_similarity(query_emb, self.indobert_embeddings)[0]
@@ -333,7 +333,7 @@ class TourismRecommender:
333
  for idx in top_indices:
334
  # SAFEGUARD: Ignore indices that are out of bounds
335
  if idx >= len(self.df):
336
- print(f" ⚠️ Ignored out-of-bounds index: {idx}")
337
  continue
338
 
339
  place = self.df.iloc[idx]
 
24
  """
25
 
26
  def __init__(self, data_path='data/'):
27
+ print("Initializing Tourism Recommender...")
28
 
29
  self.data_path = data_path
30
 
 
37
  indobert_embeddings_path = os.path.join(data_path, 'indobert_embeddings.npy')
38
 
39
  # Load data
40
+ print(" Loading pre-computed data from dataset...")
41
  self.df = pd.read_csv(data_csv_path)
42
 
43
  if 'deskripsi_clean' in self.df.columns:
 
62
  # Load similarity matrices
63
  self.ngram_sim = self._expand_square_matrix(np.load(ngram_similarity_path), 'ngram_similarity')
64
  self.indobert_sim = self._expand_square_matrix(np.load(indobert_similarity_path), 'indobert_similarity')
65
+ print(f" Loaded ngram_similarity: {self.ngram_sim.shape}")
66
+ print(f" Loaded indobert_similarity: {self.indobert_sim.shape}")
67
 
68
  # Load TF-IDF untuk query search (if needed)
69
  self.tfidf_matrix = self._expand_rows(np.load(tfidf_matrix_path), 'tfidf_matrix')
 
75
  self.indobert_embeddings = self._expand_rows(np.load(indobert_embeddings_path), 'indobert_embeddings')
76
 
77
  # Load IndoBERT model untuk query embedding
78
+ print(" Loading IndoBERT model for query encoding...")
79
  self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
80
  self.tokenizer = AutoTokenizer.from_pretrained("indobenchmark/indobert-base-p1")
81
  self.model = AutoModel.from_pretrained("indobenchmark/indobert-base-p1")
82
  self.model.eval()
83
  self.model.to(self.device)
84
+ print(f" IndoBERT model loaded! Device: {self.device}")
85
 
86
+ print(f"\nRecommender ready!")
87
  print(f" Total destinations: {len(self.df)}")
88
  print(f" Method: IndoBERT (Search) & N-Gram (Detail Recommendations)")
89
 
 
99
  expanded = np.zeros((target_len, target_len), dtype=matrix.dtype)
100
  expanded[np.ix_(self.valid_text_indices, self.valid_text_indices)] = matrix
101
  np.fill_diagonal(expanded, 1.0)
102
+ print(f" Expanded {name}: {matrix.shape} -> {expanded.shape}")
103
  return expanded
104
 
105
  raise ValueError(
 
120
  expanded_shape = (target_len, *values.shape[1:])
121
  expanded = np.zeros(expanded_shape, dtype=values.dtype)
122
  expanded[self.valid_text_indices] = values
123
+ print(f" Expanded {name}: {values.shape} -> {expanded.shape}")
124
  return expanded
125
 
126
  raise ValueError(
 
183
  """
184
  # Validate index
185
  if place_idx is None or place_idx < 0 or place_idx >= len(self.df):
186
+ print(f"Invalid Place Index: {place_idx}")
187
  return []
188
 
189
  # Use pre-computed N-Gram similarity matrix directly
 
316
 
317
  if place_idx is not None:
318
  # Use pre-computed IndoBERT similarity matrix (like notebook)
319
+ print(f" Using IndoBERT similarity matrix for place: {self.df.iloc[place_idx]['nama']}")
320
  sim_scores = self.indobert_sim[place_idx]
321
 
322
  # Get top N (excluding itself at index 0)
323
  top_indices = sim_scores.argsort()[::-1][1:top_n+1]
324
  else:
325
  # Fall back to on-the-fly query encoding
326
+ print(f" Query not found as place name, using query encoding...")
327
  query_emb = self._get_query_embedding(query)
328
  query_emb = query_emb.reshape(1, -1)
329
  sim_scores = cosine_similarity(query_emb, self.indobert_embeddings)[0]
 
333
  for idx in top_indices:
334
  # SAFEGUARD: Ignore indices that are out of bounds
335
  if idx >= len(self.df):
336
+ print(f" Ignored out-of-bounds index: {idx}")
337
  continue
338
 
339
  place = self.df.iloc[idx]