Skip to content

david-vendrell/damm-hack

Repository files navigation

LineWise · Damm × Engineering HUB Hackathon

Predict the real OEE of a planned production week, and rearrange it to lift the ceiling.

LineWise is an internal planning aid for the canning lines L14, L17 and L19 at Damm's El Prat factory. Blue Yonder tells the planner what the schedule should look like. LineWise tells the planner what reality will actually deliver, and how to rearrange the week to do better.


The problem

  • Damm plans production with Blue Yonder / JDA, which uses theoretical changeover times.
  • Reality diverges: format changes, maintenance windows, micro-stops, SKU mix, warm-up after CIP.
  • Over 2,141 production OFs in 2025, mean OEE was ~49 % with a historical p90 ceiling of ~68 %.
  • That is a ~14 point controllable gap that nobody is harvesting systematically. Each OEE point on these lines is millions of cans per year.

Our solution

LineWise is built around four moves:

  1. Predict. A LightGBM quantile model (p10 / p50 / p90) scores every planned block using ~70 features (SKU identity, previous OF, packaging hierarchy, recent line history, maintenance proximity, calendar). MAE on p50 is 0.103 on a Q4 2025 holdout.
  2. Validate. Planners upload a Planificado producciones or Diario Hl_Planif Excel and get a per-OF verdict (Procede / Revisar / Evitar) with the top SHAP drivers behind it.
  3. Optimize. A constraint-aware solver reassigns blocks across (línea, día, turno) respecting deadlines, line-format compatibility, HL invariance, and a quality gate (≥ 3 historical runs per SKU × línea). Two objectives: expected p50 or aggressive p90.
  4. Explain. Every recommendation ships with a reason: drivers per block, swap log per move. No LLM in the prediction or optimization paths.

The planner-facing surface is a Next.js dashboard with four routes: Observabilidad (historic 2025 performance), Validar plan (upload + verdicts), Post mortem (root-cause), Urgencias (reactive replan). Stack: Next.js 14 (App Router) + Tailwind + Prisma/SQLite for editable objects + DuckDB for the analytics layer + LightGBM for the model.


Quick start

One command, on a fresh clone:

git clone https://github.com/david-vendrell/damm-hack.git
cd damm-hack
./start.sh

start.sh is idempotent. It will:

  1. Verify Node 18+, npm and Python 3.
  2. Confirm the five required Excels are present in Repte operacions/.
  3. Seed web/.env from web/.env.example if missing (you must then add your OPENAI_API_KEY).
  4. Install web/ JS dependencies if node_modules is missing.
  5. Create a .venv and pip install -r requirements.txt for the model sidecar.
  6. Sync the 3D viewer assets into web/public/interactive-3d/.
  7. Apply Prisma migrations to web/prisma/dev.db.
  8. Build db/linewise.duckdb from the Excels (only if missing).
  9. Launch Prisma Studio (:5555), the LineWise model sidecar (:8001) and the Next dev server (:3000).

Then open the dashboard at http://localhost:3000/observabilidad.

Useful flags:

./start.sh --setup-only      # prepare everything, do not launch any server
./start.sh --no-studio       # skip Prisma Studio, just the dev server
./start.sh --rebuild-duck    # force-rebuild db/linewise.duckdb from the Excels
./start.sh --help            # full usage

One-time prerequisites

  • Node 18+ and npm.
  • Python 3.10+ with pip (used to build db/linewise.duckdb and to run the model sidecar). start.sh creates a .venv and installs requirements.txt for you on first run.
  • macOS only, and only if you intend to retrain the model: brew install libomp (LightGBM dependency). Not needed to run the dashboard.

Environment variables (web/.env)

The dashboard reads configuration from web/.env (loaded by Next.js) or web/.env.local for personal overrides. Both files are gitignored.

On first run, start.sh will copy web/.env.exampleweb/.env automatically. You then need to open that file and paste your own OPENAI_API_KEY — every other default is fine for local development.

# web/.env
DATABASE_URL="file:./dev.db"                 # Prisma SQLite path (matches start.sh)
LINEWISE_URL="http://localhost:8001"         # model sidecar (started by start.sh)
OPENAI_API_KEY="sk-..."                      # required: Ask bar in /observabilidad
OPENAI_MODEL="gpt-4o-mini"                   # optional, server-side model id
NEXT_PUBLIC_OPENAI_MODEL="gpt-4o-mini"       # optional, label shown in the Ask bar UI
# HF_TOKEN="hf_..."                          # optional: only for redeploying the HF Space
# DATA_DIR="../Repte operacions"             # optional: override source-Excel path

