Spaces:
Sleeping
Sleeping
File size: 6,432 Bytes
3ebd172 5bf53f5 3ebd172 5bf53f5 3ebd172 876e202 5bf53f5 3ebd172 876e202 3ebd172 5bf53f5 876e202 3ebd172 5bf53f5 876e202 3ebd172 5bf53f5 876e202 5bf53f5 3ebd172 5bf53f5 3ebd172 876e202 5bf53f5 876e202 3ebd172 5bf53f5 3ebd172 5bf53f5 3ebd172 5bf53f5 3ebd172 876e202 3ebd172 876e202 3ebd172 5bf53f5 3ebd172 5bf53f5 3ebd172 876e202 3ebd172 876e202 3ebd172 876e202 3ebd172 5bf53f5 876e202 3ebd172 5bf53f5 3ebd172 5bf53f5 876e202 5bf53f5 3ebd172 5bf53f5 3ebd172 5bf53f5 876e202 3ebd172 5bf53f5 3ebd172 5bf53f5 |
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 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
import gradio as gr
import time
import json
import os
from collections import deque, defaultdict
import gspread
from oauth2client.service_account import ServiceAccountCredentials
# --- Configuration ---
MAX_RECENT_EVENTS = 1000 # Trigger snapshot compression at this count
GRID_PRECISION = 4 # ~11 meters precision
RADIUS_BASE = 20
GOOGLE_SHEET_NAME = "UrbanScout_Data" # Name of your Google Sheet
GOOGLE_CREDS_FILE = "credentials.json" # Path to your Service Account JSON
# --- Data Structures ---
# 1. Movement Data (Fog of War)
recent_events = deque()
archived_grid = defaultdict(int)
# 2. Static Markers (The Rental Spaces)
# We store these in memory for fast serving to map,
# and also send them to Google Sheets for persistence/analysis.
reported_markers = []
# 3. Version Control
snapshot_version = 0
last_snapshot_time = time.time()
# --- Google Sheets Setup ---
def init_google_sheets():
try:
scope = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive']
# You need to generate this JSON from Google Cloud Console
creds = ServiceAccountCredentials.from_json_keyfile_name(GOOGLE_CREDS_FILE, scope)
client = gspread.authorize(creds)
sheet = client.open(GOOGLE_SHEET_NAME).sheet1
return sheet
except Exception as e:
print(f"[Warning] Google Sheets not connected: {e}")
return None
# Initialize Sheet (Global)
gsheet = init_google_sheets()
# --- Helper Functions ---
def get_radius_by_speed(speed_kmh):
if speed_kmh is None: return 10
if speed_kmh < 6: return 35 # Walking
if speed_kmh < 25: return 15 # Biking
return 8 # Driving
def trigger_snapshot():
"""Compress recent movement events."""
global recent_events, snapshot_version, last_snapshot_time
if len(recent_events) > MAX_RECENT_EVENTS:
events_to_archive = []
count_to_remove = int(MAX_RECENT_EVENTS / 2)
for _ in range(count_to_remove):
events_to_archive.append(recent_events.popleft())
for event in events_to_archive:
lat, lng = event['l']
radius = event['r']
grid_key = (round(lat, GRID_PRECISION), round(lng, GRID_PRECISION))
if radius > archived_grid[grid_key]:
archived_grid[grid_key] = radius
snapshot_version += 1
print(f"[Server] Snapshot v{snapshot_version}")
# --- API Endpoints ---
def sync_game_state(client_last_time, client_ver, user_id, lat, lng, speed):
"""
Handles Fog of War updates AND returns reported markers.
"""
current_time = time.time()
# 1. Record Movement
if lat is not None and lng is not None:
if speed is None or speed < 150:
radius = get_radius_by_speed(speed)
new_event = {
"t": current_time,
"u": user_id,
"l": [lat, lng],
"r": radius
}
recent_events.append(new_event)
trigger_snapshot()
# 2. Prepare Movement Payload
response = {
"server_time": current_time,
"snapshot_version": snapshot_version,
"sync_type": "delta",
"payload": [],
"markers": reported_markers # Send the pins to the client
}
if client_ver < snapshot_version:
response["sync_type"] = "snapshot"
response["payload"] = [[k[0], k[1], v] for k, v in archived_grid.items()]
response["recent_events"] = [[e['l'][0], e['l'][1], e['r']] for e in recent_events]
else:
delta = [[e['l'][0], e['l'][1], e['r']] for e in recent_events if e['t'] > client_last_time]
response["payload"] = delta
return response
def report_discovery(report_json):
"""
New Endpoint: Receives the form data from the Report Modal.
"""
try:
data = report_json if isinstance(report_json, dict) else json.loads(report_json)
# 1. Add to In-Memory list (so other users see it immediately on map)
marker_data = {
"lat": data.get("lat"),
"lng": data.get("lng"),
"name": data.get("spaceName"),
"type": "rental",
"info": data.get("additionalInfo"),
"price": data.get("price"),
"cap": data.get("capacity"),
"addr": data.get("address"),
"book": data.get("bookingMethod")
}
reported_markers.append(marker_data)
# 2. Send to Google Sheets (Async/Best Effort)
if gsheet:
row = [
data.get("timestamp"),
data.get("lat"),
data.get("lng"),
data.get("address"),
data.get("spaceName"),
data.get("rentableCount"),
data.get("capacity"),
data.get("price"),
data.get("bookingMethod"),
data.get("additionalInfo")
]
gsheet.append_row(row)
print(f"[Sheet] Logged: {data.get('spaceName')}")
return {"status": "success", "message": "Discovery logged"}
except Exception as e:
print(f"Error reporting: {e}")
return {"status": "error", "message": str(e)}
# --- Gradio App ---
with gr.Blocks() as demo:
gr.Markdown("## City Explorer Backend")
# Hidden inputs for the Game Loop
with gr.Row(visible=False):
in_time = gr.Number()
in_ver = gr.Number()
in_uid = gr.Textbox()
in_lat = gr.Number()
in_lng = gr.Number()
in_speed = gr.Number()
out_sync_json = gr.JSON()
# Endpoint 1: The fast sync loop
btn_sync = gr.Button("Sync Loop (Hidden)")
btn_sync.click(
fn=sync_game_state,
inputs=[in_time, in_ver, in_uid, in_lat, in_lng, in_speed],
outputs=out_sync_json,
api_name="sync"
)
# Endpoint 2: The report form submission
with gr.Row(visible=False):
in_report_json = gr.JSON()
out_report_status = gr.JSON()
btn_report = gr.Button("Report Discovery (Hidden)")
btn_report.click(
fn=report_discovery,
inputs=[in_report_json],
outputs=out_report_status,
api_name="report"
)
if __name__ == "__main__":
demo.queue().launch(server_name="0.0.0.0", server_port=7860) |