import gradio as gr import re import ast import pandas as pd from geopy.geocoders import Nominatim from geopy.distance import geodesic from geopy.exc import GeocoderTimedOut from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity import google.generativeai as genai from dotenv import load_dotenv from supabase import create_client import os # --- Supabase & Gemini --- load_dotenv() SUPABASE_URL = os.getenv("SUPABASE_URL") SUPABASE_KEY = os.getenv("SBASEKEY") API_KEY = os.getenv("GEMINI_API_KEY") supabase = create_client(SUPABASE_URL, SUPABASE_KEY) genai.configure(api_key=API_KEY) # --- Load data dari Supabase --- def fetch_data_from_supabase(): response = supabase.table("Maps").select("*").execute() return pd.DataFrame(response.data) df = fetch_data_from_supabase() # --- Ekstraksi Kata Kunci --- def extract_keywords(user_input): prompt = f""" Ekstrak 3–7 kata kunci penting dari deskripsi wisata berikut: "{user_input}" Tulis langsung sebagai list Python tanpa variabel apapun. """ try: response = genai.GenerativeModel("gemini-1.5-flash").generate_content(prompt) matches = re.findall(r'\[.*?\]', response.text) if matches: return ast.literal_eval(matches[0]) else: return [] except Exception as e: return [f"Error: {e}"] # --- Lokasi --- def get_coordinates_from_location(location_name): try: geolocator = Nominatim(user_agent="geoapi") location = geolocator.geocode(location_name, timeout=10) return (location.latitude, location.longitude) if location else (None, None) except GeocoderTimedOut: return (None, None) def get_location_name_from_coordinates(lat, lon): try: geolocator = Nominatim(user_agent="geoapi") location = geolocator.reverse((lat, lon), timeout=10) return location.address if location else "Tidak ditemukan" except GeocoderTimedOut: return "Tidak ditemukan" # --- Enhancement untuk deskripsi singkat --- def enhance_description_with_gemini(short_desc): prompt = f""" Deskripsi berikut terlalu singkat: "{short_desc}" Tolong kembangkan menjadi paragraf singkat (2–3 kalimat) yang menggambarkan keinginan wisata pengguna secara lebih lengkap. Contohnya: sebutkan suasana, aktivitas, atau lokasi ideal. """ try: response = genai.GenerativeModel("gemini-1.5-flash").generate_content(prompt) return response.text.strip() except Exception as e: return short_desc # --- TF-IDF dan Cosine Similarity --- def prepare_and_recommend(df, user_description): tfidf = TfidfVectorizer() tfidf_matrix = tfidf.fit_transform(df['deskripsi'].astype(str).tolist() + [user_description]) similarity = cosine_similarity(tfidf_matrix[-1], tfidf_matrix[:-1]).flatten() df['similarity'] = similarity return df.sort_values(by='similarity', ascending=False).head(10) # --- Jarak Lokasi --- def sort_by_nearest_location(df, user_lat, user_lon): df['distance_km'] = df.apply( lambda row: geodesic((user_lat, user_lon), (row['latitude'], row['longitude'])).km, axis=1 ) df['distance_km'] = df['distance_km'].round(2) return df.sort_values(by='distance_km') # --- Fungsi Utama --- def wisata_rekomendasi(deskripsi, lokasi): if df.empty: return "Data tidak tersedia.", pd.DataFrame([["Data tidak tersedia"]], columns=["nama"]) if len(deskripsi.strip().split()) < 3 or len(deskripsi.strip()) < 20: deskripsi = enhance_description_with_gemini(deskripsi) # Lokasi → Koordinat lat, lon = get_coordinates_from_location(lokasi) if lat is None or lon is None: return "Lokasi tidak ditemukan.", pd.DataFrame([["Lokasi tidak ditemukan"]], columns=["nama"]) # Tambahkan lokasi ke deskripsi deskripsi_lengkap = f"{deskripsi} di sekitar {lokasi}" keywords = extract_keywords(deskripsi_lengkap) if "Error:" in str(keywords): return f"Kata kunci gagal diambil: {keywords[0]}", pd.DataFrame([[keywords[0]]], columns=["nama"]) user_description_joined = " ".join(keywords) top_place = prepare_and_recommend(df.copy(), user_description_joined) top_place = top_place[top_place['total_ulasan'] > 10] sorted_place = sort_by_nearest_location(top_place, lat, lon) sorted_place = sorted_place[sorted_place["gambar"].apply(lambda x: isinstance(x, str) and x.startswith("https"))] # Urutkan berdasarkan similarity tertinggi dan tampilkan kolom similarity sorted_place = sorted_place.sort_values(by='similarity', ascending=False) return f"Kata kunci: {', '.join(keywords)}", sorted_place[[ "id", "nama", "alamat", "distance_km", "deskripsi", "harga", "rating", "total_ulasan", "gambar", "similarity" ]] # --- Gradio UI --- demo = gr.Interface( fn=wisata_rekomendasi, inputs=[ gr.Textbox(label="Deskripsi Wisata yang Anda Inginkan"), gr.Textbox(label="Lokasi Anda (Contoh: Cilacap, Jawa Tengah, Indonesia)"), ], outputs=[ gr.Textbox(label="Kata Kunci yang Diekstrak"), gr.Dataframe( headers=["id", "nama", "alamat", "distance_km", "deskripsi", "harga", "rating", "total_ulasan", "gambar", "similarity"], label="Rekomendasi Tempat Wisata" ) ], title="Sistem Rekomendasi Wisata", description="Masukkan deskripsi dan lokasi, lalu dapatkan rekomendasi tempat wisata terdekat beserta skor kecocokannya." ) demo.launch()