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`")