buzzbandit commited on
Commit
bbb5424
Β·
verified Β·
1 Parent(s): 2f7af21

hide categories that aren't sold abroad

Browse files
Files changed (1) hide show
  1. app.py +67 -102
app.py CHANGED
@@ -19,6 +19,7 @@ with open("items.json", "r", encoding="utf-8") as f:
19
  items_data = json.load(f)["items"]
20
 
21
  ITEM_TO_TYPE = {v["name"]: v["type"].lower() for v in items_data.values() if "name" in v and "type" in v}
 
22
  ALL_CATEGORIES = sorted(set(ITEM_TO_TYPE.values()))
23
  ITEM_FILE_MTIME = os.path.getmtime("items.json")
24
 
@@ -177,6 +178,17 @@ def fetch_yata(force_refresh=False):
177
  print(f"❌ Fetch error: {e}")
178
  return {"stocks": {}}, "Fetch failed"
179
 
 
 
 
 
 
 
 
 
 
 
 
180
  # ---------------- Core logic ----------------
181
  def query_inventory(query_text="", category="", country_name="", capacity=10, refresh=False):
182
  print(f"πŸ”Ž Query received: '{query_text}', category='{category}', country='{country_name}', cap={capacity}")
@@ -184,7 +196,6 @@ def query_inventory(query_text="", category="", country_name="", capacity=10, re
184
  stocks = data.get("stocks", {})
185
  rows = []
186
 
187
- # Parse query
188
  parsed_item, parsed_country = parse_freeform_query(query_text)
189
  if not country_name and parsed_country:
190
  country_name = parsed_country
@@ -193,7 +204,6 @@ def query_inventory(query_text="", category="", country_name="", capacity=10, re
193
  semantic_items = semantic_result["items"]
194
  semantic_category = semantic_result["category"]
195
 
196
- # Country gating (strict)
197
  user_code = normalize_country_query(country_name)
198
 
199
  for code_raw, cdata in stocks.items():
@@ -210,14 +220,12 @@ def query_inventory(query_text="", category="", country_name="", capacity=10, re
210
  update_ts = cdata.get("update")
211
  update_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(update_ts)) if update_ts else "Unknown"
212
 
213
- # ONLY iterate this country's actual items (no global catalog)
214
  for item in cdata.get("stocks", []):
215
  iname = item.get("name", "")
216
  itype = ITEM_TO_TYPE.get(iname, "").lower()
217
  qty = item.get("quantity", 0)
218
  cost = item.get("cost", 0)
219
 
220
- # Item filter
221
  if item_term:
222
  item_ok = (
223
  item_term.lower() in iname.lower()
@@ -236,7 +244,6 @@ def query_inventory(query_text="", category="", country_name="", capacity=10, re
236
  "Category": itype.title() if itype else "",
237
  "Quantity": qty,
238
  "Cost": cost,
239
- # IMPORTANT: use the function arg 'capacity' directly
240
  "Max Capacity Cost": cost * capacity,
241
  "Updated": update_str,
242
  })
@@ -250,16 +257,15 @@ def query_inventory(query_text="", category="", country_name="", capacity=10, re
250
  df[col] = df[col].apply(lambda x: f"{x:,.0f}" if isinstance(x, (int, float)) and x != "" else x)
251
  return df, f"Last update: {last_update}"
252
 
253
- # ---------------- Wrapper ----------------
254
  def run_query(query_text, category, country, capacity, refresh):
255
- return query_inventory(query_text, category, country, capacity, refresh)
256
-
257
- # ---------------- Gradio UI ----------------
258
- def run_query(query_text, category, country, capacity, refresh):
259
- return query_inventory(query_text, category, country, capacity, refresh)
260
 
261
  def run_multi(queries, capacity, refresh):
262
- """Execute multiple queries and merge all results."""
263
  dfs = []
264
  for qtext in queries:
265
  print(f"⚑ Running convenience subquery: {qtext}")
@@ -267,24 +273,24 @@ def run_multi(queries, capacity, refresh):
267
  if "Result" not in df.columns:
268
  dfs.append(df)
269
  if not dfs:
270
- return pd.DataFrame([{"Result": "No results for that set."}]), "No results."
271
- merged = pd.concat(dfs, ignore_index=True)
272
- merged = merged.sort_values(by=["Country", "Item"])
273
  for col in ["Quantity", "Cost", "Max Capacity Cost"]:
274
  if col in merged.columns:
275
  merged[col] = merged[col].apply(lambda x: f"{x:,.0f}" if isinstance(x, (int, float)) and x != "" else x)
276
- return merged, f"Last update: {time.strftime('%Y-%m-%d %H:%M:%S')}"
 
277
 
 
278
  with gr.Blocks(title="🧳 Torn Inventory Viewer") as iface:
279
  gr.Markdown("## 🧳 Torn Inventory Viewer")
280
  gr.Markdown("_Search Torn YATA travel stocks with semantic matching._")
281
 
282
- # --- Convenience Buttons ---
283
  with gr.Row():
284
  btn_short = gr.Button("🌸 Flushies (Short Haul)")
285
  btn_med = gr.Button("✈️ Flushies (Medium Haul)")
286
  btn_long = gr.Button("πŸ›« Flushies (Long Haul)")
287
- btn_xanax = gr.Button("πŸ’Š Xanax (SA)")
288
  btn_temps = gr.Button("πŸ’£ Temps")
289
 
290
  query_box = gr.Textbox(label="Search (semantic, e.g. 'flowers in England')", elem_id="qbox")
@@ -293,7 +299,6 @@ with gr.Blocks(title="🧳 Torn Inventory Viewer") as iface:
293
  capacity_slider = gr.Number(label="Travel Capacity", value=10, minimum=5, maximum=88, precision=0, elem_id="cap_num")
294
  refresh_check = gr.Checkbox(label="Force refresh (ignore cache)", value=False)
295
 
296
- # Hidden bridge for JS sync
297
  cap_bridge = gr.Textbox(visible=False, elem_id="cap_bridge")
298
 
299
  def apply_bridge(cap_text):
@@ -313,77 +318,38 @@ with gr.Blocks(title="🧳 Torn Inventory Viewer") as iface:
313
 
314
  run_btn.click(run_query,
315
  inputs=[query_box, category_drop, country_box, capacity_slider, refresh_check],
316
- outputs=[result_df, meta_box])
317
-
318
- # --- Convenience button handlers ---
319
- btn_short.click(
320
- fn=lambda capacity, refresh: run_multi(
321
- [
322
- "flowers in mexico",
323
- "flowers in cayman islands",
324
- "flowers in canada",
325
- "plushies in mexico",
326
- "plushies in cayman islands",
327
- "plushies in canada",
328
- ],
329
- capacity, refresh),
330
- inputs=[capacity_slider, refresh_check],
331
- outputs=[result_df, meta_box],
332
- )
333
-
334
- btn_med.click(
335
- fn=lambda capacity, refresh: run_multi(
336
- [
337
- "flowers in hawaii",
338
- "flowers in united kingdom",
339
- "flowers in argentina",
340
- "flowers in switzerland",
341
- "flowers in japan",
342
- "plushies in hawaii",
343
- "plushies in united kingdom",
344
- "plushies in argentina",
345
- "plushies in switzerland",
346
- "plushies in japan",
347
- ],
348
- capacity, refresh),
349
- inputs=[capacity_slider, refresh_check],
350
- outputs=[result_df, meta_box],
351
- )
352
-
353
- btn_long.click(
354
- fn=lambda capacity, refresh: run_multi(
355
- [
356
- "flowers in uae",
357
- "flowers in china",
358
- "flowers in south africa",
359
- "plushies in uae",
360
- "plushies in china",
361
- "plushies in south africa",
362
- ],
363
- capacity, refresh),
364
- inputs=[capacity_slider, refresh_check],
365
- outputs=[result_df, meta_box],
366
- )
367
-
368
- btn_xanax.click(
369
- fn=lambda capacity, refresh: run_multi(["xanax in south africa"], capacity, refresh),
370
- inputs=[capacity_slider, refresh_check],
371
- outputs=[result_df, meta_box],
372
- )
373
-
374
- btn_temps.click(
375
- fn=lambda capacity, refresh: run_multi(
376
- [
377
- "tear gas in argentina",
378
- "smoke grenade in south africa",
379
- "flash grenade in switzerland",
380
- ],
381
- capacity, refresh),
382
- inputs=[capacity_slider, refresh_check],
383
- outputs=[result_df, meta_box],
384
- )
385
-
386
- # --- JS: restore capacity ---
387
  gr.HTML("""
388
  <script>
389
  (function () {
@@ -392,29 +358,28 @@ with gr.Blocks(title="🧳 Torn Inventory Viewer") as iface:
392
  return app && app.shadowRoot ? app.shadowRoot : document;
393
  }
394
  function el(sel) { return root().querySelector(sel); }
395
-
396
- function restoreCapacity() {
397
  const saved = localStorage.getItem('travel_capacity');
398
- const capField = el('#cap_num input[type=number]');
399
  const bridge = el('#cap_bridge textarea');
400
- if (saved && capField) {
401
- capField.value = saved;
402
- capField.dispatchEvent(new Event('input', { bubbles: true }));
403
  if (bridge) {
404
  bridge.value = saved;
405
  bridge.dispatchEvent(new Event('input', { bubbles: true }));
406
  bridge.dispatchEvent(new Event('change', { bubbles: true }));
407
  }
408
  }
409
- if (capField && !capField.dataset.synced) {
410
- capField.dataset.synced = '1';
411
- capField.addEventListener('input', () => {
412
- localStorage.setItem('travel_capacity', capField.value);
413
  });
414
  }
415
  }
416
- window.addEventListener('DOMContentLoaded', () => setTimeout(restoreCapacity, 500));
417
- const obs = new MutationObserver(() => setTimeout(restoreCapacity, 100));
418
  obs.observe(document.documentElement, { childList: true, subtree: true });
419
  })();
420
  </script>
 
19
  items_data = json.load(f)["items"]
20
 
21
  ITEM_TO_TYPE = {v["name"]: v["type"].lower() for v in items_data.values() if "name" in v and "type" in v}
22
+ ALL_ITEMS = list(ITEM_TO_TYPE.keys())
23
  ALL_CATEGORIES = sorted(set(ITEM_TO_TYPE.values()))
24
  ITEM_FILE_MTIME = os.path.getmtime("items.json")
25
 
 
178
  print(f"❌ Fetch error: {e}")
179
  return {"stocks": {}}, "Fetch failed"
180
 
181
+ def get_live_categories(data):
182
+ """Return sorted list of categories present in YATA data."""
183
+ live_cats = set()
184
+ for _, cdata in data.get("stocks", {}).items():
185
+ for item in cdata.get("stocks", []):
186
+ name = item.get("name")
187
+ cat = ITEM_TO_TYPE.get(name)
188
+ if cat:
189
+ live_cats.add(cat.lower())
190
+ return sorted(live_cats)
191
+
192
  # ---------------- Core logic ----------------
193
  def query_inventory(query_text="", category="", country_name="", capacity=10, refresh=False):
194
  print(f"πŸ”Ž Query received: '{query_text}', category='{category}', country='{country_name}', cap={capacity}")
 
196
  stocks = data.get("stocks", {})
197
  rows = []
198
 
 
199
  parsed_item, parsed_country = parse_freeform_query(query_text)
200
  if not country_name and parsed_country:
201
  country_name = parsed_country
 
204
  semantic_items = semantic_result["items"]
205
  semantic_category = semantic_result["category"]
206
 
 
207
  user_code = normalize_country_query(country_name)
208
 
209
  for code_raw, cdata in stocks.items():
 
220
  update_ts = cdata.get("update")
221
  update_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(update_ts)) if update_ts else "Unknown"
222
 
 
223
  for item in cdata.get("stocks", []):
224
  iname = item.get("name", "")
225
  itype = ITEM_TO_TYPE.get(iname, "").lower()
226
  qty = item.get("quantity", 0)
227
  cost = item.get("cost", 0)
228
 
 
229
  if item_term:
230
  item_ok = (
231
  item_term.lower() in iname.lower()
 
244
  "Category": itype.title() if itype else "",
245
  "Quantity": qty,
246
  "Cost": cost,
 
247
  "Max Capacity Cost": cost * capacity,
248
  "Updated": update_str,
249
  })
 
257
  df[col] = df[col].apply(lambda x: f"{x:,.0f}" if isinstance(x, (int, float)) and x != "" else x)
258
  return df, f"Last update: {last_update}"
259
 
260
+ # ---------------- Wrappers ----------------
261
  def run_query(query_text, category, country, capacity, refresh):
262
+ data, _ = fetch_yata(force_refresh=refresh)
263
+ df, ts = query_inventory(query_text, category, country, capacity, refresh)
264
+ live_categories = get_live_categories(data)
265
+ return df, ts, gr.Dropdown.update(choices=[""] + live_categories)
 
266
 
267
  def run_multi(queries, capacity, refresh):
268
+ data, _ = fetch_yata(force_refresh=refresh)
269
  dfs = []
270
  for qtext in queries:
271
  print(f"⚑ Running convenience subquery: {qtext}")
 
273
  if "Result" not in df.columns:
274
  dfs.append(df)
275
  if not dfs:
276
+ return pd.DataFrame([{"Result": "No results for that set."}]), "No results.", gr.Dropdown.update()
277
+ merged = pd.concat(dfs, ignore_index=True).sort_values(by=["Country", "Item"])
 
278
  for col in ["Quantity", "Cost", "Max Capacity Cost"]:
279
  if col in merged.columns:
280
  merged[col] = merged[col].apply(lambda x: f"{x:,.0f}" if isinstance(x, (int, float)) and x != "" else x)
281
+ live_categories = get_live_categories(data)
282
+ return merged, f"Last update: {time.strftime('%Y-%m-%d %H:%M:%S')}", gr.Dropdown.update(choices=[""] + live_categories)
283
 
284
+ # ---------------- Gradio UI ----------------
285
  with gr.Blocks(title="🧳 Torn Inventory Viewer") as iface:
286
  gr.Markdown("## 🧳 Torn Inventory Viewer")
287
  gr.Markdown("_Search Torn YATA travel stocks with semantic matching._")
288
 
 
289
  with gr.Row():
290
  btn_short = gr.Button("🌸 Flushies (Short Haul)")
291
  btn_med = gr.Button("✈️ Flushies (Medium Haul)")
292
  btn_long = gr.Button("πŸ›« Flushies (Long Haul)")
293
+ btn_xanax = gr.Button("πŸ’Š Xanax")
294
  btn_temps = gr.Button("πŸ’£ Temps")
295
 
296
  query_box = gr.Textbox(label="Search (semantic, e.g. 'flowers in England')", elem_id="qbox")
 
299
  capacity_slider = gr.Number(label="Travel Capacity", value=10, minimum=5, maximum=88, precision=0, elem_id="cap_num")
300
  refresh_check = gr.Checkbox(label="Force refresh (ignore cache)", value=False)
301
 
 
302
  cap_bridge = gr.Textbox(visible=False, elem_id="cap_bridge")
303
 
304
  def apply_bridge(cap_text):
 
318
 
319
  run_btn.click(run_query,
320
  inputs=[query_box, category_drop, country_box, capacity_slider, refresh_check],
321
+ outputs=[result_df, meta_box, category_drop])
322
+
323
+ # Convenience buttons
324
+ btn_short.click(lambda c, r: run_multi(
325
+ ["flowers in mexico","flowers in cayman islands","flowers in canada",
326
+ "plushies in mexico","plushies in cayman islands","plushies in canada"],
327
+ c, r),
328
+ inputs=[capacity_slider, refresh_check], outputs=[result_df, meta_box, category_drop])
329
+
330
+ btn_med.click(lambda c, r: run_multi(
331
+ ["flowers in hawaii","flowers in united kingdom","flowers in argentina",
332
+ "flowers in switzerland","flowers in japan",
333
+ "plushies in hawaii","plushies in united kingdom","plushies in argentina",
334
+ "plushies in switzerland","plushies in japan"],
335
+ c, r),
336
+ inputs=[capacity_slider, refresh_check], outputs=[result_df, meta_box, category_drop])
337
+
338
+ btn_long.click(lambda c, r: run_multi(
339
+ ["flowers in uae","flowers in china","flowers in south africa",
340
+ "plushies in uae","plushies in china","plushies in south africa"],
341
+ c, r),
342
+ inputs=[capacity_slider, refresh_check], outputs=[result_df, meta_box, category_drop])
343
+
344
+ btn_xanax.click(lambda c, r: run_multi(["xanax in south africa"], c, r),
345
+ inputs=[capacity_slider, refresh_check], outputs=[result_df, meta_box, category_drop])
346
+
347
+ btn_temps.click(lambda c, r: run_multi(
348
+ ["tear gas in argentina","smoke grenade in south africa","flash grenade in switzerland"],
349
+ c, r),
350
+ inputs=[capacity_slider, refresh_check], outputs=[result_df, meta_box, category_drop])
351
+
352
+ # --- JS for capacity persistence ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
  gr.HTML("""
354
  <script>
355
  (function () {
 
358
  return app && app.shadowRoot ? app.shadowRoot : document;
359
  }
360
  function el(sel) { return root().querySelector(sel); }
361
+ function restore() {
 
362
  const saved = localStorage.getItem('travel_capacity');
363
+ const num = el('#cap_num input[type=number]');
364
  const bridge = el('#cap_bridge textarea');
365
+ if (saved && num) {
366
+ num.value = saved;
367
+ num.dispatchEvent(new Event('input', { bubbles: true }));
368
  if (bridge) {
369
  bridge.value = saved;
370
  bridge.dispatchEvent(new Event('input', { bubbles: true }));
371
  bridge.dispatchEvent(new Event('change', { bubbles: true }));
372
  }
373
  }
374
+ if (num && !num.dataset.synced) {
375
+ num.dataset.synced = '1';
376
+ num.addEventListener('input', () => {
377
+ localStorage.setItem('travel_capacity', num.value);
378
  });
379
  }
380
  }
381
+ window.addEventListener('DOMContentLoaded', () => setTimeout(restore, 500));
382
+ const obs = new MutationObserver(() => setTimeout(restore, 100));
383
  obs.observe(document.documentElement, { childList: true, subtree: true });
384
  })();
385
  </script>