Spaces:
Sleeping
Sleeping
File size: 6,467 Bytes
639f871 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | """
streamlit_ui.py β Streamlit UI for Tour Generator.
Allows uploading POIs and selecting profile to generate tours.
"""
import streamlit as st
import requests
import json
import os
import pandas as pd
from typing import Optional
from pathlib import Path
from huggingface_hub import CommitScheduler
DATASET_REPO_ID = "NextGenTech/tour-generator-logs"
LOG_DIR = Path("data")
LOG_DIR.mkdir(parents=True, exist_ok=True)
LOG_FILE = LOG_DIR / "tour_results.jsonl"
scheduler = CommitScheduler(
repo_id=DATASET_REPO_ID,
repo_type="dataset",
folder_path=LOG_DIR,
path_in_repo="logs",
every=15
)
st.title("πΊοΈ Tour Generator")
st.markdown("Upload Points of Interest and select a user profile to generate an optimized tour using genetic algorithms.")
# Get available profiles
try:
response = requests.get("http://localhost:8000/profiles")
if response.status_code == 200:
available_profiles = response.json()["profiles"]
else:
available_profiles = ["cultural_walker", "foodie_transit", "family_mixed", "art_lover_car"]
except:
available_profiles = ["cultural_walker", "foodie_transit", "family_mixed", "art_lover_car"]
st.header("π Upload Points of Interest (POIs)")
pois_file = st.file_uploader(
"Upload POIs as CSV or JSON file",
type=['csv', 'json'],
help="CSV columns: id,name,lat,lon,score,visit_duration,time_window_open,time_window_close,category,tags\nJSON: list of POI objects"
)
st.header("π€ Select User Profile")
profile_option = st.radio("Profile Type", ["Predefined", "Custom"])
profile_name: Optional[str] = None
profile_data: Optional[dict] = None
if profile_option == "Predefined":
profile_name = st.selectbox("Choose a predefined profile", available_profiles)
if profile_name:
try:
response = requests.get(f"http://localhost:8000/profiles/{profile_name}")
if response.status_code == 200:
profile_data = response.json()
st.subheader(f"Profile Details: {profile_name}")
st.json(profile_data)
except:
st.warning("Could not load profile details")
else:
profile_file = st.file_uploader(
"Upload custom profile JSON",
type=['json'],
help="JSON object with TouristProfile fields"
)
if profile_file:
try:
profile_data = json.load(profile_file)
st.json(profile_data)
except:
st.error("Invalid JSON file")
st.header("βοΈ Tour Parameters")
col1, col2 = st.columns(2)
with col1:
budget = st.number_input("Time Budget (minutes)", value=480, min_value=60, help="Total available time for the tour")
start_lat = st.number_input("Start Latitude", value=41.9028, format="%.4f")
with col2:
start_time = st.number_input("Start Time (minutes from midnight)", value=540, min_value=0, max_value=1439, help="e.g., 540 = 9:00 AM")
start_lon = st.number_input("Start Longitude", value=12.4964, format="%.4f")
if st.button("π Generate Tour", type="primary"):
if not pois_file:
st.error("Please upload a POIs file")
st.stop()
if profile_option == "Custom" and not profile_data:
st.error("Please upload a custom profile JSON")
st.stop()
# Prepare request
files = {'pois_file': pois_file}
data = {
'budget': budget,
'start_time': start_time,
'start_lat': start_lat,
'start_lon': start_lon,
}
if profile_name:
data['profile_name'] = profile_name
elif profile_data:
data['profile_json'] = json.dumps(profile_data)
with st.spinner("Generating tour... This may take a few moments."):
try:
response = requests.post("http://localhost:8000/generate_tour", files=files, data=data, timeout=60)
if response.status_code == 200:
result = response.json()
# Log the result
log_entry = {
"timestamp": str(pd.Timestamp.now()),
"profile": profile_name if profile_name else "custom",
"budget": budget,
"start_time": start_time,
"start_lat": start_lat,
"start_lon": start_lon,
"result": result
}
with scheduler.lock:
with LOG_FILE.open("a", encoding="utf-8") as f:
json.dump(log_entry, f, ensure_ascii=False)
f.write("\n")
st.success("β
Tour generated successfully!")
# Display summary
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Total Score", f"{result['total_score']:.2f}")
with col2:
st.metric("Total Distance", f"{result['total_distance']:.1f} km")
with col3:
st.metric("Total Time", f"{result['total_time']} min")
st.write(f"**Feasible:** {'β
Yes' if result['is_feasible'] else 'β No'}")
# Display stops
st.header("π Tour Itinerary")
if result['stops']:
stops_df = pd.DataFrame(result['stops'])
stops_df['arrival_time'] = stops_df['arrival'].apply(lambda x: f"{x//60:02d}:{x%60:02d}")
stops_df['departure_time'] = stops_df['departure'].apply(lambda x: f"{x//60:02d}:{x%60:02d}")
stops_df['wait_min'] = stops_df['wait']
st.dataframe(
stops_df[['poi_name', 'arrival_time', 'departure_time', 'wait_min', 'travel_distance_km', 'travel_time_min']],
width="stretch"
)
else:
st.info("No stops in the generated tour")
else:
st.error(f"Error: {response.status_code} - {response.text}")
except requests.exceptions.RequestException as e:
st.error(f"Failed to connect to the API. Make sure the FastAPI server is running on localhost:8000\nError: {str(e)}")
st.markdown("---")
st.markdown("**Note:** Make sure to start the FastAPI server first: `python app.py` or `uvicorn app:app --reload`") |