{"id":51611403,"url":"https://github.com/braboj/demo-randomgen","last_synced_at":"2026-07-12T08:03:56.861Z","repository":{"id":253642595,"uuid":"781474921","full_name":"braboj/demo-randomgen","owner":"braboj","description":"Random number generator REST API using Flask.","archived":false,"fork":false,"pushed_at":"2026-06-18T15:30:05.000Z","size":296,"stargazers_count":3,"open_issues_count":10,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-18T15:30:29.755Z","etag":null,"topics":["flask","python","rest-api"],"latest_commit_sha":null,"homepage":"https://braboj.github.io/demo-randomgen","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/braboj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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":"2024-04-03T13:03:16.000Z","updated_at":"2026-06-18T14:21:00.000Z","dependencies_parsed_at":"2024-08-18T13:09:38.558Z","dependency_job_id":null,"html_url":"https://github.com/braboj/demo-randomgen","commit_stats":null,"previous_names":["braboj/randomgen","braboj/demo-randomgen"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/braboj/demo-randomgen","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/braboj%2Fdemo-randomgen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/braboj%2Fdemo-randomgen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/braboj%2Fdemo-randomgen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/braboj%2Fdemo-randomgen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/braboj","download_url":"https://codeload.github.com/braboj/demo-randomgen/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/braboj%2Fdemo-randomgen/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35385773,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-12T02:00:06.386Z","response_time":87,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["flask","python","rest-api"],"created_at":"2026-07-12T08:03:56.363Z","updated_at":"2026-07-12T08:03:56.847Z","avatar_url":"https://github.com/braboj.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![CI](https://github.com/braboj/randomgen/actions/workflows/ci.yml/badge.svg)](https://github.com/braboj/randomgen/actions/workflows/ci.yml)\n[![CD](https://github.com/braboj/randomgen/actions/workflows/cd.yml/badge.svg)](https://github.com/braboj/randomgen/actions/workflows/cd.yml)\n\n# RandomGen\n\n*A coding kata taken to a production-grade, documented, deployed service.*\n\nRandomGen is a small Flask REST API that draws random numbers from a\nconfigurable discrete distribution and scores the sample against it with a\nChi-Square goodness-of-fit test.\n\n**🌐 Live demo:** \u003chttps://randomgen-llyc.onrender.com/\u003e — try the interactive\nUI or the API directly (free Render instance; the first request after idle may\ncold-start for ~30–60s).\n\n## Features\n\n- Two interchangeable generators — `/api/v1` and `/api/v2`.\n- Stateless sampling from a built-in or per-request distribution.\n- A Chi-Square goodness-of-fit report on every response.\n- An interactive browser UI — presets, weight sliders, light/dark, CSV export.\n- An OpenAPI 3.1 contract with a ReDoc reference at `/docs`.\n- `/info` and `/health` endpoints, on a hardened, non-root Docker image on gunicorn.\n\n## Quick start\n\nPrerequisites: [Docker](https://docs.docker.com/engine/install/).\n\n```bash\ndocker pull braboj/randomgen:latest\ndocker run -p 5000:5000 braboj/randomgen:latest\n```\n\nOpen [http://localhost:5000](http://localhost:5000) for the interactive home\npage — a small UI to pick a generator, distribution, and sample size and see\nthe Chi-Square verdict with an expected-vs-observed histogram (see the\n[UI snapshots](docs/reference/ui-snapshots.md) for a preview), or browse the interactive\nAPI reference at [http://localhost:5000/docs](http://localhost:5000/docs). Or\ncall the API directly to generate 100 numbers:\n\n```bash\ncurl \"http://localhost:5000/api/v1/randomgen?numbers=100\"\n```\n\n## Usage\n\nGenerate numbers from the built-in distribution:\n\n```bash\ncurl \"http://localhost:5000/api/v1/randomgen?numbers=1000\"\n```\n\nExpected output (truncated):\n\n```json\n{\n  \"numbers\": [1, 0, 1, 2, 1, 0, 1, 1, -1, 1],\n  \"quality\": {\n    \"chi_square_test\": {\n      \"is_null\": true,\n      \"chi_square\": 4.97,\n      \"p_value\": 0.17,\n      \"df\": 4\n    },\n    \"expected_histogram\": {\"-1\": 0.01, \"0\": 0.3, \"1\": 0.58, \"2\": 0.1, \"3\": 0.01},\n    \"observed_histogram\": {\"-1\": 0.01, \"0\": 0.29, \"1\": 0.59, \"2\": 0.1, \"3\": 0.01}\n  }\n}\n```\n\nOverride the distribution per request with a single `dist` parameter of\ncomma-separated `value:probability` pairs (preferred — each value is bound to\nits own weight, and they must sum to 1):\n\n```bash\ncurl \"http://localhost:5000/api/v1/randomgen?numbers=1000\u0026dist=1:0.2,2:0.2,3:0.6\"\n```\n\nThe repeated `value` / `probability` parameters still work (they must be the\nsame length and sum to 1):\n\n```bash\ncurl \"http://localhost:5000/api/v1/randomgen?numbers=1000\u0026value=1\u0026value=2\u0026value=3\u0026probability=0.2\u0026probability=0.2\u0026probability=0.6\"\n```\n\nInvalid input — a non-integer `numbers`, mismatched lengths, or weights that\ndon't sum to 1 — returns `400` with a JSON `{\"error\": \"...\"}` body.\n\n## Deploy a free demo (Render)\n\nThe repo ships a [`render.yaml`](render.yaml) blueprint, so you can run a\nzero-cost demo on [Render](https://render.com) from the published image:\n\n[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/braboj/demo-randomgen)\n\n1. In the Render dashboard choose **New → Blueprint** and connect this\n   repository (or use the button above).\n2. Render provisions a free web service that runs the published\n   `braboj/randomgen:latest` image with a `/health` check. It injects `$PORT`;\n   the image binds it automatically (`gunicorn ... 0.0.0.0:${PORT:-5000}`), so\n   no extra configuration is needed.\n3. Once live, the service is reachable at the URL Render assigns — this\n   project's demo runs at \u003chttps://randomgen-llyc.onrender.com/\u003e.\n\nReleases redeploy automatically: the image workflow POSTs a Render Deploy Hook\nafter pushing a new image (see PLAYBOOK section 5).\n\n\u003e **Note:** free instances spin down after ~15 minutes of inactivity and\n\u003e cold-start (~30–60s) on the next request — expected for a zero-cost demo.\n\n## Project structure\n\n```text\nsrc/randomgen/         # application package — app factory, service, OpenAPI contract\n  domain/              # framework-free core: generators, histogram, Chi-Square, errors\n  blueprints/          # web + versioned-API route blueprints\n  templates/, static/  # home-page UI (Jinja + CSS/JS)\ntests/                 # pytest suite — unit, integration, e2e\nscripts/               # demo, plotting, and API client helpers\ndocs/                  # arc42 architecture, ADRs, history, assets\npyproject.toml         # PEP 621 metadata, dependencies, tool config\nDockerfile             # non-root, gunicorn, digest-pinned base image\nrender.yaml            # Render free-tier deploy blueprint\ngunicorn.conf.py       # gunicorn runtime config (bind + workers)\n.github/workflows/     # CI (gated jobs), CodeQL (SAST), CD (release + publish + deploy)\n```\n\n## Development\n\nSupported Python: 3.14+.\n\n```bash\n# Clone\ngit clone https://github.com/braboj/randomgen.git\ncd randomgen\n\n# Install the project with the developer toolchain (ruff, mypy, pytest)\npip install -e \".[dev]\"\n\n# Lint, type-check, and run the fast test gate (unit + integration)\nruff check . \u0026\u0026 ruff format --check . \u0026\u0026 mypy\npytest --cov=randomgen --cov-fail-under=85\n\n# End-to-end tier: real container (Testcontainers on a Podman or Docker\n# backend) + a Playwright browser test. One-time setup, then run:\npip install -e \".[e2e]\" \u0026\u0026 playwright install chromium\npytest -m e2e\n\n# Run the service locally (Flask dev server, hot reload)\nflask --app \"randomgen.app:create_app\" run   # http://127.0.0.1:5000\n```\n\n## Configuration\n\nThe service is stateless: per-request behavior is set with query parameters\n(see [Usage](#usage)), while deployment and limits are set by the keys below.\n\n| Key | Where | Default | Description |\n|-----|-------|---------|-------------|\n| `RANDOMGEN_LOG_LEVEL` | env var | `INFO` | Application log level (one log line per request). |\n| `WEB_CONCURRENCY` | env var | `2` | gunicorn worker count; see `gunicorn.conf.py`. |\n| `PORT` | env var | `5000` | Listen port; gunicorn binds `$PORT` (the Flask dev server uses `5000`). |\n| `MAX_NUMBERS` | `service.py` | `10000` | Upper bound on `numbers`. |\n| Built-in distribution | `service.py` | `[-1,0,1,2,3]` / `[0.01,0.3,0.58,0.1,0.01]` | `DEFAULT_NUMBERS` / `DEFAULT_PROBABILITIES`. |\n\n## Contributing\n\nContributions are welcome. Set up your environment from\n[Development](#development) above, follow the project conventions and session\nworkflow in [CLAUDE.md](CLAUDE.md), and open a PR — one concern per PR,\nconventional commits, and green CI before merge (`main` is branch-protected).\n\n## Next steps\n\n- For the full REST contract, see [`src/randomgen/openapi.yaml`](src/randomgen/openapi.yaml) (rendered at `/docs`).\n- For the architecture, see the [arc42 documentation](docs/arc42/).\n- For how it was built — from coding kata to deployed service — see [docs/history/](docs/history/).\n- To contribute, see [Contributing](#contributing) above.\n- To leave feedback, open a [discussion](https://github.com/braboj/randomgen/discussions).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbraboj%2Fdemo-randomgen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbraboj%2Fdemo-randomgen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbraboj%2Fdemo-randomgen/lists"}