A live, filterable view of the model's actual scoring table — not a mock-up. Search it, sort it, filter by warehouse or category, and click any row to run the reorder calculator. Every number on this page is computed from the pipeline's real output file.
This is powerbi/model_output.csv, loaded in full. Click a row to open the reorder calculator for that SKU.
| SKU ↕ | Product | Warehouse | Category | Days of Supply ↕ | Lead Time | Risk Score ↕ | Tier | Action |
|---|
Three views into the same table above: risk concentration, category exposure, and what the model actually weights.
Monthly average actual lead time per supplier, from real purchase-order delivery dates.
Selected over random forest on ROC-AUC, with a threshold deliberately tuned for recall over precision.
Why recall over precision: missing a real stockout costs more than reviewing a SKU that turns out fine. The threshold was tuned to guarantee recall ≥ 0.75 on the positive class, then maximize precision within that constraint — not picked for the best-looking single accuracy number.
Train/test split is time-based, not random — the model is evaluated on dates it has never seen, because SKU/warehouse series are correlated across time and a random split would leak future information into training.
Each stage hands the next a file it actually consumes — this can be re-run end to end.
Seeded daily simulation — demand, seasonality, lead times, a real disruption event.
Relational warehouse — products, suppliers, warehouses, sales, snapshots, POs.
8 business-logic queries — days-of-supply, anomaly detection, ABC analysis.
Rolling 7/30-day features + forward-looking stockout label.
Trains, evaluates, scores the latest snapshot → model_output.csv.
Weekly action dashboard — this table is the dashboard's main visual.
Real excerpts — one per layer of the stack.
WITH last_date AS (SELECT MAX(date) AS d FROM inventory_snapshots), recent_sales AS ( SELECT sku, warehouse_id, AVG(units_sold) AS avg_daily_sales_30d FROM daily_sales WHERE date > (SELECT date(d, '-30 days') FROM last_date) GROUP BY sku, warehouse_id ) SELECT c.sku, c.warehouse_id, ROUND(c.on_hand_qty / NULLIF(r.avg_daily_sales_30d, 0), 1) AS days_of_supply, s.base_lead_time_days FROM current_stock c JOIN recent_sales r ON r.sku = c.sku AND r.warehouse_id = c.warehouse_id WHERE c.on_hand_qty / NULLIF(r.avg_daily_sales_30d, 0) < s.base_lead_time_days ORDER BY days_of_supply ASC; -- Finding: any row here is at risk — stock will likely run out before -- a reorder placed today would even arrive.
WITH monthly AS ( SELECT po.supplier_id, strftime('%Y-%m', po.order_date) AS order_month, AVG(julianday(po.actual_delivery_date) - julianday(po.order_date)) AS avg_lead_time, s.base_lead_time_days FROM purchase_orders po JOIN suppliers s ON s.supplier_id = po.supplier_id GROUP BY po.supplier_id, order_month ) SELECT supplier_id, order_month, ROUND(avg_lead_time / base_lead_time_days, 2) AS lead_time_ratio FROM monthly WHERE avg_lead_time > base_lead_time_days * 1.8 -- flag: 80%+ above baseline ORDER BY supplier_id, order_month; -- Finding: exactly 3 suppliers flagged, exactly during a Jul-Sep window.
# time-based split — SKU/warehouse series are correlated across time, # a random split would leak the future into training cutoff = df["date"].quantile(0.75) train_mask = df["date"] <= cutoff model = LogisticRegression(max_iter=1000, class_weight="balanced") model.fit(X_train_s, y_train) proba = model.predict_proba(X_test_s)[:, 1] # pick the lowest threshold that still guarantees recall >= 0.75, # then take whatever precision comes with it thresholds = np.linspace(0.05, 0.95, 19) best = max( thresholds, key=lambda t: (recall_score(y_test, proba >= t) >= 0.75, precision_score(y_test, proba >= t, zero_division=0)) )
Estimated Exposure ($) = SUMX( FILTER(model_output, model_output[risk_tier] = "High"), model_output[unit_cost] * model_output[avg_sales_7d] * model_output[base_lead_time_days] ) // estimates the $ value of demand that could go unfulfilled if a // High-risk SKU stocks out for its full lead-time window Risk Color = SWITCH( TRUE(), model_output[risk_tier] = "High", "#D9534F", model_output[risk_tier] = "Medium", "#F0AD4E", "#5CB85C" )