Spaces:
Sleeping
Sleeping
Add js to convert times to browser's timezone
Browse files
app.py
CHANGED
|
@@ -166,7 +166,7 @@ def fetch_yata(force_refresh=False):
|
|
| 166 |
_cache.update({
|
| 167 |
"data": data,
|
| 168 |
"timestamp": time.time(),
|
| 169 |
-
"last_update": time.strftime("%Y-%m-%
|
| 170 |
})
|
| 171 |
print(f"β
Fetched YATA data at {_cache['last_update']}")
|
| 172 |
return data, _cache["last_update"]
|
|
@@ -207,7 +207,7 @@ def query_inventory(query_text="", category="", country_name="", capacity=10, re
|
|
| 207 |
elif country_name.lower() not in cname.lower():
|
| 208 |
continue
|
| 209 |
update_ts = cdata.get("update")
|
| 210 |
-
update_str = time.strftime("%Y-%m-%
|
| 211 |
for item in cdata.get("stocks", []):
|
| 212 |
iname = item.get("name", "")
|
| 213 |
itype = ITEM_TO_TYPE.get(iname, "").lower()
|
|
@@ -247,33 +247,10 @@ def run_query(query_text, category, country, capacity, refresh):
|
|
| 247 |
live_categories = get_live_categories(data)
|
| 248 |
return df, ts, gr.update(choices=[""] + live_categories)
|
| 249 |
|
| 250 |
-
def run_multi(queries, capacity, refresh):
|
| 251 |
-
data, _ = fetch_yata(force_refresh=refresh)
|
| 252 |
-
dfs = []
|
| 253 |
-
for qtext in queries:
|
| 254 |
-
print(f"β‘ Running convenience subquery: {qtext}")
|
| 255 |
-
df, _ = query_inventory(qtext, "", "", capacity, refresh)
|
| 256 |
-
if "Result" not in df.columns:
|
| 257 |
-
dfs.append(df)
|
| 258 |
-
if not dfs:
|
| 259 |
-
return pd.DataFrame([{"Result": "No results for that set."}]), "No results.", gr.update()
|
| 260 |
-
merged = pd.concat(dfs, ignore_index=True).sort_values(by=["Country", "Item"])
|
| 261 |
-
for col in ["Quantity", "Cost", "Max Capacity Cost"]:
|
| 262 |
-
merged[col] = merged[col].apply(lambda x: f"{x:,.0f}" if isinstance(x, (int, float)) else x)
|
| 263 |
-
live_categories = get_live_categories(data)
|
| 264 |
-
return merged, f"Last update: {time.strftime('%Y-%m-%d %H:%M:%S')}", gr.update(choices=[""] + live_categories)
|
| 265 |
-
|
| 266 |
# ---------------- Gradio UI ----------------
|
| 267 |
-
with gr.Blocks(title="π§³ Torn
|
| 268 |
-
gr.Markdown("## π§³ Torn
|
| 269 |
-
gr.Markdown("_Search YATA
|
| 270 |
-
|
| 271 |
-
with gr.Row():
|
| 272 |
-
btn_short = gr.Button("πΈ Flushies (Short Haul)")
|
| 273 |
-
btn_med = gr.Button("βοΈ Flushies (Medium Haul)")
|
| 274 |
-
btn_long = gr.Button("π« Flushies (Long Haul)")
|
| 275 |
-
btn_xanax = gr.Button("π Xanax (SA)")
|
| 276 |
-
btn_temps = gr.Button("π£ Temps")
|
| 277 |
|
| 278 |
query_box = gr.Textbox(label="Search (semantic, e.g. 'flowers in England')", elem_id="qbox")
|
| 279 |
category_drop = gr.Dropdown(label="Category (optional exact match)", choices=[""] + ALL_CATEGORIES, elem_id="catdrop")
|
|
@@ -281,83 +258,34 @@ with gr.Blocks(title="π§³ Torn Foreign Stocks") as iface:
|
|
| 281 |
capacity_slider = gr.Number(label="Travel Capacity", value=10, minimum=5, maximum=88, precision=0, elem_id="cap_num")
|
| 282 |
refresh_check = gr.Checkbox(label="Force refresh (ignore cache)", value=False)
|
| 283 |
|
| 284 |
-
cap_bridge = gr.Textbox(visible=False, elem_id="cap_bridge")
|
| 285 |
-
|
| 286 |
-
def apply_bridge(cap_text):
|
| 287 |
-
try:
|
| 288 |
-
v = float(cap_text)
|
| 289 |
-
if 5 <= v <= 88:
|
| 290 |
-
return gr.Number.update(value=v)
|
| 291 |
-
except:
|
| 292 |
-
pass
|
| 293 |
-
return gr.Number.update()
|
| 294 |
-
|
| 295 |
-
cap_bridge.change(apply_bridge, inputs=[cap_bridge], outputs=[capacity_slider])
|
| 296 |
-
|
| 297 |
-
run_btn = gr.Button("π Search / Refresh")
|
| 298 |
result_df = gr.Dataframe(label="Results")
|
| 299 |
meta_box = gr.Textbox(label="Metadata / Last Update")
|
| 300 |
|
| 301 |
-
run_btn.
|
| 302 |
-
|
| 303 |
outputs=[result_df, meta_box, category_drop])
|
| 304 |
|
| 305 |
-
|
| 306 |
-
["flowers in mexico", "flowers in cayman islands", "flowers in canada",
|
| 307 |
-
"plushies in mexico", "plushies in cayman islands", "plushies in canada"], c, r),
|
| 308 |
-
inputs=[capacity_slider, refresh_check], outputs=[result_df, meta_box, category_drop])
|
| 309 |
-
|
| 310 |
-
btn_med.click(lambda c, r: run_multi(
|
| 311 |
-
["flowers in hawaii", "flowers in united kingdom", "flowers in argentina",
|
| 312 |
-
"flowers in switzerland", "flowers in japan",
|
| 313 |
-
"plushies in hawaii", "plushies in united kingdom", "plushies in argentina",
|
| 314 |
-
"plushies in switzerland", "plushies in japan"], c, r),
|
| 315 |
-
inputs=[capacity_slider, refresh_check], outputs=[result_df, meta_box, category_drop])
|
| 316 |
-
|
| 317 |
-
btn_long.click(lambda c, r: run_multi(
|
| 318 |
-
["flowers in uae", "flowers in china", "flowers in south africa",
|
| 319 |
-
"plushies in uae", "plushies in china", "plushies in south africa"], c, r),
|
| 320 |
-
inputs=[capacity_slider, refresh_check], outputs=[result_df, meta_box, category_drop])
|
| 321 |
-
|
| 322 |
-
btn_xanax.click(lambda c, r: run_multi(["xanax in south africa"], c, r),
|
| 323 |
-
inputs=[capacity_slider, refresh_check], outputs=[result_df, meta_box, category_drop])
|
| 324 |
-
|
| 325 |
-
btn_temps.click(lambda c, r: run_multi(
|
| 326 |
-
["tear gas in argentina", "smoke grenade in south africa", "flash grenade in switzerland"], c, r),
|
| 327 |
-
inputs=[capacity_slider, refresh_check], outputs=[result_df, meta_box, category_drop])
|
| 328 |
-
|
| 329 |
-
# --- JS for capacity persistence ---
|
| 330 |
gr.HTML("""
|
| 331 |
<script>
|
| 332 |
-
(function
|
| 333 |
-
function
|
| 334 |
-
const
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
bridge.value = saved;
|
| 347 |
-
bridge.dispatchEvent(new Event('input', { bubbles: true }));
|
| 348 |
-
bridge.dispatchEvent(new Event('change', { bubbles: true }));
|
| 349 |
}
|
| 350 |
-
}
|
| 351 |
-
if (num && !num.dataset.synced) {
|
| 352 |
-
num.dataset.synced = '1';
|
| 353 |
-
num.addEventListener('input', () => {
|
| 354 |
-
localStorage.setItem('travel_capacity', num.value);
|
| 355 |
-
});
|
| 356 |
-
}
|
| 357 |
}
|
| 358 |
-
|
| 359 |
-
const obs = new MutationObserver(() => setTimeout(restore, 100));
|
| 360 |
-
obs.observe(document.documentElement, { childList: true, subtree: true });
|
| 361 |
})();
|
| 362 |
</script>
|
| 363 |
""")
|
|
|
|
| 166 |
_cache.update({
|
| 167 |
"data": data,
|
| 168 |
"timestamp": time.time(),
|
| 169 |
+
"last_update": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) # UTC ISO
|
| 170 |
})
|
| 171 |
print(f"β
Fetched YATA data at {_cache['last_update']}")
|
| 172 |
return data, _cache["last_update"]
|
|
|
|
| 207 |
elif country_name.lower() not in cname.lower():
|
| 208 |
continue
|
| 209 |
update_ts = cdata.get("update")
|
| 210 |
+
update_str = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(update_ts)) if update_ts else "Unknown"
|
| 211 |
for item in cdata.get("stocks", []):
|
| 212 |
iname = item.get("name", "")
|
| 213 |
itype = ITEM_TO_TYPE.get(iname, "").lower()
|
|
|
|
| 247 |
live_categories = get_live_categories(data)
|
| 248 |
return df, ts, gr.update(choices=[""] + live_categories)
|
| 249 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
# ---------------- Gradio UI ----------------
|
| 251 |
+
with gr.Blocks(title="π§³ Torn Inventory Viewer") as iface:
|
| 252 |
+
gr.Markdown("## π§³ Torn Inventory Viewer")
|
| 253 |
+
gr.Markdown("_Search Torn YATA travel stocks β timestamps shown in your local timezone._")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
|
| 255 |
query_box = gr.Textbox(label="Search (semantic, e.g. 'flowers in England')", elem_id="qbox")
|
| 256 |
category_drop = gr.Dropdown(label="Category (optional exact match)", choices=[""] + ALL_CATEGORIES, elem_id="catdrop")
|
|
|
|
| 258 |
capacity_slider = gr.Number(label="Travel Capacity", value=10, minimum=5, maximum=88, precision=0, elem_id="cap_num")
|
| 259 |
refresh_check = gr.Checkbox(label="Force refresh (ignore cache)", value=False)
|
| 260 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 261 |
result_df = gr.Dataframe(label="Results")
|
| 262 |
meta_box = gr.Textbox(label="Metadata / Last Update")
|
| 263 |
|
| 264 |
+
run_btn = gr.Button("π Search / Refresh")
|
| 265 |
+
run_btn.click(run_query, inputs=[query_box, category_drop, country_box, capacity_slider, refresh_check],
|
| 266 |
outputs=[result_df, meta_box, category_drop])
|
| 267 |
|
| 268 |
+
# --- JS for local time conversion ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
gr.HTML("""
|
| 270 |
<script>
|
| 271 |
+
(function(){
|
| 272 |
+
function convertTimesToLocal(){
|
| 273 |
+
const elems = document.querySelectorAll("td, textarea, input");
|
| 274 |
+
const fmt = new Intl.DateTimeFormat([], {year:"numeric", month:"2-digit", day:"2-digit",
|
| 275 |
+
hour:"2-digit", minute:"2-digit", second:"2-digit"});
|
| 276 |
+
elems.forEach(el=>{
|
| 277 |
+
const text = el.value || el.textContent;
|
| 278 |
+
if (text && /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$/.test(text.trim())){
|
| 279 |
+
try{
|
| 280 |
+
const dt = new Date(text.trim());
|
| 281 |
+
const local = fmt.format(dt);
|
| 282 |
+
if (el.value !== undefined) el.value = local;
|
| 283 |
+
else el.textContent = local;
|
| 284 |
+
}catch{}
|
|
|
|
|
|
|
|
|
|
| 285 |
}
|
| 286 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 287 |
}
|
| 288 |
+
setInterval(convertTimesToLocal, 1000);
|
|
|
|
|
|
|
| 289 |
})();
|
| 290 |
</script>
|
| 291 |
""")
|