blindgrid 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- blindgrid-0.2.0/.gitignore +26 -0
- blindgrid-0.2.0/CHANGELOG.md +109 -0
- blindgrid-0.2.0/CONTRIBUTING.md +126 -0
- blindgrid-0.2.0/LICENSE +21 -0
- blindgrid-0.2.0/PKG-INFO +488 -0
- blindgrid-0.2.0/README.md +437 -0
- blindgrid-0.2.0/config.example.toml +112 -0
- blindgrid-0.2.0/docs/installation.md +405 -0
- blindgrid-0.2.0/docs/logo.svg +45 -0
- blindgrid-0.2.0/install.ps1 +338 -0
- blindgrid-0.2.0/install.py +258 -0
- blindgrid-0.2.0/install.sh +323 -0
- blindgrid-0.2.0/pyproject.toml +87 -0
- blindgrid-0.2.0/src/blindgrid/__init__.py +13 -0
- blindgrid-0.2.0/src/blindgrid/__main__.py +13 -0
- blindgrid-0.2.0/src/blindgrid/allocation.py +81 -0
- blindgrid-0.2.0/src/blindgrid/cli.py +769 -0
- blindgrid-0.2.0/src/blindgrid/config.py +337 -0
- blindgrid-0.2.0/src/blindgrid/errors.py +27 -0
- blindgrid-0.2.0/src/blindgrid/export.py +119 -0
- blindgrid-0.2.0/src/blindgrid/filters.py +157 -0
- blindgrid-0.2.0/src/blindgrid/generator.py +65 -0
- blindgrid-0.2.0/src/blindgrid/household.py +72 -0
- blindgrid-0.2.0/src/blindgrid/i18n/__init__.py +118 -0
- blindgrid-0.2.0/src/blindgrid/i18n/de.py +192 -0
- blindgrid-0.2.0/src/blindgrid/i18n/en.py +188 -0
- blindgrid-0.2.0/src/blindgrid/i18n/es.py +188 -0
- blindgrid-0.2.0/src/blindgrid/i18n/fr.py +190 -0
- blindgrid-0.2.0/src/blindgrid/models.py +263 -0
- blindgrid-0.2.0/src/blindgrid/paths.py +49 -0
- blindgrid-0.2.0/src/blindgrid/plan.py +202 -0
- blindgrid-0.2.0/src/blindgrid/planner.py +52 -0
- blindgrid-0.2.0/src/blindgrid/render.py +401 -0
- blindgrid-0.2.0/src/blindgrid/store.py +224 -0
- blindgrid-0.2.0/tests/conftest.py +78 -0
- blindgrid-0.2.0/tests/test_allocation.py +88 -0
- blindgrid-0.2.0/tests/test_cli.py +423 -0
- blindgrid-0.2.0/tests/test_config.py +277 -0
- blindgrid-0.2.0/tests/test_export.py +60 -0
- blindgrid-0.2.0/tests/test_filters.py +87 -0
- blindgrid-0.2.0/tests/test_generator.py +84 -0
- blindgrid-0.2.0/tests/test_household.py +102 -0
- blindgrid-0.2.0/tests/test_i18n.py +202 -0
- blindgrid-0.2.0/tests/test_packaging.py +68 -0
- blindgrid-0.2.0/tests/test_paths.py +93 -0
- blindgrid-0.2.0/tests/test_plan.py +109 -0
- blindgrid-0.2.0/tests/test_plan_household.py +166 -0
- blindgrid-0.2.0/tests/test_planner.py +100 -0
- blindgrid-0.2.0/tests/test_randomness.py +45 -0
- blindgrid-0.2.0/tests/test_render.py +229 -0
- blindgrid-0.2.0/tests/test_store.py +156 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Local configuration and generated output
|
|
2
|
+
config.toml
|
|
3
|
+
plan.md
|
|
4
|
+
|
|
5
|
+
# Python
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.py[cod]
|
|
8
|
+
*.egg-info/
|
|
9
|
+
build/
|
|
10
|
+
dist/
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
|
|
14
|
+
# Tooling
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
.coverage
|
|
18
|
+
htmlcov/
|
|
19
|
+
|
|
20
|
+
# OS
|
|
21
|
+
.DS_Store
|
|
22
|
+
|
|
23
|
+
# Not committed: this is an application installed from source, not a
|
|
24
|
+
# reproducible deployment. Pinning here would freeze contributors to one
|
|
25
|
+
# resolution of the dependency tree.
|
|
26
|
+
uv.lock
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.0] - 2026-07-27
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Households.** Declare people with `blindgrid player add`, or `[[player]]`
|
|
14
|
+
blocks in the config, and `generate` plans for everyone in one pass. Each
|
|
15
|
+
person keeps their own ceiling and their own lotteries, with their own
|
|
16
|
+
weights; nobody's budget affects anyone else's share, and there is
|
|
17
|
+
deliberately no household cap.
|
|
18
|
+
- Draws are spread across dates so the same lottery is not played twice on the
|
|
19
|
+
same day while free dates remain. This does **not** improve anyone's odds —
|
|
20
|
+
two grids are two independent chances either way — and the documentation
|
|
21
|
+
says so plainly. When dates run short they are shared rather than grids
|
|
22
|
+
dropped, and the output reports it.
|
|
23
|
+
- `player add`, `player list` and `player remove`.
|
|
24
|
+
- **Windows support.** `install.ps1` mirrors the Unix installer, and
|
|
25
|
+
configuration and state follow `%APPDATA%` / `%LOCALAPPDATA%` on Windows
|
|
26
|
+
while keeping XDG elsewhere. CI now runs the test suite on Linux, macOS and
|
|
27
|
+
Windows, and exercises both installers end to end on each.
|
|
28
|
+
- Past draws are struck through when a plan is shown again, so what is left to
|
|
29
|
+
play stands out.
|
|
30
|
+
- **Translations.** The interface is available in English, French, Spanish and
|
|
31
|
+
German, chosen with `--lang`, `BLINDGRID_LANG`, the config file or your
|
|
32
|
+
system locale. Tests enforce that every catalogue matches the English one
|
|
33
|
+
key for key and placeholder for placeholder. Terms follow each country's own
|
|
34
|
+
vocabulary: a played grid is an *apuesta* in Spanish, a *Tipp* in German.
|
|
35
|
+
- **Published on PyPI**, so installing is `pip install blindgrid` rather than a
|
|
36
|
+
ninety-character URL. Releases run from a tag and refuse to publish unless
|
|
37
|
+
the tag matches the declared version, the changelog carries an entry, and the
|
|
38
|
+
built wheel installs and runs.
|
|
39
|
+
- A month is now drawn once. `generate` saves the plan to the state directory
|
|
40
|
+
and shows it again on later runs, so it can be found while the grids are
|
|
41
|
+
being filled in. `--force` draws a new one and states what it replaces.
|
|
42
|
+
- `config show` reports where the current plan lives and when it was drawn.
|
|
43
|
+
- **Runs on an iPad.** `install.py` installs anywhere Python exists, which is
|
|
44
|
+
what iOS needs: a-Shell has neither bash nor git, so it pulls a source
|
|
45
|
+
archive rather than a git URL. `python -m blindgrid` works where a
|
|
46
|
+
pip-installed command does not reach the PATH.
|
|
47
|
+
- The plan lays itself out for narrow terminals. Below the width the table
|
|
48
|
+
needs, each draw becomes two lines with its numbers unwrapped on their own —
|
|
49
|
+
at 60 columns the table used to stack them one digit per line and cut the
|
|
50
|
+
lottery name to `Eur…`. `--compact` and `--table` force either layout.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- Number columns line up across the whole plan. Lotteries of different shapes
|
|
55
|
+
used to leave the separator and pool names drifting from row to row, which
|
|
56
|
+
is exactly the wrong thing when numbers are being copied onto a slip.
|
|
57
|
+
Weights share one precision so their decimal points align.
|
|
58
|
+
- Amounts accept the way people type them: `30 €`, `30€`, `30 EUR`, commas and
|
|
59
|
+
non-breaking spaces. Nonsense is still refused.
|
|
60
|
+
- `--lang` is accepted before the command as well as after it.
|
|
61
|
+
- The stored plan is one file, replaced when the month turns, and a
|
|
62
|
+
self-contained snapshot: editing the configuration afterwards does not alter
|
|
63
|
+
a plan already drawn. This narrows the original "no persistence" rule to
|
|
64
|
+
what it was protecting against — an accumulated history of past grids, which
|
|
65
|
+
is still out of scope — while removing the ability to reroll a month until
|
|
66
|
+
the numbers look right.
|
|
67
|
+
|
|
68
|
+
### Fixed
|
|
69
|
+
|
|
70
|
+
- Asked to prompt with no terminal attached — a pipe, a cron job, one of the
|
|
71
|
+
iOS shells — the tool now says which option to use instead, rather than
|
|
72
|
+
raising a bare `OSError` from inside prompt_toolkit.
|
|
73
|
+
- The help screen no longer mixed languages: Typer takes a command's docstring
|
|
74
|
+
as its help text unless told otherwise, which left the top-level commands in
|
|
75
|
+
English while the sub-apps were translated.
|
|
76
|
+
- The message shown when no configuration exists yet — the first thing a new
|
|
77
|
+
user sees — is translated.
|
|
78
|
+
- A language given before the command is no longer overwritten when the
|
|
79
|
+
configuration is read.
|
|
80
|
+
|
|
81
|
+
## [0.1.0] - 2026-07-25
|
|
82
|
+
|
|
83
|
+
First public release.
|
|
84
|
+
|
|
85
|
+
### Added
|
|
86
|
+
|
|
87
|
+
- `generate` — interactive monthly planning: prompts for a budget and a set of
|
|
88
|
+
lotteries, allocates the money by weight, picks draw dates at random and
|
|
89
|
+
produces one grid per selected draw.
|
|
90
|
+
- Weighted budget allocation with a hard ceiling that has no override. Shares
|
|
91
|
+
round down, leftovers stay unspent, and a share too small for one grid skips
|
|
92
|
+
that lottery with an explicit note rather than borrowing from another.
|
|
93
|
+
- Draw planner enumerating real calendar dates, capped by the draws actually
|
|
94
|
+
remaining in the period.
|
|
95
|
+
- Grid generation from `secrets.SystemRandom` with rejection sampling against
|
|
96
|
+
five anti-pattern filters, each expressed relative to pool size and each
|
|
97
|
+
switching itself off on pools too small to satisfy it.
|
|
98
|
+
- `rich` table output and a Markdown export, overwritten on every run.
|
|
99
|
+
- `config init` / `config show` / `config edit` and `lottery add` /
|
|
100
|
+
`lottery list`.
|
|
101
|
+
- TOML configuration describing lotteries generically: any game in any country
|
|
102
|
+
is a matter of price, draw days and pools of numbers.
|
|
103
|
+
- Test suite covering allocation, planning, filters, generation, config and
|
|
104
|
+
the CLI, including a contract test asserting that `random` is never imported
|
|
105
|
+
by the package.
|
|
106
|
+
|
|
107
|
+
[Unreleased]: https://github.com/adrnbttr/blindgrid/compare/v0.2.0...HEAD
|
|
108
|
+
[0.2.0]: https://github.com/adrnbttr/blindgrid/compare/v0.1.0...v0.2.0
|
|
109
|
+
[0.1.0]: https://github.com/adrnbttr/blindgrid/releases/tag/v0.1.0
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for taking a look. This is a small project with a narrow purpose, so
|
|
4
|
+
the most useful thing to read first is the section below on what will not be
|
|
5
|
+
merged — it is not a long list, but it is a firm one.
|
|
6
|
+
|
|
7
|
+
## Out of scope, permanently
|
|
8
|
+
|
|
9
|
+
The README explains the reasoning; this is the short version.
|
|
10
|
+
|
|
11
|
+
- **Anything predictive.** Frequency analysis, hot/cold numbers, historical
|
|
12
|
+
draw data, "due" numbers, statistical models of past results. Lottery draws
|
|
13
|
+
are independent events. A feature that implies otherwise is a bug in the
|
|
14
|
+
product, not a missing capability.
|
|
15
|
+
- **Seeding or reproducible output.** No `--seed`, no deterministic mode, no
|
|
16
|
+
fixed generator in the package. A test enforces this.
|
|
17
|
+
- **A history of past months.** The current plan is kept in one file so it can
|
|
18
|
+
be found again, and replaced when the month turns. Accumulating months is a
|
|
19
|
+
different thing: it makes past grids comparable to results, which is where
|
|
20
|
+
pattern-hunting starts.
|
|
21
|
+
- **Consuming the budget exactly.** Leftover money stays unspent.
|
|
22
|
+
- **Hardcoding a country or a game.** Lotteries live in configuration. If
|
|
23
|
+
something cannot be expressed as a price, a set of draw days and pools of
|
|
24
|
+
numbers, open an issue and let's discuss the model rather than special-case
|
|
25
|
+
the code.
|
|
26
|
+
- **Claiming that spreading a household's draws improves anyone's odds.** It
|
|
27
|
+
does not. Two grids are two independent chances whether they sit on one draw
|
|
28
|
+
or two. Spreading buys exposure to more distinct jackpots and avoids
|
|
29
|
+
near-duplicate tickets in one house; the wording in the code and docs says
|
|
30
|
+
exactly that, and should stay that way.
|
|
31
|
+
|
|
32
|
+
Everything else is fair game: better rendering, clearer errors, more
|
|
33
|
+
platforms, faster tests, documentation.
|
|
34
|
+
|
|
35
|
+
## Setup
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://github.com/adrnbttr/blindgrid.git
|
|
39
|
+
cd blindgrid
|
|
40
|
+
uv venv
|
|
41
|
+
uv pip install -e ".[dev]"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Before opening a pull request
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
ruff check .
|
|
48
|
+
ruff format .
|
|
49
|
+
pytest
|
|
50
|
+
shellcheck install.sh # only if you touched the Unix installer
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
CI runs the same commands on Linux (3.11, 3.12, 3.13), macOS and Windows, and
|
|
54
|
+
a pull request needs them green. It also installs the project with
|
|
55
|
+
`install.sh` and `install.ps1` on clean runners and uninstalls them again, so
|
|
56
|
+
a change to either is exercised for real rather than merely linted.
|
|
57
|
+
|
|
58
|
+
If you touched `install.ps1` and have no Windows machine, push and let CI run
|
|
59
|
+
PSScriptAnalyzer and the install-uninstall round trip — that is how it is
|
|
60
|
+
maintained here. The same goes for `install.py`, which exists for iOS: no CI
|
|
61
|
+
runner is an iPad, so it is checked on a bare interpreter, at 55 columns, and
|
|
62
|
+
with no terminal attached.
|
|
63
|
+
|
|
64
|
+
### Platform-specific code
|
|
65
|
+
|
|
66
|
+
There is exactly one place that branches on the operating system:
|
|
67
|
+
`src/blindgrid/paths.py`, which decides where configuration and state live.
|
|
68
|
+
Everything else is platform-neutral, and should stay that way. `tests/
|
|
69
|
+
test_paths.py` fakes each platform, so both layouts are checked wherever the
|
|
70
|
+
suite runs.
|
|
71
|
+
|
|
72
|
+
## Conventions
|
|
73
|
+
|
|
74
|
+
- **Commits** follow [Conventional Commits](https://www.conventionalcommits.org):
|
|
75
|
+
`feat:`, `fix:`, `docs:`, `test:`, `refactor:`, `chore:`, `ci:`.
|
|
76
|
+
- **Line length** is 100 characters, enforced by ruff.
|
|
77
|
+
- **Type annotations** are required on new functions; `ANN` rules are on.
|
|
78
|
+
- **Money** is `Decimal`, never `float`. Budget arithmetic in binary floating
|
|
79
|
+
point drifts, and the whole point of the ceiling is that it does not move.
|
|
80
|
+
- **Randomness** comes from the `RandomSource` protocol, and production code
|
|
81
|
+
passes `secrets.SystemRandom`. Do not import `random` inside the package.
|
|
82
|
+
|
|
83
|
+
## Adding an anti-pattern filter
|
|
84
|
+
|
|
85
|
+
Think twice. Every active rule shrinks the sample space, and a heavily
|
|
86
|
+
filtered draw is a predictable draw. If a rule is still worth it:
|
|
87
|
+
|
|
88
|
+
1. Add the predicate functions and a `Rule` entry in `src/blindgrid/filters.py`.
|
|
89
|
+
2. Give it an `applies_to` that switches it off for pools too small to satisfy
|
|
90
|
+
it, expressed relative to `count` and `maximum` — never against a specific
|
|
91
|
+
lottery's numbers.
|
|
92
|
+
3. Add it to the `[filters]` block in `config.example.toml`.
|
|
93
|
+
4. Cover both the rejection and the degradation in `tests/test_filters.py`.
|
|
94
|
+
|
|
95
|
+
## Adding a language
|
|
96
|
+
|
|
97
|
+
One file in `src/blindgrid/i18n/`, copied from `en.py` and translated, plus one
|
|
98
|
+
line in that package's `__init__.py` and one entry in `LANGUAGE_NAMES`.
|
|
99
|
+
|
|
100
|
+
Tests then check that your catalogue carries exactly the English keys, with
|
|
101
|
+
exactly the same `{placeholders}`, and that nothing longer than three words was
|
|
102
|
+
left in English. A half-finished translation fails the build rather than
|
|
103
|
+
leaking English into someone's session.
|
|
104
|
+
|
|
105
|
+
Translate the interface only. Lottery labels, pool names and player names come
|
|
106
|
+
from configuration, and the weekday keys in the config file (`monday`, …) are
|
|
107
|
+
a file format rather than text for reading — both stay as they are.
|
|
108
|
+
|
|
109
|
+
## Regenerating the README image
|
|
110
|
+
|
|
111
|
+
`docs/demo.gif` is recorded from a real run, not drawn by hand. It needs
|
|
112
|
+
[vhs](https://github.com/charmbracelet/vhs):
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
brew install vhs # or see the vhs README for other platforms
|
|
116
|
+
vhs docs/demo.tape # from the repository root
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The tape points the tool at a throwaway config in a temporary directory, so
|
|
120
|
+
recording never touches your own `config.toml`. Regenerate after any change to
|
|
121
|
+
`render.py`, and expect different numbers every time — the demo draws from the
|
|
122
|
+
real CSPRNG like everything else.
|
|
123
|
+
|
|
124
|
+
Keep it in English and keep it short. Switching languages mid-recording spends
|
|
125
|
+
five seconds saying what one line of the README says better, and every second
|
|
126
|
+
is weight on a page people load before deciding whether to read it.
|
blindgrid-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Adrien Bouttier
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|