# phase\Student_view\games\MoneyMatch.py import time import os import random from pathlib import Path import streamlit as st from utils import db as dbapi import time from utils import db as db_util from utils import api USE_LOCAL_DB = os.getenv("DISABLE_DB", "1") != "1" # ---------- paths ---------- PROJECT_ROOT = Path(__file__).resolve().parents[3] def _asset(*parts: str) -> str: # JMD image path return str((PROJECT_ROOT / "assets" / "images" / Path(*parts)).resolve()) def _safe_image(path: str, *, caption: str = ""): if not os.path.exists(path): st.warning(f"Image not found: {Path(path).name}. Button still works.") return False st.image(path, use_container_width=True, caption=caption) return True # ---------- state helpers ---------- def _init_state(): ss = st.session_state if "mm_level" not in ss: ss.mm_level = 1 if "mm_xp" not in ss: ss.mm_xp = 0 if "mm_matches" not in ss: ss.mm_matches = 0 if "mm_target" not in ss: ss.mm_target = random.randint(7, 10000) # randon goal generator if "mm_selected" not in ss: ss.mm_selected = [] if "mm_total" not in ss: ss.mm_total = 0 if "mm_start_ts" not in ss: ss.mm_start_ts = time.perf_counter() if "mm_saved" not in ss: ss.mm_saved = False def _reset_round(new_target: int | None = None): ss = st.session_state ss.mm_selected = [] ss.mm_total = 0 ss.mm_target = new_target if new_target is not None else random.randint(7, 10000) ss.mm_start_ts = time.perf_counter() ss.mm_saved = False def _award_xp(gained: int): ss = st.session_state ss.mm_xp += gained ss.mm_matches += 1 while ss.mm_xp >= ss.mm_level * 100: ss.mm_level += 1 def _persist_success(gained_xp: int): user = st.session_state.get("user") or {} user_id = int(user.get("user_id", 0)) if not user_id: st.error("Not saving. No logged-in user_id in session.") return payload = dict( user_id=user_id, target=int(st.session_state.mm_target), total=int(st.session_state.mm_total), elapsed_ms=int((time.perf_counter() - st.session_state.mm_start_ts) * 1000), matched=True, gained_xp=int(gained_xp), ) try: if USE_LOCAL_DB and hasattr(dbapi, "record_money_match_play"): # direct DB mode dbapi.record_money_match_play(**payload) st.toast(f"Saved to DB +{gained_xp} XP") else: # backend mode (DISABLE_DB=1) api.record_money_match_play(**payload) st.toast(f"Saved via backend +{gained_xp} XP") st.session_state.mm_saved = True except Exception as e: st.error(f"Save failed: {e}") # --- CSS injection (run every render) --- def _inject_css(): css_path = PROJECT_ROOT / "assets" / "styles.css" try: css = css_path.read_text(encoding="utf-8") st.markdown(f"", unsafe_allow_html=True) except Exception: # don't crash the page because of styling pass # ---------- denominations ---------- DENOMS = [ ("JA$1", 1, _asset("jmd", "jmd_1.jpeg")), ("JA$5", 5, _asset("jmd", "jmd_5.jpeg")), ("JA$10", 10, _asset("jmd", "jmd_10.jpeg")), ("JA$20", 20, _asset("jmd", "jmd_20.jpeg")), ("JA$50", 50, _asset("jmd", "jmd_50.jpg")), ("JA$100", 100, _asset("jmd", "jmd_100.jpg")), ("JA$500", 500, _asset("jmd", "jmd_500.jpg")), ("JA$1000", 1000, _asset("jmd", "jmd_1000.jpeg")), ("JA$2000", 2000, _asset("jmd", "jmd_2000.jpeg")), ("JA$5000", 5000, _asset("jmd", "jmd_5000.jpeg")), ] # ---------- main ---------- def show_page(): _init_state() _inject_css() # <- keep this here so it runs on every rerun ss = st.session_state if st.button("← Back to Games"): ss.current_game = None st.rerun() st.title("Money Match Challenge") left, right = st.columns([1.75, 1]) with left: st.markdown('