""" page3_simulation.py — Live Simulation with Full Canvas Animation All bills complete in exactly 30 seconds. WCO themed, 5-lane layout. """ import streamlit as st import streamlit.components.v1 as components import plotly.graph_objects as go import pandas as pd from styles import (inject_global_css, page_header, metric_row, WCO_GOLD, WCO_BLUE, WCO_GREEN, WCO_RED, WCO_CARD_BG, WCO_BORDER, WCO_MUTED) from simulation_engine import (generate_declarations, compute_risk_scores, assign_channels, simulate_inspection_outcomes, compute_updated_weights, get_default_weights, compute_efficiency_metrics, RISK_AREAS) # ───────────────────────────────────────────────────────────────────────────── def build_animation_html(n_bills, bandwidth, exp_ratio, channel_counts, detected, revenue, efficiency_idx): red_n = channel_counts.get("RED", 0) yellow_n = channel_counts.get("YELLOW", 0) green_n = channel_counts.get("GREEN", 0) explore_n = int(n_bills * bandwidth * exp_ratio) return f"""
{n_bills}
Total Declarations
{red_n}
🔴 RED Target
{yellow_n}
🟡 YELLOW Target
{green_n}
🟢 GREEN Target
{efficiency_idx:.3f}
Efficiency Index
${revenue:,.0f}
Revenue Recovered
{detected}
Frauds Detected
0
📦 Processed
0
🔴 Physical Exam
0
🟡 Doc Check
0
🟢 Facilitated
0
🔍 gATE Explore
0
🚨 Fraud Detected
Initialising... 0%
""" # ───────────────────────────────────────────────────────────────────────────── def risk_score_distribution(df): fig = go.Figure() for ch, color, label in [ ("GREEN","#00843D","🟢 GREEN"), ("YELLOW","#F5A800","🟡 YELLOW"), ("RED","#C8102E","🔴 RED"), ]: cdf = df[df["channel"]==ch] fig.add_trace(go.Violin( y=cdf["fraud_score"], name=label, fillcolor=color, opacity=0.7, line_color=color, box_visible=True, meanline_visible=True, points=False, )) fig.update_layout( paper_bgcolor="#070E1C", plot_bgcolor="#0B1220", font=dict(family="IBM Plex Sans", color="#D0DCF0", size=12), height=300, title=dict(text="Risk Score Distribution by Channel", font=dict(color=WCO_GOLD, size=13, family="Playfair Display"), x=0.5), yaxis=dict(title="Fraud Score", gridcolor="#1E3A6E", range=[0,1]), xaxis=dict(gridcolor="#1E3A6E"), legend=dict(bgcolor="#0F1C35", bordercolor=WCO_BORDER), margin=dict(l=50,r=20,t=50,b=40), violingap=0.3, ) return fig def area_heatmap(df): pivot = pd.crosstab(df["risk_area"], df["channel"]) for col in ["RED","YELLOW","GREEN"]: if col not in pivot.columns: pivot[col]=0 pivot = pivot[["RED","YELLOW","GREEN"]] fig = go.Figure(go.Heatmap( z=pivot.values, x=pivot.columns, y=pivot.index, colorscale=[[0,"#0B1220"],[0.5,"#003087"],[1,"#C8A951"]], text=pivot.values, texttemplate="%{text}", textfont={"size":13}, hovertemplate="%{y}
Channel: %{x}
Bills: %{z}", showscale=True, colorbar=dict(tickfont=dict(color="#D0DCF0"), title="Bills", titlefont=dict(color=WCO_GOLD)), )) fig.update_layout( paper_bgcolor="#070E1C", plot_bgcolor="#0B1220", font=dict(family="IBM Plex Sans", color="#D0DCF0", size=11), height=280, title=dict(text="Risk Area × Channel Heatmap", font=dict(color=WCO_GOLD, size=13, family="Playfair Display"), x=0.5), margin=dict(l=165,r=30,t=50,b=40), ) return fig # ───────────────────────────────────────────────────────────────────────────── def show(): inject_global_css() page_header("🔄", "Self-Learning Simulation Engine", "DATE EXPLOITATION · gATE EXPLORATION · 30-SECOND LIVE CANVAS ANIMATION") # ── Controls ────────────────────────────────────────────────── st.markdown('
🎛️ Simulation Control Panel
', unsafe_allow_html=True) c1,c2,c3,c4 = st.columns(4) with c1: n_bills = st.slider("📦 Declarations", 200,1000,1000,step=100) with c2: bandwidth = st.slider("📡 Bandwidth (%)", 5,30,10,step=1)/100 with c3: exp_ratio = st.slider("🔍 Exploration ε (%)",1,30,10,step=1)/100 with c4: seed = st.slider("🎲 Seed",1,99,42,step=1) st.markdown(f"""
Strategy: DATE {100-int(exp_ratio*100)}% + gATE {int(exp_ratio*100)}%  |  Bandwidth: {int(bandwidth*100)}% → {int(n_bills*bandwidth)} bills inspected  |  Facilitated: {n_bills-int(n_bills*bandwidth)} bills (GREEN)  |  ⏱ Animation duration: exactly 30 seconds for full batch
""", unsafe_allow_html=True) col1,col2,_ = st.columns([1.2,1,4]) with col1: run_btn = st.button("▶ Run Simulation", type="primary", use_container_width=True) with col2: reset_btn = st.button("🔄 Reset All", use_container_width=True) if reset_btn: for k in ["sim_df","sim_weights","sim_efficiency"]: st.session_state.pop(k,None) st.rerun() # ── Run ─────────────────────────────────────────────────────── if run_btn or "sim_df" in st.session_state: if run_btn or "sim_df" not in st.session_state: weights = st.session_state.get("rule_weights", get_default_weights()) prog = st.progress(0, text="⚙️ Generating declarations...") df = generate_declarations(n_bills, seed=seed) prog.progress(25, text="🧠 DATE scoring...") df = compute_risk_scores(df, weights) prog.progress(50, text="⚖️ Hybrid channel routing...") df = assign_channels(df, bandwidth=bandwidth, exploration_ratio=exp_ratio) prog.progress(75, text="👮 Inspection outcomes...") df = simulate_inspection_outcomes(df, seed=seed) prog.progress(90, text="📈 Weight update & efficiency...") uw = compute_updated_weights(df, weights) eff = compute_efficiency_metrics(df) prog.progress(100, text="✅ Launching animation!") st.session_state.sim_df = df st.session_state.sim_weights = uw st.session_state.sim_efficiency = eff df = st.session_state.sim_df eff = st.session_state.sim_efficiency hybrid = eff.get("hybrid", {}) ch = df["channel"].value_counts() detected = int((df["inspection_outcome"]=="FRAUD_DETECTED").sum()) revenue = float(df["detected_revenue"].sum()) eff_idx = float(hybrid.get("efficiency_index", 0.0)) channel_counts = { "RED": int(ch.get("RED",0)), "YELLOW": int(ch.get("YELLOW",0)), "GREEN": int(ch.get("GREEN",0)), } # ── Legend ──────────────────────────────────────────────── st.markdown('
🎬 Live RMS Canvas Animation
', unsafe_allow_html=True) st.markdown("""
📌 30-second real-time simulation.   Each icon = one customs declaration flowing through the RMS pipeline.
■ Red glow = Fraud detected  |  ■ Purple glow = gATE exploration pick  |  ■ Blue = Standard routing  |  Fraud bills arc → Offence DB vault (right lane)
""", unsafe_allow_html=True) # ── CANVAS ANIMATION ────────────────────────────────────── html_src = build_animation_html( n_bills=n_bills, bandwidth=bandwidth, exp_ratio=exp_ratio, channel_counts=channel_counts, detected=detected, revenue=revenue, efficiency_idx=eff_idx, ) components.html(html_src, height=720, scrolling=False) # ── Supporting charts ────────────────────────────────────── st.markdown("
", unsafe_allow_html=True) st.markdown('
📊 Channel Analytics
', unsafe_allow_html=True) ca,cb = st.columns([3,2]) with ca: st.plotly_chart(area_heatmap(df), use_container_width=True) with cb: st.plotly_chart(risk_score_distribution(df), use_container_width=True) # ── Risk area cards ─────────────────────────────────────── st.markdown('
🗂️ Risk Area Breakdown
', unsafe_allow_html=True) acols = st.columns(5) for i,(aname,acfg) in enumerate(RISK_AREAS.items()): a_df = df[df["risk_area"]==aname] color = acfg["color"] ch_a = a_df["channel"].value_counts() fraud_a= (a_df["inspection_outcome"]=="FRAUD_DETECTED").sum() exp_a = a_df["is_exploration"].sum() with acols[i]: st.markdown(f"""
{acfg['icon']} {aname}
🔴 RED: {ch_a.get('RED',0)}
🟡 YEL: {ch_a.get('YELLOW',0)}
🟢 GRN: {ch_a.get('GREEN',0)}
🚨 Fraud: {fraud_a}
🔍 Expl: {exp_a}
""", unsafe_allow_html=True) st.markdown("""
✅ Simulation complete. Go to Page 4 for full tables, offence DB growth, and weight-evolution analysis.
""", unsafe_allow_html=True) else: st.markdown("""
🎬
Ready to Simulate
Configure parameters above and click ▶ Run Simulation
A 30-second canvas animation will show all declarations flowing through RED / YELLOW / GREEN channels in real-time.
""", unsafe_allow_html=True)