Exact functions, APIs, validations and data keys for every user action
From the moment the user clicks "Upload" to the moment JSON data is in React state — every function, API call, validation and Python function.
| Check | Where | Function | Condition | On Fail |
|---|---|---|---|---|
| File type | FE | handleFile(file) — UploadModal.jsx |
filename ends with .xlsx, .xlsm, or .xls |
setError("Please upload an .xlsx / .xlsm / .xls file"); returns early, no API call |
| Server extension check | BE | upload_excel() — upload_server.py |
Same extension check server-side (double guard) | HTTP 400 — detail shown in modal error state |
| Concurrent run guard | BE | upload_excel() — upload_server.py |
_job['status'] != 'running' |
HTTP 409 — "A pipeline run is already in progress" |
| HTTP response check | FE | handleFile() — UploadModal.jsx |
res.ok === true after POST |
Reads res.json().detail and shows as error; stops polling |
| Pipeline error check | FE | startPolling() — UploadModal.jsx |
status != 'error' |
clearInterval; phase = 'error'; shows logs panel with failure detail |
| Data key unknown | BE | get_data(key) — upload_server.py |
key in DATA_KEY_MAP | HTTP 404 — DataContext catches, key value = null; tab shows empty state |
| JSON file missing | BE | get_data(key) — upload_server.py |
output/{key}.json exists on disk |
HTTP 404 — DataContext falls back to /data/{key}.json static file |
| # | File | Function | Input | Output | What it does |
|---|---|---|---|---|---|
| 1 | pipeline/loader.py | load_source_data(path) | xlsx path | DataFrame | Reads 1_Source_Data sheet; computes Price = Value/Volume; parses dates; drops nulls in Brand/Month/Value/Volume |
| 2 | pipeline/run.py | read_brand_config(path) | xlsx path | config dict | Reads 2_Brand_Config sheet; extracts focal brand name + competitor list |
| 3 | pipeline/modelling.py | build_wide_pivot(df, focal, comps, ch, rg, pack_order) | DataFrame | wide DataFrame | One wide monthly table per CH×Region grain. Adds Vol_F, Price_F, Dist_F, Price_<comp>, Cat_Vol, Vol_Up, Vol_Down columns |
| 4 | pipeline/diagnostics.py | run_diagnostics(all_data, competitors) | wide DataFrame | Excel file | 5-sheet diagnostics: descriptive stats, correlation/collinearity, RPI trends, cannibalization, summary flags |
| 4a | pipeline/diagnostics.py | safe_corr(a, b) | two Series | (r, p) | Pearson r with ≥5 non-null pairs guard |
| 4b | pipeline/diagnostics.py | safe_trend(s) | Series | (slope, r², p) | OLS trend over time index |
| 5 | pipeline/modelling.py | run_elasticity_models(all_data, comps, pack_order) | wide DataFrame | (all_results_df, best_df) | Exhaustive OLS spec search per grain; selects best Adj-R² with own-price coef < 0; clamps elasticity to [−6, 0] |
| 5a | pipeline/modelling.py | ols(y, X) | arrays | coef dict | numpy.linalg.lstsq OLS; returns betas, t-stats, p-values, R², Adj-R² |
| 6 | pipeline/proxies.py | assign_proxies(best_df, pack_order) | best_df | final_df | Wrong-sign grains: interpolate adjacent packs → borrow same CH/RG → borrow any region. Adds Final_OwnE, IsProxy, ProxyMethod |
| 7 | pipeline/proxies.py | compute_freq_anchors(df, focal, ppa_pml) | DataFrame | anchors dict | Dominant pack per CH×Region by vol %; tie-break by lowest price/ml if PPA supplied |
| 8 | pipeline/exporters/stats.py | build_grain_metrics(df, focal, comps) | DataFrame | metrics dict | Vol sal, val share, MS yr25/yr24, price/ml, base vol/val — enrichment for model export |
| 9 | pipeline/exporters/stats.py | build_model_export(final_df, grain_metrics) | final_df | → models.json | Flat list of grain elasticities + market metrics |
| 10 | pipeline/exporters/stats.py | build_stats_json(df, focal, final_df, anchors, growth_decomp) | DataFrame | → stats.json | Brand-level KPIs: vol growth, avg elasticity, market share, anchor count |
| 11 | pipeline/exporters/market.py | build_trend_json(df, focal, comps) | DataFrame | → trend.json | Monthly time-series rows for all brands |
| 12 | pipeline/exporters/market.py | build_ms_json(df, focal) | DataFrame | → ms.json | Focal brand market share yr25 vs yr24 per grain |
| 13 | pipeline/exporters/market.py | build_vol_salience_json(df, focal) | DataFrame | → vol_salience.json | Pack volume % of focal brand total |
| 14 | pipeline/exporters/market.py | build_val_share_json(df, focal) | DataFrame | → val_share.json | Pack value % of focal brand total |
| 15 | pipeline/exporters/market.py | build_comp_ms_json(df, focal, comps) | DataFrame | → comp_ms.json | All brands' market share yr25 vs yr24 |
| 16 | pipeline/exporters/market.py | build_vtm_json(df, focal, comps) | DataFrame | → vtm.json | Category + per-brand volumes with MS and vol change |
| 17 | pipeline/exporters/ppa.py | build_ppa_json(df, focal, comps, channel, ...) | DataFrame + xlsx | → ppa_mt.json / ppa_tt.json | Per-brand PPA matrix (SKU, MRP, price/ml, RPI, gross contribution) |
| 18 | pipeline/exporters/analytics.py | build_interaction_json(df, focal, comps) | DataFrame | → interaction.json | Cross-brand Pearson r of monthly volumes per CH×Region |
| 19 | pipeline/exporters/analytics.py | build_growth_decomp_json(df, focal) | DataFrame | → growth_decomp.json | %-point contribution per grain to brand volume growth yr24→yr25 |
| 20 | pipeline/exporters/analytics.py | build_pgi_json(df, focal, pack_order) | DataFrame | → pgi.json | Price Gradient Index per channel yr24 vs yr25 |
| 21 | pipeline/exporters/recommendations.py | build_recs_json(df, focal, comps, final_df, pack_order, xlsx) | DataFrame | → recs_full.json | ±5% pricing recommendation cards with score, feasibility, impact (vol, val, GC) |
What happens in the frontend when a tab is clicked. Data is already loaded in DataContext at this point — no new backend calls are made on tab switch.
Data flows from pipeline → JSON files → FastAPI → DataContext → tab component. Once loaded, tab switches are instant (no API calls).
| Tab (id) | Component | Data keys used | Python function that created each JSON | What is displayed |
|---|---|---|---|---|
| results | ElasticityResults.jsx |
models, stats, freq_anchors |
build_model_export() → models.jsonbuild_stats_json() → stats.jsoncompute_freq_anchors() → freq_anchors.json
|
Elasticity table per CH×Region×Pack; own-price E, Adj-R², dist E, comp coefs; anchor tags |
| trends | Trends.jsx |
trend |
build_trend_json() → trend.json |
Monthly time-series chart; brand/channel/metric filter; volume, value, price, distribution lines |
| ppa | PPA.jsx |
ppa_mt, ppa_tt, freq_anchors, trend |
build_ppa_json(ch='MT') → ppa_mt.jsonbuild_ppa_json(ch='TT') → ppa_tt.json
|
Price-pack architecture matrix; MRP, price/ml, RPI, gross contribution per brand/pack; MT or TT toggle |
| recs | Recommendations.jsx |
recs, freq_anchors |
build_recs_json() → recs_full.json (served as "recs") |
Pricing recommendation cards per CH×Pack; ±5% scenarios; score, feasibility, vol/val/GC impact |
| sim | Simulation.jsx |
models |
build_model_export() → models.json |
Interactive price simulator; user enters % price change; computes vol/val impact using own-price elasticity |
| interaction | BrandInteraction.jsx |
vtm, interaction, freq_anchors |
build_vtm_json() → vtm.jsonbuild_interaction_json() → interaction.json
|
Cross-brand correlation heatmap; volume-to-market by brand; channel/region filters |
| growth | GrowthDecomposition.jsx |
growth_decomp, stats |
build_growth_decomp_json() → growth_decomp.jsonbuild_stats_json() → stats.json
|
%-point contribution bars per grain to brand volume growth yr24→yr25; sorted by contribution |
| gi | PriceGradient.jsx |
pgi |
build_pgi_json() → pgi.json |
Price Gradient Index table per channel; MRP, price/ml, relative gradient yr24 vs yr25 |
| methodology | Methodology.jsx |
none | — | Static methodology explanation; no data dependency |
This runs once on page load and again after every successful upload. All 16 keys are fetched sequentially; progress bar reflects completion.
| Method | Endpoint | Called by | Function in upload_server.py | Purpose |
|---|---|---|---|---|
| POST | /api/upload |
handleFile() — UploadModal.jsx |
upload_excel() |
Saves xlsx, starts background pipeline thread |
| POST | /api/upload/reset |
UploadModal on open | reset_job() |
Clears previous job state so fresh run can start |
| GET | /api/upload/status |
startPolling() — every 1.5 s |
get_status() |
Returns { status, message, elapsed_s, filename } |
| GET | /api/upload/logs |
startPolling() — every 1.5 s |
get_logs() |
Returns full pipeline stdout log accumulated so far |
| GET | /api/data/{key} |
fetchKey(key) — DataContext.jsx |
get_data(key) |
Reads output/{key}.json written by pipeline; returns as JSON |
| GET | /api/data |
Debug / manual use | list_data_keys() |
Lists all 16 keys with availability and file size |
| API key | JSON file | Exporter file | Python function | Used in tab |
|---|---|---|---|---|
models | models.json | exporters/stats.py | build_model_export() | results, sim |
stats | stats.json | exporters/stats.py | build_stats_json() | results, growth, header KPIs |
freq_anchors | freq_anchors.json | pipeline/proxies.py | compute_freq_anchors() | results, ppa, recs, interaction |
trend | trend.json | exporters/market.py | build_trend_json() | trends, ppa |
ms | ms.json | exporters/market.py | build_ms_json() | (available for market share views) |
vol_salience | vol_salience.json | exporters/market.py | build_vol_salience_json() | (available for pack salience charts) |
val_share | val_share.json | exporters/market.py | build_val_share_json() | (available for value share charts) |
comp_ms | comp_ms.json | exporters/market.py | build_comp_ms_json() | (available for competitor views) |
vtm | vtm.json | exporters/market.py | build_vtm_json() | interaction |
ppa_mt | ppa_mt.json | exporters/ppa.py | build_ppa_json(channel='MT') | ppa |
ppa_tt | ppa_tt.json | exporters/ppa.py | build_ppa_json(channel='TT') | ppa |
interaction | interaction.json | exporters/analytics.py | build_interaction_json() | interaction |
growth_decomp | growth_decomp.json | exporters/analytics.py | build_growth_decomp_json() | growth |
pgi | pgi.json | exporters/analytics.py | build_pgi_json() | gi |
recs / recs_full | recs_full.json | exporters/recommendations.py | build_recs_json() | recs |