nmo-genio commited on
Commit
20cf0d0
·
verified ·
1 Parent(s): 64f31ba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +82 -55
app.py CHANGED
@@ -1,18 +1,15 @@
1
  import csv
2
  import os
3
  import gradio as gr
 
 
4
 
5
  CSV_PATH = "zones.csv"
6
 
7
  def load_zones(csv_path: str = CSV_PATH):
8
- """
9
- Loads zones from a CSV with columns:
10
- zone_id,zone_name,latitude,longitude,venue_density,foot_traffic_index,
11
- nightlife_score,daytime_score,primary_vibe,notes
12
- """
13
  if not os.path.exists(csv_path):
14
  raise FileNotFoundError(
15
- f"Can't find {csv_path}. Put zones.csv in the same folder as app.py (Files tab)."
16
  )
17
 
18
  zones = []
@@ -32,28 +29,25 @@ def load_zones(csv_path: str = CSV_PATH):
32
  }
33
  missing = required - set(reader.fieldnames or [])
34
  if missing:
35
- raise ValueError(
36
- "zones.csv is missing columns: " + ", ".join(sorted(missing))
37
- )
38
 
39
- for row in reader:
40
- # Convert numeric fields safely
41
- def to_float(key, default=0.0):
42
- try:
43
- return float(row.get(key, default))
44
- except (TypeError, ValueError):
45
- return default
46
 
 
47
  zones.append(
48
  {
49
- "zone_id": row["zone_id"].strip(),
50
- "zone_name": row["zone_name"].strip(),
51
- "lat": to_float("latitude"),
52
- "lon": to_float("longitude"),
53
- "venue_density": to_float("venue_density"),
54
- "foot_traffic": to_float("foot_traffic_index"),
55
- "nightlife": to_float("nightlife_score"),
56
- "daytime": to_float("daytime_score"),
57
  "vibe": (row.get("primary_vibe") or "").strip(),
58
  "notes": (row.get("notes") or "").strip(),
59
  }
@@ -61,12 +55,6 @@ def load_zones(csv_path: str = CSV_PATH):
61
  return zones
62
 
63
  def score_zone(zone, time_of_day: str) -> float:
64
- """
65
- Simple, explainable scoring:
66
- - foot_traffic is the main "mobility" signal
67
- - time_of_day picks nightlife/daytime emphasis
68
- - venue_density boosts places that have lots of options nearby
69
- """
70
  ft = zone["foot_traffic"]
71
  vd = zone["venue_density"]
72
  nl = zone["nightlife"]
@@ -75,34 +63,52 @@ def score_zone(zone, time_of_day: str) -> float:
75
  if time_of_day == "Tonight":
76
  time_factor = nl
77
  elif time_of_day == "Weekend":
78
- time_factor = 0.6 * nl + 0.4 * dt # weekends are mixed
79
- else: # "Now"
80
  time_factor = 0.5 * nl + 0.5 * dt
81
 
82
- # Weighted blend (kept simple and stable)
83
  return (0.55 * ft) + (0.30 * time_factor) + (0.15 * vd)
84
 
85
- def explain_zone(zone, time_of_day: str) -> str:
86
- if time_of_day == "Tonight":
87
- angle = "nightlife score"
88
- elif time_of_day == "Weekend":
89
- angle = "weekend mix (night + day)"
90
- else:
91
- angle = "balanced (day + night)"
92
-
93
- return (
94
- f"Why: mobility/foot-traffic + {angle} + venue density. "
95
- f"{zone['notes']}".strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  )
 
97
 
98
  def find_hot_places(time_of_day: str, top_k: int):
99
  try:
100
  zones = load_zones(CSV_PATH)
101
  except Exception as e:
102
- return f"⚠️ {e}"
103
 
104
  if not zones:
105
- return "No zones found in zones.csv."
106
 
107
  ranked = []
108
  for z in zones:
@@ -112,16 +118,30 @@ def find_hot_places(time_of_day: str, top_k: int):
112
  ranked.sort(key=lambda x: x[0], reverse=True)
113
  ranked = ranked[: max(1, int(top_k))]
114
 
 
115
  lines = []
116
  for i, (s, z) in enumerate(ranked, start=1):
 
 
 
 
 
 
 
 
 
 
 
 
117
  lines.append(
118
  f"{i}. 📍 {z['zone_name']} (score: {s:.2f})\n"
119
  f"Vibe: {z['vibe']}\n"
120
- f"{explain_zone(z, time_of_day)}\n"
121
- f"Coords: {z['lat']:.4f}, {z['lon']:.4f}"
122
  )
123
 
124
- return "\n\n---\n\n".join(lines)
 
 
125
 
126
  with gr.Blocks() as demo:
127
  gr.Markdown(
@@ -134,16 +154,23 @@ with gr.Blocks() as demo:
134
  time_input = gr.Dropdown(
135
  choices=["Now", "Tonight", "Weekend"],
136
  value="Now",
137
- label="When are you going out?"
138
  )
139
  top_k = gr.Slider(
140
- minimum=1, maximum=10, value=4, step=1,
141
- label="How many zones to show?"
 
 
 
142
  )
143
 
144
- output = gr.Textbox(label="Hot Zones", lines=16)
145
-
146
  btn = gr.Button("Find hot places")
147
- btn.click(fn=find_hot_places, inputs=[time_input, top_k], outputs=output)
 
 
 
 
 
 
148
 
149
  demo.launch()
 
1
  import csv
2
  import os
3
  import gradio as gr
4
+ import pandas as pd
5
+ import plotly.express as px
6
 
7
  CSV_PATH = "zones.csv"
8
 
9
  def load_zones(csv_path: str = CSV_PATH):
 
 
 
 
 
10
  if not os.path.exists(csv_path):
11
  raise FileNotFoundError(
12
+ f"Can't find {csv_path}. Add zones.csv in the Space Files tab (same folder as app.py)."
13
  )
14
 
15
  zones = []
 
29
  }
30
  missing = required - set(reader.fieldnames or [])
31
  if missing:
32
+ raise ValueError("zones.csv is missing columns: " + ", ".join(sorted(missing)))
 
 
33
 
34
+ def to_float(val, default=0.0):
35
+ try:
36
+ return float(val)
37
+ except (TypeError, ValueError):
38
+ return default
 
 
39
 
40
+ for row in reader:
41
  zones.append(
42
  {
43
+ "zone_id": (row.get("zone_id") or "").strip(),
44
+ "zone_name": (row.get("zone_name") or "").strip(),
45
+ "lat": to_float(row.get("latitude")),
46
+ "lon": to_float(row.get("longitude")),
47
+ "venue_density": to_float(row.get("venue_density")),
48
+ "foot_traffic": to_float(row.get("foot_traffic_index")),
49
+ "nightlife": to_float(row.get("nightlife_score")),
50
+ "daytime": to_float(row.get("daytime_score")),
51
  "vibe": (row.get("primary_vibe") or "").strip(),
52
  "notes": (row.get("notes") or "").strip(),
53
  }
 
55
  return zones
56
 
57
  def score_zone(zone, time_of_day: str) -> float:
 
 
 
 
 
 
58
  ft = zone["foot_traffic"]
59
  vd = zone["venue_density"]
60
  nl = zone["nightlife"]
 
63
  if time_of_day == "Tonight":
64
  time_factor = nl
65
  elif time_of_day == "Weekend":
66
+ time_factor = 0.6 * nl + 0.4 * dt
67
+ else: # Now
68
  time_factor = 0.5 * nl + 0.5 * dt
69
 
 
70
  return (0.55 * ft) + (0.30 * time_factor) + (0.15 * vd)
71
 
72
+ def build_map(df: pd.DataFrame, time_of_day: str):
73
+ if df.empty:
74
+ return None
75
+
76
+ # Center roughly around Sydney
77
+ center_lat = df["lat"].mean()
78
+ center_lon = df["lon"].mean()
79
+
80
+ fig = px.scatter_mapbox(
81
+ df,
82
+ lat="lat",
83
+ lon="lon",
84
+ hover_name="zone_name",
85
+ hover_data={
86
+ "score": ":.2f",
87
+ "vibe": True,
88
+ "lat": False,
89
+ "lon": False,
90
+ },
91
+ size="score",
92
+ size_max=18,
93
+ zoom=10,
94
+ center={"lat": center_lat, "lon": center_lon},
95
+ title=f"Hot Zones Map ({time_of_day})",
96
+ )
97
+ fig.update_layout(
98
+ mapbox_style="open-street-map",
99
+ margin={"l": 0, "r": 0, "t": 40, "b": 0},
100
+ height=520,
101
  )
102
+ return fig
103
 
104
  def find_hot_places(time_of_day: str, top_k: int):
105
  try:
106
  zones = load_zones(CSV_PATH)
107
  except Exception as e:
108
+ return f"⚠️ {e}", None
109
 
110
  if not zones:
111
+ return "No zones found in zones.csv.", None
112
 
113
  ranked = []
114
  for z in zones:
 
118
  ranked.sort(key=lambda x: x[0], reverse=True)
119
  ranked = ranked[: max(1, int(top_k))]
120
 
121
+ rows = []
122
  lines = []
123
  for i, (s, z) in enumerate(ranked, start=1):
124
+ rows.append(
125
+ {
126
+ "rank": i,
127
+ "zone_name": z["zone_name"],
128
+ "lat": z["lat"],
129
+ "lon": z["lon"],
130
+ "score": s,
131
+ "vibe": z["vibe"],
132
+ "notes": z["notes"],
133
+ }
134
+ )
135
+
136
  lines.append(
137
  f"{i}. 📍 {z['zone_name']} (score: {s:.2f})\n"
138
  f"Vibe: {z['vibe']}\n"
139
+ f"Why: {z['notes']}"
 
140
  )
141
 
142
+ df = pd.DataFrame(rows)
143
+ fig = build_map(df, time_of_day)
144
+ return "\n\n---\n\n".join(lines), fig
145
 
146
  with gr.Blocks() as demo:
147
  gr.Markdown(
 
154
  time_input = gr.Dropdown(
155
  choices=["Now", "Tonight", "Weekend"],
156
  value="Now",
157
+ label="When are you going out?",
158
  )
159
  top_k = gr.Slider(
160
+ minimum=1,
161
+ maximum=12,
162
+ value=6,
163
+ step=1,
164
+ label="How many zones to show?",
165
  )
166
 
 
 
167
  btn = gr.Button("Find hot places")
168
+
169
+ # On desktop this shows side-by-side; on phone it stacks nicely.
170
+ with gr.Row():
171
+ output_text = gr.Textbox(label="Ranked Zones", lines=16)
172
+ output_map = gr.Plot(label="Map")
173
+
174
+ btn.click(fn=find_hot_places, inputs=[time_input, top_k], outputs=[output_text, output_map])
175
 
176
  demo.launch()