focustiki commited on
Commit
e2c2f42
·
1 Parent(s): 957c664

Feat: Attach weather (type, temp_c) at save using Open-Meteo for Laurier; cache for 10m; soften fallback message to non-error phrasing; update both RPC and fallback paths without UI jitter.

Browse files
Files changed (1) hide show
  1. streamlit_app.py +85 -2
streamlit_app.py CHANGED
@@ -440,6 +440,60 @@ def main():
440
 
441
  ensure_ai_detection_defaults()
442
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
  # Modern sign-out button
444
  col1, col2, col3 = st.columns([1, 1, 1])
445
  with col2:
@@ -700,6 +754,7 @@ def main():
700
  st.markdown(ModernUIComponents.create_status_message("Saving item...", "loading"), unsafe_allow_html=True)
701
  ts_iso = datetime.utcnow().isoformat()
702
  ingest_id = deterministic_ingest_id(int(v["id"]), user_email, name_clean, int(quantity), ts_iso)
 
703
 
704
  try:
705
  ok, msg = try_rpc_ingest(
@@ -711,16 +766,35 @@ def main():
711
  if ok:
712
  save_status.empty()
713
  st.markdown(ModernUIComponents.create_status_message("Item logged successfully!", "success"), unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
714
  log_event("item_logged", user_email, {
715
  "visit_id": v["id"],
716
  "item_name": name_clean,
717
  "quantity": quantity
718
  })
719
  else:
720
- st.markdown(ModernUIComponents.create_status_message(f"{msg}. Trying fallback method...", "warning"), unsafe_allow_html=True)
721
  fallback_direct_insert(user_email, int(v["id"]), name_clean, int(quantity),
722
  clean_text(category,80), clean_text(unit,40),
723
- clean_text(barcode,64), ts_iso, ingest_id)
 
 
 
 
 
 
 
 
 
 
724
  save_status.empty()
725
  st.markdown(ModernUIComponents.create_status_message("Item logged successfully (fallback method)!", "success"), unsafe_allow_html=True)
726
  log_event("item_logged_fallback", user_email, {
@@ -733,6 +807,15 @@ def main():
733
  fallback_direct_insert(user_email, int(v["id"]), name_clean, int(quantity),
734
  clean_text(category,80), clean_text(unit,40),
735
  clean_text(barcode,64), ts_iso, ingest_id)
 
 
 
 
 
 
 
 
 
736
  save_status.empty()
737
  st.markdown(ModernUIComponents.create_status_message("Item logged successfully (fallback method)!", "success"), unsafe_allow_html=True)
738
  log_event("item_logged_fallback", user_email, {
 
440
 
441
  ensure_ai_detection_defaults()
442
 
443
+ # ---------------- Weather (Laurier Waterloo campus) ----------------
444
+ if "_weather_cache" not in st.session_state:
445
+ st.session_state["_weather_cache"] = {"at": None, "type": None, "temp_c": None}
446
+
447
+ def _map_weather_code(code: int) -> str:
448
+ try:
449
+ c = int(code)
450
+ except Exception:
451
+ return None
452
+ # Open-Meteo weather codes grouped into simple buckets
453
+ if c == 0:
454
+ return "clear"
455
+ if c in {1, 2, 3}:
456
+ return "cloudy"
457
+ if c in {45, 48}:
458
+ return "fog"
459
+ if c in {51, 53, 55, 56, 57, 61, 63, 65, 66, 67, 80, 81, 82}:
460
+ return "rain"
461
+ if c in {71, 73, 75, 77, 85, 86}:
462
+ return "snow"
463
+ if c in {95, 96, 99}:
464
+ return "thunderstorm"
465
+ return "unknown"
466
+
467
+ def fetch_weather_at_laurier() -> tuple[Optional[str], Optional[float]]:
468
+ """Fetch current weather near Wilfrid Laurier University, Waterloo (lat 43.4753, lon -80.5273)."""
469
+ try:
470
+ lat, lon = 43.4753, -80.5273
471
+ url = (
472
+ f"https://api.open-meteo.com/v1/forecast" \
473
+ f"?latitude={lat}&longitude={lon}&current=temperature_2m,weather_code&timezone=auto"
474
+ )
475
+ r = requests.get(url, timeout=6)
476
+ if r.status_code != 200:
477
+ return None, None
478
+ data = r.json() or {}
479
+ cur = data.get("current") or {}
480
+ temp_c = cur.get("temperature_2m")
481
+ code = cur.get("weather_code")
482
+ return _map_weather_code(code), float(temp_c) if temp_c is not None else None
483
+ except Exception:
484
+ return None, None
485
+
486
+ def get_cached_weather() -> tuple[Optional[str], Optional[float]]:
487
+ now = datetime.utcnow()
488
+ cache = st.session_state.get("_weather_cache") or {}
489
+ ts = cache.get("at")
490
+ # refresh every 10 minutes
491
+ if ts and isinstance(ts, datetime) and (now - ts).total_seconds() < 600:
492
+ return cache.get("type"), cache.get("temp_c")
493
+ wtype, temp_c = fetch_weather_at_laurier()
494
+ st.session_state["_weather_cache"] = {"at": now, "type": wtype, "temp_c": temp_c}
495
+ return wtype, temp_c
496
+
497
  # Modern sign-out button
498
  col1, col2, col3 = st.columns([1, 1, 1])
499
  with col2:
 
754
  st.markdown(ModernUIComponents.create_status_message("Saving item...", "loading"), unsafe_allow_html=True)
755
  ts_iso = datetime.utcnow().isoformat()
756
  ingest_id = deterministic_ingest_id(int(v["id"]), user_email, name_clean, int(quantity), ts_iso)
757
+ weather_type, temp_c = get_cached_weather()
758
 
759
  try:
760
  ok, msg = try_rpc_ingest(
 
766
  if ok:
767
  save_status.empty()
768
  st.markdown(ModernUIComponents.create_status_message("Item logged successfully!", "success"), unsafe_allow_html=True)
769
+ # Best-effort: attach weather on the row created by RPC using ingest_id
770
+ try:
771
+ if weather_type is not None or temp_c is not None:
772
+ sb.table("visit_items_p").update({
773
+ "weather_type": weather_type,
774
+ "temp_c": temp_c
775
+ }).eq("ingest_id", ingest_id).execute()
776
+ except Exception:
777
+ pass
778
  log_event("item_logged", user_email, {
779
  "visit_id": v["id"],
780
  "item_name": name_clean,
781
  "quantity": quantity
782
  })
783
  else:
784
+ st.markdown(ModernUIComponents.create_status_message("Using reliable save path...", "info"), unsafe_allow_html=True)
785
  fallback_direct_insert(user_email, int(v["id"]), name_clean, int(quantity),
786
  clean_text(category,80), clean_text(unit,40),
787
+ clean_text(barcode,64), ts_iso, ingest_id,
788
+ )
789
+ # After fallback insert, attach weather fields as part of payload
790
+ try:
791
+ if weather_type is not None or temp_c is not None:
792
+ sb.table("visit_items_p").update({
793
+ "weather_type": weather_type,
794
+ "temp_c": temp_c
795
+ }).eq("ingest_id", ingest_id).execute()
796
+ except Exception:
797
+ pass
798
  save_status.empty()
799
  st.markdown(ModernUIComponents.create_status_message("Item logged successfully (fallback method)!", "success"), unsafe_allow_html=True)
800
  log_event("item_logged_fallback", user_email, {
 
807
  fallback_direct_insert(user_email, int(v["id"]), name_clean, int(quantity),
808
  clean_text(category,80), clean_text(unit,40),
809
  clean_text(barcode,64), ts_iso, ingest_id)
810
+ # Attach weather after direct insert
811
+ try:
812
+ if weather_type is not None or temp_c is not None:
813
+ sb.table("visit_items_p").update({
814
+ "weather_type": weather_type,
815
+ "temp_c": temp_c
816
+ }).eq("ingest_id", ingest_id).execute()
817
+ except Exception:
818
+ pass
819
  save_status.empty()
820
  st.markdown(ModernUIComponents.create_status_message("Item logged successfully (fallback method)!", "success"), unsafe_allow_html=True)
821
  log_event("item_logged_fallback", user_email, {