dabur-pricing-app / Genesis_AI_File_Trigger_Map.html
Bera
initial deploy
14356bb
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Genesis AI β€” File Trigger Map</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Segoe UI',Arial,sans-serif;background:#f0f4f1;color:#1a1a2e;padding:32px}
h1{font-size:22px;font-weight:700;color:#1a6b2f;border-bottom:3px solid #1a6b2f;padding-bottom:10px;margin-bottom:4px}
.sub{font-size:12px;color:#888;margin-bottom:28px}
.card{background:#fff;border-radius:14px;box-shadow:0 2px 16px rgba(0,0,0,.08);
padding:0;margin-bottom:28px;overflow:hidden}
.card-hdr{padding:16px 24px;display:flex;align-items:center;gap:10px;border-bottom:2px solid #eef3ee}
.card-hdr-title{font-size:13px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:#1a6b2f}
.card-hdr-desc{font-size:11px;color:#888;margin-left:auto}
/* step number */
.step-num{background:#1a6b2f;color:#fff;font-size:12px;font-weight:700;
width:28px;height:28px;border-radius:50%;display:flex;align-items:center;
justify-content:center;flex-shrink:0}
/* file row */
.file-row{display:grid;grid-template-columns:28px 1fr 1fr auto;
align-items:start;gap:0;border-bottom:1px solid #f0f4f0;padding:14px 24px}
.file-row:last-child{border-bottom:none}
.file-row:hover{background:#fafcfa}
.file-path{font-family:Consolas,monospace;font-size:11px;color:#1a6b2f;font-weight:700;line-height:1.5}
.file-base{font-size:9.5px;color:#999;margin-top:1px}
.fn-list{font-size:11px;line-height:1.8;color:#333}
.fn-list code{font-family:Consolas,monospace;font-size:10.5px;background:#eef4ee;
padding:1px 5px;border-radius:3px;color:#1b5e20}
.what{font-size:11px;color:#555;line-height:1.6;padding-left:12px;border-left:2px solid #e0ece0}
/* badge */
.badge{display:inline-block;font-size:9.5px;font-weight:700;padding:2px 8px;
border-radius:10px;white-space:nowrap;margin-left:8px}
.fe {background:#e3f2fd;color:#1565c0}
.be {background:#fff3e0;color:#c84b00}
.py {background:#e8f5e9;color:#1b5e20}
.cfg{background:#fce4ec;color:#880e4f}
.out{background:#f3e5f5;color:#6a1b9a}
/* arrow connector */
.arrow-row{text-align:center;padding:4px 0;font-size:20px;color:#1a6b2f;
background:#f5faf5;border-bottom:1px solid #eef3ee}
/* output file list */
.out-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));
gap:8px;padding:16px 24px;background:#fafcfa;border-top:1px solid #eef3ee}
.out-file{font-family:Consolas,monospace;font-size:10.5px;background:#f3e5f5;
color:#6a1b9a;padding:5px 10px;border-radius:6px;border:1px solid #e1bee7}
/* col headers */
.col-hdr{display:grid;grid-template-columns:28px 1fr 1fr auto;gap:0;
padding:8px 24px;background:#f5faf5;border-bottom:1px solid #eef3ee}
.col-hdr span{font-size:9px;font-weight:700;letter-spacing:.5px;
text-transform:uppercase;color:#999}
/* path highlight */
.dir{color:#888}
.fname{color:#1a6b2f;font-weight:700}
</style>
</head>
<body>
<h1>Genesis AI β€” Complete File Trigger Map</h1>
<p class="sub">Every file triggered from the moment the Excel is uploaded to the moment the dashboard renders. In exact execution order.</p>
<!-- ══════════════════════════════════════════════════════════ -->
<!-- PHASE 1 β€” FRONTEND (UPLOAD) -->
<!-- ══════════════════════════════════════════════════════════ -->
<div class="card">
<div class="card-hdr">
<div class="step-num">1</div>
<div class="card-hdr-title">Phase 1 β€” Frontend: User Uploads File <span class="badge fe">React</span></div>
<div class="card-hdr-desc">Browser β†’ UploadModal β†’ POST /api/upload</div>
</div>
<div class="col-hdr">
<span></span><span>File Path</span><span>Functions Called</span><span style="padding-left:12px">What it does</span>
</div>
<div class="file-row">
<div class="step-num" style="background:#1565c0">1</div>
<div>
<div class="file-path"><span class="dir">react-dashboard/src/components/</span><span class="fname">Header.jsx</span></div>
<div class="file-base"><span class="badge fe">FE</span></div>
</div>
<div class="fn-list">
<code>Header()</code><br>
<code>setUploadOpen(true)</code>
</div>
<div class="what">User clicks Upload button β†’ sets <code>uploadOpen=true</code> β†’ UploadModal mounts</div>
</div>
<div class="file-row">
<div class="step-num" style="background:#1565c0">2</div>
<div>
<div class="file-path"><span class="dir">react-dashboard/src/components/</span><span class="fname">UploadModal.jsx</span></div>
<div class="file-base"><span class="badge fe">FE</span></div>
</div>
<div class="fn-list">
<code>UploadModal()</code><br>
<code>POST /api/upload/reset</code><br>
<code>onDrop(e)</code> or browse<br>
<code>handleFile(file)</code>
</div>
<div class="what">
On open β†’ POST <code>/api/upload/reset</code> (clears previous job)<br>
User picks file β†’ <code>handleFile()</code> runs:<br>
β‘  Validates extension (<code>.xlsx/.xlsm/.xls</code>)<br>
β‘‘ Builds <code>FormData({ file })</code><br>
β‘’ <code>POST /api/upload</code><br>
β‘£ Starts <code>startPolling()</code> β€” polls every 1.5 s
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════ -->
<!-- PHASE 2 β€” BACKEND (RECEIVE + SPAWN) -->
<!-- ══════════════════════════════════════════════════════════ -->
<div class="card">
<div class="card-hdr">
<div class="step-num">2</div>
<div class="card-hdr-title">Phase 2 β€” Backend: Receive File &amp; Start Pipeline <span class="badge be">FastAPI</span></div>
<div class="card-hdr-desc">upload_server.py handles the POST, saves file, spawns thread</div>
</div>
<div class="col-hdr">
<span></span><span>File Path</span><span>Functions Called</span><span style="padding-left:12px">What it does</span>
</div>
<div class="file-row">
<div class="step-num" style="background:#c84b00">3</div>
<div>
<div class="file-path"><span class="dir">Modular app/</span><span class="fname">upload_server.py</span></div>
<div class="file-base"><span class="badge be">BE</span> FastAPI server</div>
</div>
<div class="fn-list">
<code>upload_excel()</code><br>
<code>POST /api/upload</code><br>
<code>_find_pipeline_python()</code><br>
<code>Thread(_run_pipeline).start()</code>
</div>
<div class="what">
β‘  Validates extension (server-side guard)<br>
β‘‘ Checks no job already running (HTTP 409 if busy)<br>
β‘’ Saves xlsx β†’ <code>_uploads/filename.xlsx</code><br>
β‘£ <code>_find_pipeline_python()</code> β€” finds Python with pandas<br>
β‘€ Spawns background thread β†’ <code>_run_pipeline(xlsx_path)</code><br>
β‘₯ Returns HTTP 200 immediately
</div>
</div>
<div class="file-row">
<div class="step-num" style="background:#c84b00">4</div>
<div>
<div class="file-path"><span class="dir">Modular app/</span><span class="fname">upload_server.py</span></div>
<div class="file-base"><span class="badge be">BE</span> background thread</div>
</div>
<div class="fn-list">
<code>_run_pipeline(xlsx_path)</code><br>
<code>_reset_steps()</code><br>
<code>subprocess.Popen([python,</code><br>
<code>&nbsp;&nbsp;"-m", "pipeline.run", ...])</code><br>
<code>_annotate(line)</code> per line
</div>
<div class="what">
Streams subprocess stdout live to terminal<br>
<code>_annotate()</code> prints step banners at key lines<br>
After completion β†’ copies <code>output/*.json</code> β†’ <code>react-dashboard/public/data/</code><br>
Sets <code>_job["status"] = "done"</code>
</div>
</div>
</div>
<div class="arrow-row">β–Ό subprocess.Popen launches Python pipeline</div>
<!-- ══════════════════════════════════════════════════════════ -->
<!-- PHASE 3 β€” PYTHON PIPELINE -->
<!-- ══════════════════════════════════════════════════════════ -->
<div class="card">
<div class="card-hdr">
<div class="step-num">3</div>
<div class="card-hdr-title">Phase 3 β€” Python Pipeline Execution <span class="badge py">Python</span></div>
<div class="card-hdr-desc">python -m pipeline.run --input file.xlsx --output output/</div>
</div>
<div class="col-hdr">
<span></span><span>File Path</span><span>Functions Called</span><span style="padding-left:12px">What it does</span>
</div>
<!-- run.py -->
<div class="file-row">
<div class="step-num">5</div>
<div>
<div class="file-path"><span class="dir">Modular app/pipeline/</span><span class="fname">run.py</span></div>
<div class="file-base"><span class="badge py">PY</span> Entry point β€” <code>python -m pipeline.run</code></div>
</div>
<div class="fn-list">
<code>main()</code>
</div>
<div class="what">
Parses CLI args (<code>--input</code>, <code>--output</code>)<br>
Orchestrates all pipeline steps in sequence<br>
Calls every function below and writes all JSON outputs
</div>
</div>
<!-- config.py -->
<div class="file-row">
<div class="step-num">6</div>
<div>
<div class="file-path"><span class="dir">Modular app/pipeline/</span><span class="fname">config.py</span></div>
<div class="file-base"><span class="badge cfg">CFG</span> imported by run.py, modelling.py, proxies.py, recommendations.py</div>
</div>
<div class="fn-list">
<code>DEFAULT_PACK_ORDER</code><br>
<code>GUARDRAIL_MIN_OBS = 6</code><br>
<code>MAX_COMP_COMBO_SIZE = 3</code><br>
<code>ELASTICITY_ABS_CAP = 6.0</code><br>
<code>SEASONAL_R2_THRESHOLD = 0.05</code><br>
<code>ANCHOR_DOMINANCE_RATIO = 1.20</code><br>
<code>REC_DELTA_PCT = 5.0</code>
</div>
<div class="what">
Single source of truth for all thresholds.<br>
Imported (not called) β€” all constants read at import time.<br>
No magic numbers anywhere else in the pipeline.
</div>
</div>
<!-- loader.py -->
<div class="file-row">
<div class="step-num">7</div>
<div>
<div class="file-path"><span class="dir">Modular app/pipeline/</span><span class="fname">loader.py</span></div>
<div class="file-base"><span class="badge py">PY</span> Step 0 β€” Data loading</div>
</div>
<div class="fn-list">
<code>load_source_data(path)</code><br>
<code>read_brand_config(path)</code>
</div>
<div class="what">
<code>load_source_data()</code>:<br>
&nbsp;β†’ Opens xlsx, finds <code>1_Source_Data</code> sheet header row<br>
&nbsp;β†’ Reads Brand, Date, Channel, Region, Pack Size Group, Value, Volume, Distribution<br>
&nbsp;β†’ Computes <code>Price = Value / Volume</code><br>
&nbsp;β†’ Parses dates, drops null rows<br><br>
<code>read_brand_config()</code>:<br>
&nbsp;β†’ Reads <code>2_Brand_Config</code> sheet<br>
&nbsp;β†’ Returns focal brand(s) + competitors list
</div>
</div>
<!-- modelling.py -->
<div class="file-row">
<div class="step-num">8</div>
<div>
<div class="file-path"><span class="dir">Modular app/pipeline/</span><span class="fname">modelling.py</span></div>
<div class="file-base"><span class="badge py">PY</span> Step 1 &amp; 2 β€” Pivot + OLS models</div>
</div>
<div class="fn-list">
<code>build_wide_pivot(df, focal,</code><br>
<code>&nbsp; comps, ch, rg, pack_order)</code><br><br>
<code>run_elasticity_models(all_data,</code><br>
<code>&nbsp; comps, pack_order)</code><br><br>
<code>ols(y, X)</code> β€” internal
</div>
<div class="what">
<code>build_wide_pivot()</code>:<br>
&nbsp;β†’ Called once per Channel Γ— Region combination<br>
&nbsp;β†’ Creates wide monthly table with focal brand + competitor columns<br>
&nbsp;β†’ Adds: Vol_F, Price_F, Dist_F, Cat_Vol, Vol_Up, Vol_Down<br><br>
<code>run_elasticity_models()</code>:<br>
&nbsp;β†’ Tests all OLS spec combinations per grain<br>
&nbsp;β†’ Base + competitors + seasonal dummies<br>
&nbsp;β†’ Selects best Adj-RΒ² with own-price coef &lt; 0<br>
&nbsp;β†’ Clamps elasticity to [βˆ’6, 0]<br><br>
<code>ols(y, X)</code>:<br>
&nbsp;β†’ numpy.linalg.lstsq solver<br>
&nbsp;β†’ Returns betas, t-stats, p-values, RΒ², Adj-RΒ²
</div>
</div>
<!-- diagnostics.py -->
<div class="file-row">
<div class="step-num">9</div>
<div>
<div class="file-path"><span class="dir">Modular app/pipeline/</span><span class="fname">diagnostics.py</span></div>
<div class="file-base"><span class="badge py">PY</span> Step 1 β€” Pre-modelling quality checks</div>
</div>
<div class="fn-list">
<code>run_diagnostics(all_data, comps)</code><br>
<code>safe_corr(a, b)</code> β€” internal<br>
<code>safe_trend(s)</code> β€” internal
</div>
<div class="what">
Produces 5-sheet diagnostics Excel:<br>
&nbsp;β†’ <strong>descriptive</strong>: mean, std, CV, trend per grain<br>
&nbsp;β†’ <strong>correlation</strong>: Pearson |r|, collinearity flags (&gt;0.70)<br>
&nbsp;β†’ <strong>rpi_dist</strong>: relative price index vs competitors<br>
&nbsp;β†’ <strong>cannibalization</strong>: cross-pack volume correlations<br>
&nbsp;β†’ <strong>summary</strong>: model-readiness pass/warn/fail per grain<br>
Writes: <code>output/{focal}_Step1_Diagnostics.xlsx</code>
</div>
</div>
<!-- proxies.py -->
<div class="file-row">
<div class="step-num">10</div>
<div>
<div class="file-path"><span class="dir">Modular app/pipeline/</span><span class="fname">proxies.py</span></div>
<div class="file-base"><span class="badge py">PY</span> Step 3 β€” Proxy assignment &amp; anchors</div>
</div>
<div class="fn-list">
<code>assign_proxies(best_df, pack_order)</code><br>
<code>compute_freq_anchors(df, focal)</code>
</div>
<div class="what">
<code>assign_proxies()</code>:<br>
&nbsp;β†’ Grains with wrong sign get borrowed elasticity<br>
&nbsp;β†’ Priority 1: interpolate adjacent packs<br>
&nbsp;β†’ Priority 2: borrow from nearest pack same CH/Region<br>
&nbsp;β†’ Priority 3: borrow from nearest pack any region<br>
&nbsp;β†’ Adds <code>Final_OwnE</code>, <code>IsProxy</code>, <code>ProxyMethod</code> columns<br><br>
<code>compute_freq_anchors()</code>:<br>
&nbsp;β†’ Finds dominant pack per Channel Γ— Region by vol %<br>
&nbsp;β†’ Returns <code>{CH|Region: {anchor, vol_sal}}</code>
</div>
</div>
<!-- exporters/stats.py -->
<div class="file-row">
<div class="step-num">11</div>
<div>
<div class="file-path"><span class="dir">Modular app/pipeline/exporters/</span><span class="fname">stats.py</span></div>
<div class="file-base"><span class="badge py">PY</span> Exporter β€” Elasticity results &amp; brand KPIs</div>
</div>
<div class="fn-list">
<code>build_grain_metrics(df, focal, comps)</code><br>
<code>build_model_export(final_df, metrics)</code><br>
<code>build_stats_json(df, focal,</code><br>
<code>&nbsp; final_df, anchors)</code><br>
<code>print_summary(final_df, anchors)</code>
</div>
<div class="what">
<code>build_grain_metrics()</code>: vol sal, val share, MS yr25/yr24, price/ml per grain<br>
<code>build_model_export()</code>: flat list of grain elasticities + metrics β†’ <strong>models.json</strong><br>
<code>build_stats_json()</code>: brand KPIs (vol growth, avg Ξ΅, anchors, Cr value) β†’ <strong>stats.json</strong><br>
<code>print_summary()</code>: prints grain/proxy/Adj-RΒ² summary to terminal
</div>
</div>
<!-- exporters/market.py -->
<div class="file-row">
<div class="step-num">12</div>
<div>
<div class="file-path"><span class="dir">Modular app/pipeline/exporters/</span><span class="fname">market.py</span></div>
<div class="file-base"><span class="badge py">PY</span> Exporter β€” Market dynamics</div>
</div>
<div class="fn-list">
<code>build_trend_json(df, focal, comps)</code><br>
<code>build_ms_json(df, focal)</code><br>
<code>build_vol_salience_json(df, focal)</code><br>
<code>build_val_share_json(df, focal)</code><br>
<code>build_comp_ms_json(df, focal, comps)</code><br>
<code>build_vtm_json(df, focal, comps)</code>
</div>
<div class="what">
β†’ <strong>trend.json</strong>: monthly time-series for all brands (volume, value, price, dist)<br>
β†’ <strong>ms.json</strong>: focal brand market share yr25 vs yr24 per grain<br>
β†’ <strong>vol_salience.json</strong>: pack vol % of focal brand total<br>
β†’ <strong>val_share.json</strong>: pack value % of focal brand total<br>
β†’ <strong>comp_ms.json</strong>: all brands market share yr25 vs yr24<br>
β†’ <strong>vtm.json</strong>: category + per-brand volumes with MS and vol change
</div>
</div>
<!-- exporters/ppa.py -->
<div class="file-row">
<div class="step-num">13</div>
<div>
<div class="file-path"><span class="dir">Modular app/pipeline/exporters/</span><span class="fname">ppa.py</span></div>
<div class="file-base"><span class="badge py">PY</span> Exporter β€” Price Pack Architecture</div>
</div>
<div class="fn-list">
<code>build_ppa_json(df, focal, comps,</code><br>
<code>&nbsp; channel, pack_order,</code><br>
<code>&nbsp; excel_path, excel_sheet)</code><br>
<code>_parse_ppa_sheet(path, sheet)</code> β€” internal
</div>
<div class="what">
Called twice:<br>
β‘  <code>channel="MT"</code>, sheet <code>3_PPA_MT</code> β†’ <strong>ppa_mt.json</strong><br>
β‘‘ <code>channel="TT"</code>, sheet <code>4_PPA_TT</code> β†’ <strong>ppa_tt.json</strong><br>
Per-brand PPA matrix: SKU count, MRP, price/ml, relative gradient, gross contribution %, RPI
</div>
</div>
<!-- exporters/analytics.py -->
<div class="file-row">
<div class="step-num">14</div>
<div>
<div class="file-path"><span class="dir">Modular app/pipeline/exporters/</span><span class="fname">analytics.py</span></div>
<div class="file-base"><span class="badge py">PY</span> Exporter β€” Growth &amp; interaction analytics</div>
</div>
<div class="fn-list">
<code>build_interaction_json(df, focal, comps)</code><br>
<code>build_growth_decomp_json(df, focal)</code><br>
<code>build_pgi_json(df, focal, pack_order)</code>
</div>
<div class="what">
β†’ <strong>interaction.json</strong>: cross-brand Pearson r of monthly volumes per CHΓ—Region<br>
β†’ <strong>growth_decomp.json</strong>: %-point contribution per grain to brand vol growth yr24β†’yr25<br>
β†’ <strong>pgi.json</strong>: Price Gradient Index per channel yr24 vs yr25
</div>
</div>
<!-- exporters/recommendations.py -->
<div class="file-row">
<div class="step-num">15</div>
<div>
<div class="file-path"><span class="dir">Modular app/pipeline/exporters/</span><span class="fname">recommendations.py</span></div>
<div class="file-base"><span class="badge py">PY</span> Exporter β€” Pricing recommendations</div>
</div>
<div class="fn-list">
<code>build_recs_json(df, focal, comps,</code><br>
<code>&nbsp; final_df, pack_order, excel_path)</code>
</div>
<div class="what">
β†’ <strong>recs_full.json</strong>: pricing rec cards per Channel Γ— Pack<br>
Β±5% scenarios: score, feasibility, vol/val/GC impact for each<br>
Uses elasticity from <code>final_df</code> + GC% from PPA sheet
</div>
</div>
<!-- Output files -->
<div class="out-grid">
<div style="grid-column:1/-1;font-size:10px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:#888;margin-bottom:4px">JSON files written to <code style="background:none;color:#6a1b9a">Modular app/output/</code></div>
<div class="out-file">models.json</div>
<div class="out-file">stats.json</div>
<div class="out-file">freq_anchors.json</div>
<div class="out-file">trend.json</div>
<div class="out-file">ms.json</div>
<div class="out-file">vol_salience.json</div>
<div class="out-file">val_share.json</div>
<div class="out-file">comp_ms.json</div>
<div class="out-file">vtm.json</div>
<div class="out-file">ppa_mt.json</div>
<div class="out-file">ppa_tt.json</div>
<div class="out-file">interaction.json</div>
<div class="out-file">growth_decomp.json</div>
<div class="out-file">pgi.json</div>
<div class="out-file">recs_full.json</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════ -->
<!-- PHASE 4 β€” BACKEND COPIES JSON -->
<!-- ══════════════════════════════════════════════════════════ -->
<div class="card">
<div class="card-hdr">
<div class="step-num">4</div>
<div class="card-hdr-title">Phase 4 β€” Backend: Copy JSONs &amp; Mark Done <span class="badge be">FastAPI</span></div>
<div class="card-hdr-desc">upload_server.py copies output/*.json β†’ public/data/</div>
</div>
<div class="col-hdr">
<span></span><span>File Path</span><span>Functions Called</span><span style="padding-left:12px">What it does</span>
</div>
<div class="file-row">
<div class="step-num" style="background:#c84b00">16</div>
<div>
<div class="file-path"><span class="dir">Modular app/</span><span class="fname">upload_server.py</span></div>
<div class="file-base"><span class="badge be">BE</span> post-pipeline copy step</div>
</div>
<div class="fn-list">
<code>shutil.copy2(src, REACT_DATA/fname)</code><br>
for each of 15 JSON files<br><br>
<code>_job["status"] = "done"</code>
</div>
<div class="what">
Copies all <code>output/*.json</code> β†’ <code>react-dashboard/public/data/</code><br>
Also copies <code>recs_full.json</code> β†’ <code>recs.json</code> (alias)<br>
Sets job status to <code>"done"</code> so frontend poll loop stops<br>
React now has two sources: live API + static fallback
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════ -->
<!-- PHASE 5 β€” FRONTEND DATA RELOAD -->
<!-- ══════════════════════════════════════════════════════════ -->
<div class="card">
<div class="card-hdr">
<div class="step-num">5</div>
<div class="card-hdr-title">Phase 5 β€” Frontend: Data Reload &amp; Dashboard Renders <span class="badge fe">React</span></div>
<div class="card-hdr-desc">Poll detects "done" β†’ reload() β†’ all 16 keys fetched β†’ tabs render</div>
</div>
<div class="col-hdr">
<span></span><span>File Path</span><span>Functions Called</span><span style="padding-left:12px">What it does</span>
</div>
<div class="file-row">
<div class="step-num" style="background:#1565c0">17</div>
<div>
<div class="file-path"><span class="dir">react-dashboard/src/components/</span><span class="fname">UploadModal.jsx</span></div>
<div class="file-base"><span class="badge fe">FE</span></div>
</div>
<div class="fn-list">
<code>startPolling()</code><br>
<code>GET /api/upload/status</code><br>
<code>GET /api/upload/logs</code><br>
<code>handleReload()</code><br>
<code>onSuccess(filename)</code>
</div>
<div class="what">
Polls status every 1.5 s until <code>status="done"</code><br>
On done: <code>handleReload()</code> β†’ calls <code>onSuccess(filename)</code><br>
Closes modal
</div>
</div>
<div class="file-row">
<div class="step-num" style="background:#1565c0">18</div>
<div>
<div class="file-path"><span class="dir">react-dashboard/src/components/</span><span class="fname">Header.jsx</span></div>
<div class="file-base"><span class="badge fe">FE</span></div>
</div>
<div class="fn-list">
<code>handleSuccess(filename)</code><br>
<code>setActiveFile(filename)</code><br>
<code>localStorage.setItem(LS_KEY, filename)</code><br>
<code>reload()</code>
</div>
<div class="what">
Updates header badge to show filename<br>
Persists filename in localStorage<br>
Calls <code>reload()</code> from DataContext β†’ triggers full data refresh
</div>
</div>
<div class="file-row">
<div class="step-num" style="background:#1565c0">19</div>
<div>
<div class="file-path"><span class="dir">react-dashboard/src/</span><span class="fname">DataContext.jsx</span></div>
<div class="file-base"><span class="badge fe">FE</span> shared data provider</div>
</div>
<div class="fn-list">
<code>loadAll()</code><br>
<code>fetchKey(key)</code> Γ— 16<br>
<code>GET /api/data/{key}</code> (primary)<br>
<code>GET /data/{key}.json</code> (fallback)<br>
<code>setData(results)</code><br>
<code>setLoading(false)</code>
</div>
<div class="what">
Fetches all 16 keys sequentially<br>
Each key: tries FastAPI first β†’ falls back to Vite static if server down<br>
<code>setProgress()</code> updated per key (progress bar)<br>
Once all done: <code>setData(results)</code> β†’ all tabs re-render with fresh data
</div>
</div>
<div class="file-row">
<div class="step-num" style="background:#1565c0">20</div>
<div>
<div class="file-path"><span class="dir">react-dashboard/src/</span><span class="fname">App.jsx</span></div>
<div class="file-base"><span class="badge fe">FE</span> root layout</div>
</div>
<div class="fn-list">
<code>Dashboard()</code><br>
<code>tabComponents[activeTab]</code>
</div>
<div class="what">
Loading screen clears once <code>loading=false</code><br>
Active tab component mounts with fresh data from context
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════ -->
<!-- COMPLETE FILE LIST (FLAT) -->
<!-- ══════════════════════════════════════════════════════════ -->
<div class="card">
<div class="card-hdr">
<div class="step-num">βœ“</div>
<div class="card-hdr-title">Complete File List β€” All Files Triggered</div>
<div class="card-hdr-desc">In execution order, with full paths</div>
</div>
<div style="padding:20px 24px">
<table style="width:100%;border-collapse:collapse;font-size:11.5px">
<thead>
<tr style="background:#1a6b2f;color:#fff">
<th style="padding:9px 12px;text-align:left;width:40px">#</th>
<th style="padding:9px 12px;text-align:left">Full File Path</th>
<th style="padding:9px 12px;text-align:left;width:60px">Layer</th>
<th style="padding:9px 12px;text-align:left">Triggered by</th>
<th style="padding:9px 12px;text-align:left">Purpose</th>
</tr>
</thead>
<tbody>
<tr style="background:#f7faf7">
<td style="padding:8px 12px;font-weight:700;color:#1565c0">1</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">react-dashboard/src/components/<b>Header.jsx</b></td>
<td style="padding:8px 12px"><span class="badge fe">FE</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">User click</td>
<td style="padding:8px 12px;color:#555;font-size:11px">Opens upload modal</td>
</tr>
<tr>
<td style="padding:8px 12px;font-weight:700;color:#1565c0">2</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">react-dashboard/src/components/<b>UploadModal.jsx</b></td>
<td style="padding:8px 12px"><span class="badge fe">FE</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">Header.jsx</td>
<td style="padding:8px 12px;color:#555;font-size:11px">File validation, POST /api/upload, polling</td>
</tr>
<tr style="background:#f7faf7">
<td style="padding:8px 12px;font-weight:700;color:#c84b00">3</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">Modular app/<b>upload_server.py</b></td>
<td style="padding:8px 12px"><span class="badge be">BE</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">POST /api/upload</td>
<td style="padding:8px 12px;color:#555;font-size:11px">Saves xlsx, spawns pipeline thread</td>
</tr>
<tr>
<td style="padding:8px 12px;font-weight:700;color:#1b5e20">4</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">Modular app/pipeline/<b>run.py</b></td>
<td style="padding:8px 12px"><span class="badge py">PY</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">subprocess.Popen</td>
<td style="padding:8px 12px;color:#555;font-size:11px">Pipeline entry point β€” orchestrates all steps</td>
</tr>
<tr style="background:#f7faf7">
<td style="padding:8px 12px;font-weight:700;color:#880e4f">5</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">Modular app/pipeline/<b>config.py</b></td>
<td style="padding:8px 12px"><span class="badge cfg">CFG</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">import by run.py</td>
<td style="padding:8px 12px;color:#555;font-size:11px">All thresholds and constants (pack order, caps, ratios)</td>
</tr>
<tr>
<td style="padding:8px 12px;font-weight:700;color:#1b5e20">6</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">Modular app/pipeline/<b>loader.py</b></td>
<td style="padding:8px 12px"><span class="badge py">PY</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">run.py line 65</td>
<td style="padding:8px 12px;color:#555;font-size:11px">Read 1_Source_Data + 2_Brand_Config sheets from xlsx</td>
</tr>
<tr style="background:#f7faf7">
<td style="padding:8px 12px;font-weight:700;color:#1b5e20">7</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">Modular app/pipeline/<b>modelling.py</b></td>
<td style="padding:8px 12px"><span class="badge py">PY</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">run.py line 111, 134</td>
<td style="padding:8px 12px;color:#555;font-size:11px">Wide pivot per grain + OLS elasticity model search</td>
</tr>
<tr>
<td style="padding:8px 12px;font-weight:700;color:#1b5e20">8</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">Modular app/pipeline/<b>diagnostics.py</b></td>
<td style="padding:8px 12px"><span class="badge py">PY</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">run.py line 125</td>
<td style="padding:8px 12px;color:#555;font-size:11px">Correlation, CV, RPI checks β†’ writes Diagnostics.xlsx</td>
</tr>
<tr style="background:#f7faf7">
<td style="padding:8px 12px;font-weight:700;color:#1b5e20">9</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">Modular app/pipeline/<b>proxies.py</b></td>
<td style="padding:8px 12px"><span class="badge py">PY</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">run.py line 140, 145</td>
<td style="padding:8px 12px;color:#555;font-size:11px">Proxy elasticity for wrong-sign grains + frequency anchors</td>
</tr>
<tr>
<td style="padding:8px 12px;font-weight:700;color:#1b5e20">10</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">Modular app/pipeline/exporters/<b>stats.py</b></td>
<td style="padding:8px 12px"><span class="badge py">PY</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">run.py line 153</td>
<td style="padding:8px 12px;color:#555;font-size:11px">β†’ models.json, stats.json, freq_anchors.json</td>
</tr>
<tr style="background:#f7faf7">
<td style="padding:8px 12px;font-weight:700;color:#1b5e20">11</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">Modular app/pipeline/exporters/<b>market.py</b></td>
<td style="padding:8px 12px"><span class="badge py">PY</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">run.py line 178</td>
<td style="padding:8px 12px;color:#555;font-size:11px">β†’ trend.json, ms.json, vol_salience.json, val_share.json, comp_ms.json, vtm.json</td>
</tr>
<tr>
<td style="padding:8px 12px;font-weight:700;color:#1b5e20">12</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">Modular app/pipeline/exporters/<b>ppa.py</b></td>
<td style="padding:8px 12px"><span class="badge py">PY</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">run.py line 198, 206</td>
<td style="padding:8px 12px;color:#555;font-size:11px">β†’ ppa_mt.json, ppa_tt.json (reads 3_PPA_MT, 4_PPA_TT sheets)</td>
</tr>
<tr style="background:#f7faf7">
<td style="padding:8px 12px;font-weight:700;color:#1b5e20">13</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">Modular app/pipeline/exporters/<b>analytics.py</b></td>
<td style="padding:8px 12px"><span class="badge py">PY</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">run.py line 217</td>
<td style="padding:8px 12px;color:#555;font-size:11px">β†’ interaction.json, growth_decomp.json, pgi.json</td>
</tr>
<tr>
<td style="padding:8px 12px;font-weight:700;color:#1b5e20">14</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">Modular app/pipeline/exporters/<b>recommendations.py</b></td>
<td style="padding:8px 12px"><span class="badge py">PY</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">run.py line 220</td>
<td style="padding:8px 12px;color:#555;font-size:11px">β†’ recs_full.json (pricing rec cards Β±5% scenarios)</td>
</tr>
<tr style="background:#f7faf7">
<td style="padding:8px 12px;font-weight:700;color:#c84b00">15</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">Modular app/<b>upload_server.py</b></td>
<td style="padding:8px 12px"><span class="badge be">BE</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">After pipeline exits 0</td>
<td style="padding:8px 12px;color:#555;font-size:11px">Copies output/*.json β†’ react-dashboard/public/data/, sets job done</td>
</tr>
<tr>
<td style="padding:8px 12px;font-weight:700;color:#1565c0">16</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">react-dashboard/src/<b>DataContext.jsx</b></td>
<td style="padding:8px 12px"><span class="badge fe">FE</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">Header.handleSuccess() β†’ reload()</td>
<td style="padding:8px 12px;color:#555;font-size:11px">Fetches all 16 keys via GET /api/data/{key}, updates React state</td>
</tr>
<tr style="background:#f7faf7">
<td style="padding:8px 12px;font-weight:700;color:#1565c0">17</td>
<td style="padding:8px 12px;font-family:Consolas,monospace;font-size:10.5px">react-dashboard/src/<b>App.jsx</b></td>
<td style="padding:8px 12px"><span class="badge fe">FE</span></td>
<td style="padding:8px 12px;color:#666;font-size:11px">DataContext loading=false</td>
<td style="padding:8px 12px;color:#555;font-size:11px">Clears loading screen, renders active tab with fresh data</td>
</tr>
</tbody>
</table>
<div style="margin-top:16px;padding:12px 16px;background:#e8f5e9;border-radius:8px;font-size:11.5px;color:#1b5e20;line-height:1.8">
<strong>Files NOT triggered on upload</strong> (only triggered on tab click, if pre-loaded):
<code style="background:#c8e6c9;padding:1px 5px;border-radius:3px;margin:0 3px">tabs/ElasticityResults.jsx</code>
<code style="background:#c8e6c9;padding:1px 5px;border-radius:3px;margin:0 3px">tabs/Trends.jsx</code>
<code style="background:#c8e6c9;padding:1px 5px;border-radius:3px;margin:0 3px">tabs/PPA.jsx</code>
<code style="background:#c8e6c9;padding:1px 5px;border-radius:3px;margin:0 3px">tabs/Recommendations.jsx</code>
<code style="background:#c8e6c9;padding:1px 5px;border-radius:3px;margin:0 3px">tabs/Simulation.jsx</code>
<code style="background:#c8e6c9;padding:1px 5px;border-radius:3px;margin:0 3px">tabs/BrandInteraction.jsx</code>
<code style="background:#c8e6c9;padding:1px 5px;border-radius:3px;margin:0 3px">tabs/GrowthDecomposition.jsx</code>
<code style="background:#c8e6c9;padding:1px 5px;border-radius:3px;margin:0 3px">tabs/PriceGradient.jsx</code>
<code style="background:#c8e6c9;padding:1px 5px;border-radius:3px;margin:0 3px">tabs/Methodology.jsx</code>
β€” these mount after tab click, not on upload.
</div>
</div>
</div>
</body>
</html>