Production planner for vendor allocation across programs and quarters.
Forgejo Actions auto-cancels older in-progress runs that share a workflow + ref when a new push lands. Back-to-back pushes to master will end up with the older run's deploy + close-tasks jobs marked "Has been cancelled" and silently dropping TASK closures. Add an explicit per-ref concurrency group with cancel-in-progress=false so back-to-back pushes queue instead of preempting each other. Mirrors the fix shipped in notes-app for BUG-095 / TASK-273. |
||
|---|---|---|
| .forgejo/workflows | ||
| analytics | ||
| api | ||
| archive | ||
| data | ||
| migrations | ||
| .dockerignore | ||
| .gitignore | ||
| app.html | ||
| Dockerfile | ||
| main.py | ||
| pyproject.toml | ||
| README.md | ||
Production Planner
Vendor allocation planning tool for global supply management. Built for managing assembly across multiple vendors and programs.
Quick Start
uv sync
uv run python main.py
# Open http://localhost:8001
First run auto-seeds with demo data (9 programs, 10 vendors, 5 forecast revisions).
Architecture
The app has two parts:
- Main app (
app.html) – vanilla JS, no build step, served directly by FastAPI - Analytics (
analytics/) – Vite + React app with Graphic Walker (Tableau-like data exploration)
Both share the same FastAPI backend and SQLite database.
Backend
- FastAPI + SQLite (WAL mode), single-file API at
api/app.py - DB:
data/gsm_tools.db(auto-created, gitignored) - Migrations:
migrations/*.sql, applied in order on startup - Pre-built SQL views (
v_demand,v_vendor_capacity, etc.) for analytics
Main App
- Single
app.html, vanilla JS, no dependencies, no build step - Sections: Current Forecast, Scenarios, Programs, Vendors, Forecast History, Revision Log, Debug
Analytics App
- Vite + React app at
analytics/ - Embeds Graphic Walker – drag-and-drop visual analytics like Tableau
- Loads data from any table or SQL view via the
/api/sqlendpoint - Built output goes to
static/analytics/, served by FastAPI at/analytics
Building the analytics app
cd analytics
npm install # first time only
npm run build # outputs to ../static/analytics/
For development with hot reload:
cd analytics
npm run dev # runs on :5173, proxies /api to :8001
# (start the backend separately: uv run python main.py)
You only need to rebuild when you change the analytics app. The main app (app.html) requires no build step.
Data Model
Programs
- Code (e.g. D93, D101) – primary identifier, matches imported CSV files
- Optional nickname for display (e.g. "iPhone 16 Pro")
- group_name for visual grouping (e.g. D9x, D10x)
- default_throughput (units/day/line) – baseline for all vendors
Vendors
- Code (e.g. FXN-ZZ, IPG-CH) – primary identifier
- Optional nickname for display (e.g. "Foxconn Zhengzhou")
- group_name for visual grouping (e.g. FXN, IPG)
- capacity – total line capacity at the facility
Vendor-Program Associations
- lines – production lines allocated to this program at this vendor
- throughput – optional override of the program's default (0 = use default)
Scenarios
- Named allocation strategies with rules per program
- Each rule assigns percentages or fixed quantities to vendors
- Transitions define phased allocation changes at specific quarters
- Overrides adjust vendor capacity temporarily (e.g. planned maintenance)
- Auto-saved to DB on every change
MPS (Master Production Schedule)
- mps_commit – each uploaded forecast is a versioned "commit"
- mps_entry – one row per program x quarter x commit
- mps_diff – auto-generated change records between revisions
Analytics Views
Pre-built SQL views for the analytics page:
v_demand– latest forecast with program names/groupsv_vendor_capacity– lines, throughput, quarterly capacity per vendor-programv_vendor_group_summary– aggregate stats by vendor groupv_scenario_allocations– all allocation rules denormalized across scenariosv_scenario_comparison– base allocations for comparing scenarios side-by-sidev_scenario_overrides– capacity overrides with names
File Structure
gsm-tools/
main.py # Entry point (uvicorn, port 8001)
app.html # Main app – single-page vanilla JS frontend
pyproject.toml # Python deps (fastapi, uvicorn, httpx)
api/
app.py # All API endpoints + seed logic
db.py # SQLite helpers + migration runner
analytics/ # Vite + React analytics app (Graphic Walker)
src/
App.jsx # Main component – data loading + GraphicWalker
index.css # Styles matching main app design
vite.config.js # Build config (outputs to ../static/analytics/)
package.json
static/
analytics/ # Built analytics app (gitignored, run npm run build)
migrations/
001-014 # Schema migrations (applied in order on startup)
data/
gsm_tools.db # SQLite database (auto-created)
API Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/mps/current |
Latest forecast with programs, quarters, entries |
| GET | /api/mps/history |
All commits with diff counts |
| GET | /api/mps/history/all |
Full entry history |
| POST | /api/mps/preview |
Parse CSV, return diffs and conflicts |
| POST | /api/mps/import |
Commit a previewed import |
| PUT | /api/mps/commits/{id} |
Edit commit note/publish date |
| GET | /api/programs |
Programs with aliases and vendor associations |
| PUT | /api/programs/{id} |
Update program metadata |
| GET | /api/vendors |
All vendors |
| POST | /api/vendors |
Create vendor |
| PUT | /api/vendors/{id} |
Update vendor |
| DELETE | /api/vendors/{id} |
Delete vendor |
| POST | /api/vendor-programs |
Create vendor-program association |
| PUT | /api/vendor-programs/by-pair |
Upsert lines/throughput |
| DELETE | /api/vendor-programs/{id} |
Delete association |
| GET | /api/scenarios |
All scenarios with rules and overrides |
| POST | /api/scenarios |
Create scenario |
| PUT | /api/scenarios/{id} |
Update scenario (full replace) |
| DELETE | /api/scenarios/{id} |
Delete scenario |
| POST | /api/scenarios/reorder |
Update scenario tab order |
| POST | /api/sql |
Execute arbitrary SQL (debug/analytics) |