What each one does:

  • DATABASE_URL — Prisma's SQLite connection string. The default file:./dev.db matches what start.sh migrates. Only change it if you point Prisma at a different file.
  • LINEWISE_URL — base URL of the LineWise model sidecar (scripts/local_model_server.py, FastAPI on :8001). start.sh boots the sidecar for you via npm run dev:full. If the sidecar is unreachable, /validar and /urgencias silently fall back to a local heuristic — keep this pointed at the running sidecar to see the real model.
  • OPENAI_API_KEY — required for the natural-language Ask bar in /observabilidad (web/src/server/openai.ts). Without it the Ask bar 500s; the rest of the dashboard still works. Get a key at https://platform.openai.com/api-keys.
  • OPENAI_MODEL — model id the server uses when calling OpenAI. Defaults to gpt-4o-mini.
  • NEXT_PUBLIC_OPENAI_MODEL — same name surfaced as a small badge under the Ask bar; keep in sync with OPENAI_MODEL.
  • HF_TOKEN (optional) — Hugging Face token. Not used by the running app any more; only scripts/10_push_to_hf_space.py reads it when redeploying the private marcaguilar/linewise-demo Space.
  • DATA_DIR (optional) — used by npm run ingest if you keep the Damm Excels somewhere other than Repte operacions/.

Never commit .env or .env.local. They are gitignored on purpose; keys are personal.


What lives where

damm-hack/
├── start.sh                     one-shot setup + launch (entry point)
├── web/                         Next.js dashboard (Observabilidad, Validar, Post mortem, Urgencias)
├── db/linewise.duckdb           analytics DB consumed by the dashboard (~6 MB)
├── Repte operacions/            raw Damm Excels (source of truth)
├── scripts/                     Python pipeline: Excel → DuckDB → model → lookups
├── engine/                      inference modules (parser, features, predict, optimizer)
├── app.py                       Gradio model demo (separate from the web app)
├── models/, lookups/, parquet/  trained models, runtime lookups, table exports
├── reports/                     analytics CSVs, model eval, backtest, PDF report
├── HANDOFF.md                   deep technical handoff (read this for the full story)
└── PRODUCT.md                   product intent, users, voice

Assumptions

  • The Excels in Repte operacions/ are the canonical inputs. Sheet names and headers match the originals delivered by Damm (May 2026 snapshot). If Damm reshapes a file, the ingestion scripts need a small update.
  • Line × format compatibility is a hard constraint, codified from Damm's verbal confirmation on 2026-05-23: L14 = {1/3, 1/2}, L17 = {1/3}, L19 = {1/3, 1/2, 2/5}.
  • Fecha Fin is the only date granularity available in OEE history (no hour of day). Intra-shift effects only land when uploads include a turno column.
  • The OEE formula and horas_cambio = PAR_TOT − (PNP + LIMPIEZA + IDLE) follow Damm's own data-model diagram (also stored in _meta_formulas). IDLE does not affect OEE.
  • Cross-line moves are limited to (SKU × línea) pairs with ≥ 3 historical runs. Physical feasibility is inferred from history, not asserted by Damm.
  • The model was trained on 2025 only. Cold-start SKUs new in May 2026 fall back to family-level priors (familia_line_oee_p50).

Limitations

  • Confidence band coverage of [p10, p90] is 67 % on the holdout, vs an 80 % target. The band is narrower than ideal.
  • MAE improvement over a naive-mean baseline is +20 % on p50, vs a ≥ 30 % stretch target.
  • The optimizer's typical lift on already-decent plans is modest (~+0.4 OEE pts). Most historical plans are already close to the ceiling. The diagnostic value (validate-this-plan / find-improvements / diagnose-not-schedulable) matters more than the delta.
  • Block splitting is not supported. Each OF is atomic and assigned to a single slot.
  • The worst prediction errors come from mid-shift incidents (breakdowns, quality events) that no sequencing-aware model can foresee from planning data alone.
  • The Hugging Face demo Space is private (the hackathon org is shared with competitors). Flip to public only during a live demo.

Next steps

  1. Block splitting in the optimizer (V3) so large OFs can span multiple (línea, día, turno) slots.
  2. Optuna hyperparameter search plus recalibrated quantile alphas (0.05 / 0.95) to widen the [p10, p90] band to its 80 % target.
  3. OR-tools CP-SAT formulation for global optimization (the current solver is greedy with tabu).
  4. Multi-week optimization. Today the horizon is a single week.
  5. Downloadable optimized Excel in Damm's original Planificado producciones format, so the planner can push it straight back into Blue Yonder.
  6. Real-time integration with Damm MES: pull plans directly, push recommendations back. Ingest 2024 and 2023 history if Damm shares it, to lift the training set.

Where to go next

Lines: 14, 17, 19 at El Prat. Challenge: LineWise (Operations), DAMM × Engineering HUB Hackathon, May 2026.

About

Damm x EHub Hackathon

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors