{"id":47906833,"url":"https://github.com/schmidtcode/imsim","last_synced_at":"2026-04-04T04:54:39.183Z","repository":{"id":204742774,"uuid":"712559979","full_name":"SchmidtCode/IMSim","owner":"SchmidtCode","description":"Inventory Management Simulator showing how PNA could be viewed in terms of days from Order Point.","archived":false,"fork":false,"pushed_at":"2026-03-24T18:02:25.000Z","size":245,"stargazers_count":1,"open_issues_count":11,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-04T04:54:37.157Z","etag":null,"topics":["dash","plotly","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SchmidtCode.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-10-31T17:55:33.000Z","updated_at":"2026-03-24T18:01:51.000Z","dependencies_parsed_at":"2023-11-15T14:51:19.223Z","dependency_job_id":"62804c2b-66dc-48a5-81f9-5314c6562e22","html_url":"https://github.com/SchmidtCode/IMSim","commit_stats":{"total_commits":13,"total_committers":3,"mean_commits":4.333333333333333,"dds":0.3846153846153846,"last_synced_commit":"a1f05bec6a4b8a49d55d8f9adb542b3d6bdcf8a4"},"previous_names":["schmidtcode/imsim"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/SchmidtCode/IMSim","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SchmidtCode%2FIMSim","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SchmidtCode%2FIMSim/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SchmidtCode%2FIMSim/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SchmidtCode%2FIMSim/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SchmidtCode","download_url":"https://codeload.github.com/SchmidtCode/IMSim/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SchmidtCode%2FIMSim/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31388169,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T04:26:24.776Z","status":"ssl_error","status_checked_at":"2026-04-04T04:23:34.147Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dash","plotly","python"],"created_at":"2026-04-04T04:54:38.404Z","updated_at":"2026-04-04T04:54:39.174Z","avatar_url":"https://github.com/SchmidtCode.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# IMSim\n\nIMSim is an inventory management training app built with Dash 4. It now starts with an academy-style lesson path that teaches order points, fill rate, PNA, review cycles, and dashboard controls step by step before unlocking the full simulator.\n\n[![Live Demo](https://img.shields.io/badge/demo-live-brightgreen)](https://imsim.pythonanywhere.com/)\n\n## Highlights\n\n- Dash 4 app factory under `src/imsim/` with installable package and WSGI entrypoint\n- Training-first UI with academy menu, unlockable lessons, lesson objectives, and a full simulator reward\n- Inventory policy engine for OP, LP, EOQ, OQ, SOQ, pack rounding, and auto-PO behavior\n- Deterministic early lessons for clean tutorial concepts plus stochastic later lessons for real-world behavior\n- Daily demand simulation with stockout, holding, expedite, purchase, revenue, and COGS tracking\n- ASQ OP adjuster with min-hit and max-dollar guardrails\n- Session persistence through a repository abstraction with PostgreSQL support and file fallback\n- CSV and XLSX import flow with header normalization and preview validation\n- Docker, CI, tests, and tracked `uv.lock` for reproducible development\n\n## Quickstart\n\n### Local with `uv`\n\n```bash\ngit clone https://github.com/SchmidtCode/IMSim\ncd IMSim\nuv python install\nuv sync --group dev\nuv run imsim\n```\n\nThe app starts on `http://127.0.0.1:8050/`.\n\nIf you want PostgreSQL-backed session storage locally without Docker, set:\n\n```bash\nexport IMSIM_DATABASE_URL=\"postgresql+psycopg://imsim:imsim@localhost:5432/imsim\"\nuv run imsim\n```\n\nLegacy entrypoint support is still available:\n\n```bash\nuv run python app.py\n```\n\n### Docker\n\nStandalone container with file-backed sessions:\n\n```bash\ndocker build -t imsim .\ndocker run --rm -p 8050:8050 imsim\n```\n\nPostgreSQL-backed stack with multi-worker Gunicorn defaults:\n\n```bash\ndocker compose up --build\n```\n\nCompose injects `IMSIM_DATABASE_URL`, so the container uses PostgreSQL-backed sessions and\nscales Gunicorn through the shared config in `imsim.gunicorn_config`.\n\n## Runtime configuration\n\n| Variable | Purpose |\n| --- | --- |\n| `IMSIM_DATABASE_URL` | Optional SQLAlchemy database URL for session persistence. If set, the app uses the database instead of filesystem JSON. |\n| `IMSIM_DATA_DIR` | Directory for persisted session JSON files. Defaults to `var/sessions/`. |\n| `IMSIM_GUNICORN_WORKERS` | Optional Gunicorn worker count for container/Procfile deploys. Defaults to `4` with a database URL, otherwise `1`. |\n| `IMSIM_GUNICORN_THREADS` | Optional Gunicorn thread count. Defaults to `2` with a database URL, otherwise `1`. |\n| `IMSIM_GUNICORN_TIMEOUT` | Optional Gunicorn request timeout in seconds. Defaults to `120`. |\n| `IMSIM_ADMIN_TOKEN` | Optional bearer or `X-IMSIM-ADMIN-TOKEN` value for maintenance endpoints. |\n| `ALLOW_DEV_SHUTDOWN` | Enables the `/shutdown` endpoint for local development. |\n| `SHUTDOWN_URL` | Retained for compatibility with prior deployments. Internal shutdown no longer self-posts to this URL. |\n| `IMSIM_GITHUB_URL` | Footer link override. |\n| `IMSIM_HOST` / `IMSIM_PORT` / `IMSIM_DEBUG` | App run settings for `python -m imsim`. Standard `HOST` / `PORT` env vars are also honored for platform deploys. |\n\n## Free hosting recommendation\n\nFor a lightweight public demo, the simplest free setup for IMSim today is:\n\n- Render Free Web Service for the Dash app\n- Aiven Free PostgreSQL for session persistence\n\nThis split matters because Render's free Postgres offering currently expires 30 days after creation, so it is not a good fit for a reusable demo database.\n\n### Why this works well\n\n- This repo already includes a production `Dockerfile` and Gunicorn entrypoint\n- `render.yaml` is included for a one-service Render deploy\n- The app now honors platform-provided `PORT`, which removes a common free-hosting issue\n- IMSim already accepts either `IMSIM_DATABASE_URL` or `DATABASE_URL`\n\n### Deploy steps\n\n1. Create a free PostgreSQL instance on Aiven.\n2. Copy the connection string and append SSL mode if Aiven requires it, for example: ```postgresql+psycopg://USER:PASSWORD@HOST:PORT/DBNAME?sslmode=require```\n3. On Render, create a new Blueprint service from this GitHub repo. Render will read `render.yaml`.\n4. In the Render dashboard, set `DATABASE_URL` to the Aiven connection string.\n5. Deploy and share the generated `onrender.com` URL.\n\n### Free-tier tradeoffs\n\n- Render free web services spin down after 15 minutes idle and take roughly a minute to wake up\n- Render free web services use an ephemeral filesystem, so database-backed sessions are the correct setup\n- The included `render.yaml` uses `1` Gunicorn worker to stay conservative on free-instance memory\n\n### GitHub-hosted images\n\nThe app does not currently depend on a separate image bucket. Frontend assets are served by Dash from `assets/`. If you later want lesson images or marketing screenshots to load from GitHub raw URLs, that can be added as a small config-driven enhancement.\n\n## Upload format\n\nUploads accept `.csv` and `.xlsx` files with these seven numeric columns:\n\n1. `Usage Rate`\n2. `Lead Time`\n3. `Item Cost`\n4. `Initial PNA`\n5. `Safety Allowance (%)`\n6. `Standard Pack`\n7. `Hits Per Month`\n\nCommon header aliases are normalized automatically. The import preview rejects missing columns, duplicate logical columns, non-numeric values, and non-positive inputs other than `Initial PNA`.\n\n## Development\n\n### Commands\n\n```bash\nuv run pytest\nuv run ruff check .\nuv run ruff format .\nuv build\ndocker run --rm -v \"$PWD:/repo\" -w /repo ghcr.io/gitleaks/gitleaks:v8.30.1 git . --config .gitleaks.toml --redact --verbose --no-banner\n```\n\nCI also runs `gitleaks` on every push and pull request with the repository's full git history.\n\n### Repo layout\n\n```text\nsrc/imsim/        package code, app factory, callbacks, services, and models\nassets/           custom CSS and Dash-served frontend assets\nexamples/         tracked sample input files\ntests/            unit and app/API coverage\nvar/sessions/     runtime session storage (gitignored)\n```\n\n### WSGI and deployment\n\n- Package entrypoint: `uv run imsim`\n- Module entrypoint: `uv run python -m imsim`\n- WSGI target: `imsim.wsgi:server`\n- Gunicorn config module: `python:imsim.gunicorn_config`\n- Procfile target: `gunicorn --config python:imsim.gunicorn_config imsim.wsgi:server`\n\n## Maintenance API\n\nIf `IMSIM_ADMIN_TOKEN` is set, send it as either:\n\n- `Authorization: Bearer \u003ctoken\u003e`\n- `X-IMSIM-ADMIN-TOKEN: \u003ctoken\u003e`\n\nEndpoints:\n\n- `POST /api/admin/schedule_shutdown`\n- `POST /api/admin/cancel_shutdown`\n- `GET /api/admin/shutdown_status`\n\nDuring the final minute, running sessions are paused and the UI is locked. Session data is persisted before shutdown handling.\n\n## Persistence backends\n\n- Default local behavior: file-backed sessions under `var/sessions/` or `IMSIM_DATA_DIR`\n- Database behavior: set `IMSIM_DATABASE_URL` to any supported SQLAlchemy URL\n- Docker Compose default: PostgreSQL-backed sessions using the bundled `db` service\n- Database-backed sessions are the recommended path for higher Gunicorn worker counts\n\n## Container publishing\n\nGitHub Actions now builds and publishes a container image to GHCR on pushes to `main` / `master`\nand on version tags. The compose file accepts `IMSIM_IMAGE` if you want to deploy a specific tag:\n\n```bash\nexport IMSIM_IMAGE=ghcr.io/schmidtcode/imsim:latest\ndocker compose pull\ndocker compose up -d\n```\n\n## Example data\n\n- `examples/sample-items.csv`\n- `examples/Example.xlsx`\n\n## License\n\nApache-2.0. See `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschmidtcode%2Fimsim","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fschmidtcode%2Fimsim","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschmidtcode%2Fimsim/lists"}