github-actions[bot]
Sync Flask API from GitHub 782d7c9418536a8a930bf3f588b6f5d5b1fd593d
bb0fa8a
Raw
History Blame Contribute Delete
6.99 kB
from flask import Flask, jsonify, request
from flask_cors import CORS
from models.recommender import TourismRecommender
import os
app = Flask(__name__)
CORS(app) # Enable CORS for Laravel
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# Initialize recommender
print("Initializing Tourism Recommender...")
recommender = TourismRecommender(data_path=os.path.join(BASE_DIR, 'data'))
print("Ready!")
@app.route('/')
def index():
"""API Health Check"""
return jsonify({
'status': 'success',
'message': 'Bogor Tourism Recommendation API',
'version': '1.0.0',
'endpoints': {
'GET /api/places': 'Get all tourism places with pagination',
'GET /api/places/id': 'Get specific place details',
'GET /api/search?q=query': 'Semantic search using IndoBERT',
'POST /api/recommendations': 'Get recommendations for a place'
}
})
@app.route('/api/places', methods=['GET'])
def get_places():
"""
Get all tourism places with pagination
Query params:
- limit: number of items per page (default: 20)
- offset: starting position (default: 0)
- category: filter by category (optional)
"""
try:
limit = request.args.get('limit', 20, type=int)
offset = request.args.get('offset', 0, type=int)
category = request.args.get('category', None, type=str)
result = recommender.get_all_places(limit=limit, offset=offset)
# Filter by category if provided
if category:
result['data'] = [p for p in result['data'] if p['kategori'].lower() == category.lower()]
result['total'] = len(result['data'])
return jsonify({
'status': 'success',
'data': result
})
except Exception as e:
return jsonify({
'status': 'error',
'message': str(e)
}), 500
@app.route('/api/places/<int:place_id>', methods=['GET'])
def get_place(place_id):
"""Get detailed information about a specific place"""
try:
place = recommender.get_place_by_id(place_id)
if place is None:
return jsonify({
'status': 'error',
'message': f'Place with ID {place_id} not found'
}), 404
return jsonify({
'status': 'success',
'data': place
})
except Exception as e:
return jsonify({
'status': 'error',
'message': str(e)
}), 500
@app.route('/api/recommendations', methods=['POST'])
def get_recommendations():
"""
Get recommendations for a place
Request body:
- place_id: ID of the place (optional if place_name provided)
- place_name: Name of the place (optional if place_id provided)
- top_n: number of recommendations (default: 10)
"""
try:
data = request.get_json()
if not data:
return jsonify({
'status': 'error',
'message': 'Request body is required'
}), 400
place_idx = None
place_details = None
top_n = data.get('top_n', 10)
print(f"REQUEST: {data}")
# 1. Try lookup by NAME first (Most reliable if IDs don't match)
if 'place_name' in data and data['place_name']:
print(f" Searching by name: {data['place_name']}")
search_result = recommender.search_by_name(data['place_name'])
if search_result:
place_idx = search_result['query_idx']
# Get details using this index
place_details = recommender.get_place_by_id(place_idx)
print(f" Found by name: Index {place_idx} -> {place_details['nama']}")
# 2. Fallback to ID if provided and Name lookup failed
if place_idx is None and 'place_id' in data:
print(f" Searching by ID: {data['place_id']}")
try:
# Assuming the ID passed IS the index (fallback)
potential_idx = int(data['place_id'])
# Check if valid index
if 0 <= potential_idx < len(recommender.df):
place_idx = potential_idx
place_details = recommender.get_place_by_id(place_idx)
print(f" Using ID as Index: Index {place_idx} -> {place_details['nama']}")
else:
print(f" ID {potential_idx} is out of bounds (0-{len(recommender.df)-1})")
except ValueError:
pass
if place_idx is None:
print(" Place not found")
return jsonify({
'status': 'error',
'message': 'Place not found. Provide valid place_name or place_id.'
}), 404
# Get recommendations using the resolved INDEX
recommendations = recommender.get_recommendations(place_idx, top_n=top_n)
return jsonify({
'status': 'success',
'data': {
'place': place_details,
'recommendations': recommendations,
'total_recommendations': len(recommendations)
}
})
except Exception as e:
import traceback
print(f"API ERROR: {str(e)}")
traceback.print_exc()
return jsonify({
'status': 'error',
'message': str(e)
}), 500
@app.route('/api/search', methods=['GET'])
def semantic_search():
"""
Semantic search using IndoBERT similarity.
Query params:
- q: search query string
- limit: max results (default: None = all results)
"""
try:
query = request.args.get('q', '', type=str)
limit = request.args.get('limit', None, type=int)
if not query:
return jsonify({
'status': 'error',
'message': 'Query parameter "q" is required'
}), 400
results = recommender.semantic_search(query, top_n=limit)
if isinstance(results, dict):
if results.get('status') != 'success':
return jsonify(results), 500
results = results.get('data', [])
return jsonify({
'status': 'success',
'query': query,
'total': len(results),
'data': results
})
except Exception as e:
return jsonify({
'status': 'error',
'message': str(e)
}), 500
@app.errorhandler(404)
def not_found(error):
return jsonify({
'status': 'error',
'message': 'Endpoint not found'
}), 404
@app.errorhandler(500)
def internal_error(error):
return jsonify({
'status': 'error',
'message': 'Internal server error'
}), 500
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)