matamata 0.0.1__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.
Files changed (56) hide show
  1. matamata-0.0.1/.github/workflows/ci.yml +28 -0
  2. matamata-0.0.1/.gitignore +47 -0
  3. matamata-0.0.1/.pylintrc +15 -0
  4. matamata-0.0.1/LICENSE +21 -0
  5. matamata-0.0.1/PKG-INFO +149 -0
  6. matamata-0.0.1/README.md +126 -0
  7. matamata-0.0.1/docs/apply-after.json +57 -0
  8. matamata-0.0.1/docs/apply-after.png +0 -0
  9. matamata-0.0.1/docs/apply-before.json +41 -0
  10. matamata-0.0.1/docs/apply-before.png +0 -0
  11. matamata-0.0.1/docs/copa-rio-de-la-plata.png +0 -0
  12. matamata-0.0.1/docs/facup-drawn.png +0 -0
  13. matamata-0.0.1/docs/facup-pending-draw.png +0 -0
  14. matamata-0.0.1/docs/flags.png +0 -0
  15. matamata-0.0.1/docs/format.md +194 -0
  16. matamata-0.0.1/docs/index.md +417 -0
  17. matamata-0.0.1/docs/knockout-8-table.png +0 -0
  18. matamata-0.0.1/docs/knockout-8.png +0 -0
  19. matamata-0.0.1/docs/libertadores-2026.png +0 -0
  20. matamata-0.0.1/docs/schema.json +89 -0
  21. matamata-0.0.1/examples/copa-rio-de-la-plata-2026.json +63 -0
  22. matamata-0.0.1/examples/copa_rio_host.py +61 -0
  23. matamata-0.0.1/examples/crest_data.json +6 -0
  24. matamata-0.0.1/examples/crests/athletic.png +0 -0
  25. matamata-0.0.1/examples/crests/estudiantes.svg +38 -0
  26. matamata-0.0.1/examples/crests/internacional.svg +112 -0
  27. matamata-0.0.1/examples/crests/platense.svg +68 -0
  28. matamata-0.0.1/examples/example_data.json +4 -0
  29. matamata-0.0.1/examples/facup-drawn.json +65 -0
  30. matamata-0.0.1/examples/facup-pending-draw.json +52 -0
  31. matamata-0.0.1/examples/knockout-8.json +68 -0
  32. matamata-0.0.1/examples/libertadores-2026.json +112 -0
  33. matamata-0.0.1/examples/libertadores_host.py +50 -0
  34. matamata-0.0.1/lint.sh +16 -0
  35. matamata-0.0.1/mkdocs.yml +14 -0
  36. matamata-0.0.1/pyproject.toml +38 -0
  37. matamata-0.0.1/src/matamata/__init__.py +22 -0
  38. matamata-0.0.1/src/matamata/__main__.py +66 -0
  39. matamata-0.0.1/src/matamata/diagram.py +302 -0
  40. matamata-0.0.1/src/matamata/layout.py +154 -0
  41. matamata-0.0.1/src/matamata/model.py +181 -0
  42. matamata-0.0.1/src/matamata/parse.py +196 -0
  43. matamata-0.0.1/src/matamata/render.py +127 -0
  44. matamata-0.0.1/src/matamata/render_html.py +70 -0
  45. matamata-0.0.1/tests/conftest.py +29 -0
  46. matamata-0.0.1/tests/golden/facup-drawn.html +109 -0
  47. matamata-0.0.1/tests/golden/facup-drawn.svg +85 -0
  48. matamata-0.0.1/tests/golden/facup-pending-draw.html +109 -0
  49. matamata-0.0.1/tests/golden/facup-pending-draw.svg +83 -0
  50. matamata-0.0.1/tests/golden/knockout-8.html +109 -0
  51. matamata-0.0.1/tests/golden/knockout-8.svg +87 -0
  52. matamata-0.0.1/tests/golden/libertadores-2026.html +109 -0
  53. matamata-0.0.1/tests/golden/libertadores-2026.svg +95 -0
  54. matamata-0.0.1/tests/test_apply.py +255 -0
  55. matamata-0.0.1/tests/test_model.py +526 -0
  56. matamata-0.0.1/tests/test_render.py +151 -0
@@ -0,0 +1,28 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install
25
+ run: pip install -e ".[dev]"
26
+
27
+ - name: Run tests
28
+ run: pytest -q
@@ -0,0 +1,47 @@
1
+ # Byte-compiled / optimized files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ .eggs/
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+ env/
16
+
17
+ # Test / tooling caches
18
+ .pytest_cache/
19
+ .ruff_cache/
20
+ .mypy_cache/
21
+ .coverage
22
+ htmlcov/
23
+
24
+ # Editor / OS
25
+ .idea/
26
+ .vscode/
27
+ *.swp
28
+ .DS_Store
29
+
30
+ # Generated diagrams: ignore stray renders, but keep the curated docs images
31
+ # (used by the README), the versioned test goldens, and the example crest assets.
32
+ *.svg
33
+ *.png
34
+ !docs/*.png
35
+ !tests/golden/*.svg
36
+ !examples/crests/*.svg
37
+ !examples/crests/*.png
38
+
39
+ # MkDocs build output (`mkdocs serve` / `mkdocs build`)
40
+ /site/
41
+
42
+ # Local, unversioned scratch space (personal notes, drafts — never committed)
43
+ /.local/
44
+
45
+ # Agent guidance and settings: machine- and workflow-specific, kept local only
46
+ /CLAUDE.md
47
+ /.claude/
@@ -0,0 +1,15 @@
1
+ [MASTER]
2
+
3
+ disable=
4
+ C0114,
5
+ C0115,
6
+ C0116,
7
+ C0415,
8
+ W0718,
9
+ W0511,
10
+ R0903,
11
+ R0801,
12
+ R0913,
13
+ R0914,
14
+ R0917,
15
+
matamata-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aníbal Pacheco
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.
@@ -0,0 +1,149 @@
1
+ Metadata-Version: 2.4
2
+ Name: matamata
3
+ Version: 0.0.1
4
+ Summary: Model tournament knockout stages in JSON format and render the schedule in SVG or html table format
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Topic :: Multimedia :: Graphics
11
+ Requires-Python: >=3.10
12
+ Provides-Extra: dev
13
+ Requires-Dist: jsonschema; extra == 'dev'
14
+ Requires-Dist: mkdocs; extra == 'dev'
15
+ Requires-Dist: pytest; extra == 'dev'
16
+ Provides-Extra: lint
17
+ Requires-Dist: black; extra == 'lint'
18
+ Requires-Dist: isort; extra == 'lint'
19
+ Requires-Dist: mypy; extra == 'lint'
20
+ Requires-Dist: pylint; extra == 'lint'
21
+ Requires-Dist: types-jsonschema; extra == 'lint'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # matamata
25
+
26
+ [![CI](https://github.com/anibalpacheco/matamata/actions/workflows/ci.yml/badge.svg)](https://github.com/anibalpacheco/matamata/actions/workflows/ci.yml)
27
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
28
+
29
+ Model a **tournament knockout stage** in a small **JSON "language"** and render
30
+ the schedule in **SVG** or **HTML table** format.
31
+
32
+ **matamata** also lets a host system map documents representing championship
33
+ knockout stages onto its own business objects (e.g. a Championship or Cup entity) and persist
34
+ them apart from any presentation concern — updating results means editing a document,
35
+ never the code.
36
+
37
+ Example rendered from
38
+ [`examples/libertadores-2026.json`](examples/libertadores-2026.json):
39
+
40
+ ![Copa Libertadores 2026 knockout stage](docs/libertadores-2026.png)
41
+
42
+ ## Quickstart
43
+
44
+ Requires Python ≥ 3.10. No runtime dependencies.
45
+
46
+ ```bash
47
+ git clone https://github.com/anibalpacheco/matamata.git
48
+ cd matamata
49
+ python -m venv .venv && source .venv/bin/activate
50
+ pip install -e .
51
+ ```
52
+
53
+ Render a self-contained example to an SVG file:
54
+
55
+ ```bash
56
+ # via the installed command
57
+ matamata examples/knockout-8.json -o knockout.svg
58
+
59
+ # or via the module, writing to stdout
60
+ python -m matamata examples/knockout-8.json > knockout.svg
61
+
62
+ # or as an HTML table, the layout for small screens
63
+ matamata examples/knockout-8.json -o knockout.html
64
+ ```
65
+
66
+ Open the resulting file in a browser to view the schedule. To render your own cup,
67
+ point the command at any JSON file that follows [`docs/format.md`](docs/format.md).
68
+
69
+ Use it from Python:
70
+
71
+ ```python
72
+ from matamata import load_stage, render_svg
73
+
74
+ svg = render_svg(load_stage("examples/knockout-8.json"))
75
+ ```
76
+
77
+ To use it in your own project instead, install straight from GitHub:
78
+
79
+ ```bash
80
+ pip install git+https://github.com/anibalpacheco/matamata.git
81
+ ```
82
+
83
+ ## Examples
84
+
85
+ Both examples are rendered from the JSON files in [`examples/`](examples/).
86
+
87
+ The Copa Libertadores example above shows two-legged ties — each leg's goals are
88
+ shown, shootouts appear in parentheses, and the winner of each tie is emphasized. The
89
+ first quarterfinal is **host-resolved**: its legs carry only a `ref`, so its teams and
90
+ scores come from `get_match` (see
91
+ [`examples/libertadores_host.py`](examples/libertadores_host.py)) rather than from the
92
+ document. Played ties take their team names from the legs; the final is a single match —
93
+ one leg, so just one goal figure per side — with its `winnerof` links wiring the
94
+ advancement tree.
95
+
96
+ Single matches ([`knockout-8.json`](examples/knockout-8.json)) — one goal figure per
97
+ side, with shootouts in parentheses. Sides that have not been resolved yet fall back to
98
+ placeholders such as "Winner SF2":
99
+
100
+ ![World Cup knockout stage](docs/knockout-8.png)
101
+
102
+ ## The format
103
+
104
+ The document is plain JSON, so any system can store and exchange it natively, and the
105
+ language is designed so a match can evolve from a **placeholder** (e.g. "winner
106
+ of QF1") into a **reference to a real match entity** — each leg can carry a `ref` to
107
+ the real game, resolved dynamically by the host — without changing the language. The
108
+ advancement tree is laid out **deterministically**: coordinates are
109
+ computed directly and the SVG is emitted as a string, with no layout engine and no
110
+ heavy dependencies.
111
+
112
+ See [`docs/format.md`](docs/format.md) for the full specification and
113
+ [`docs/schema.json`](docs/schema.json) for the JSON Schema. Worked examples live in
114
+ [`examples/`](examples/).
115
+
116
+ Minimal example:
117
+
118
+ ```json
119
+ {
120
+ "tournament": "Copa Libertadores",
121
+ "season": "2026",
122
+ "rounds": [
123
+ {
124
+ "name": "Final",
125
+ "matches": [
126
+ {
127
+ "id": "final",
128
+ "legs": [
129
+ { "team1": "Flamengo", "goals1": 1, "team2": "Nacional", "goals2": 2 }
130
+ ],
131
+ "winner": 2
132
+ }
133
+ ]
134
+ }
135
+ ]
136
+ }
137
+ ```
138
+
139
+ ## Documentation
140
+
141
+ [docs/index.md](docs/index.md) is the manual: rendering from the CLI and from Python
142
+ (e.g. a Django view), feeding live data from your own database through
143
+ `KnockoutStage`, updating the stored document with `apply_results` — including a
144
+ before/after walkthrough of one call — and running the test suite.
145
+
146
+ ## Status
147
+
148
+ Working: the language spec, JSON Schema, Python renderer, CLI and tests are in place,
149
+ and the package is installable from GitHub.
@@ -0,0 +1,126 @@
1
+ # matamata
2
+
3
+ [![CI](https://github.com/anibalpacheco/matamata/actions/workflows/ci.yml/badge.svg)](https://github.com/anibalpacheco/matamata/actions/workflows/ci.yml)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
+
6
+ Model a **tournament knockout stage** in a small **JSON "language"** and render
7
+ the schedule in **SVG** or **HTML table** format.
8
+
9
+ **matamata** also lets a host system map documents representing championship
10
+ knockout stages onto its own business objects (e.g. a Championship or Cup entity) and persist
11
+ them apart from any presentation concern — updating results means editing a document,
12
+ never the code.
13
+
14
+ Example rendered from
15
+ [`examples/libertadores-2026.json`](examples/libertadores-2026.json):
16
+
17
+ ![Copa Libertadores 2026 knockout stage](docs/libertadores-2026.png)
18
+
19
+ ## Quickstart
20
+
21
+ Requires Python ≥ 3.10. No runtime dependencies.
22
+
23
+ ```bash
24
+ git clone https://github.com/anibalpacheco/matamata.git
25
+ cd matamata
26
+ python -m venv .venv && source .venv/bin/activate
27
+ pip install -e .
28
+ ```
29
+
30
+ Render a self-contained example to an SVG file:
31
+
32
+ ```bash
33
+ # via the installed command
34
+ matamata examples/knockout-8.json -o knockout.svg
35
+
36
+ # or via the module, writing to stdout
37
+ python -m matamata examples/knockout-8.json > knockout.svg
38
+
39
+ # or as an HTML table, the layout for small screens
40
+ matamata examples/knockout-8.json -o knockout.html
41
+ ```
42
+
43
+ Open the resulting file in a browser to view the schedule. To render your own cup,
44
+ point the command at any JSON file that follows [`docs/format.md`](docs/format.md).
45
+
46
+ Use it from Python:
47
+
48
+ ```python
49
+ from matamata import load_stage, render_svg
50
+
51
+ svg = render_svg(load_stage("examples/knockout-8.json"))
52
+ ```
53
+
54
+ To use it in your own project instead, install straight from GitHub:
55
+
56
+ ```bash
57
+ pip install git+https://github.com/anibalpacheco/matamata.git
58
+ ```
59
+
60
+ ## Examples
61
+
62
+ Both examples are rendered from the JSON files in [`examples/`](examples/).
63
+
64
+ The Copa Libertadores example above shows two-legged ties — each leg's goals are
65
+ shown, shootouts appear in parentheses, and the winner of each tie is emphasized. The
66
+ first quarterfinal is **host-resolved**: its legs carry only a `ref`, so its teams and
67
+ scores come from `get_match` (see
68
+ [`examples/libertadores_host.py`](examples/libertadores_host.py)) rather than from the
69
+ document. Played ties take their team names from the legs; the final is a single match —
70
+ one leg, so just one goal figure per side — with its `winnerof` links wiring the
71
+ advancement tree.
72
+
73
+ Single matches ([`knockout-8.json`](examples/knockout-8.json)) — one goal figure per
74
+ side, with shootouts in parentheses. Sides that have not been resolved yet fall back to
75
+ placeholders such as "Winner SF2":
76
+
77
+ ![World Cup knockout stage](docs/knockout-8.png)
78
+
79
+ ## The format
80
+
81
+ The document is plain JSON, so any system can store and exchange it natively, and the
82
+ language is designed so a match can evolve from a **placeholder** (e.g. "winner
83
+ of QF1") into a **reference to a real match entity** — each leg can carry a `ref` to
84
+ the real game, resolved dynamically by the host — without changing the language. The
85
+ advancement tree is laid out **deterministically**: coordinates are
86
+ computed directly and the SVG is emitted as a string, with no layout engine and no
87
+ heavy dependencies.
88
+
89
+ See [`docs/format.md`](docs/format.md) for the full specification and
90
+ [`docs/schema.json`](docs/schema.json) for the JSON Schema. Worked examples live in
91
+ [`examples/`](examples/).
92
+
93
+ Minimal example:
94
+
95
+ ```json
96
+ {
97
+ "tournament": "Copa Libertadores",
98
+ "season": "2026",
99
+ "rounds": [
100
+ {
101
+ "name": "Final",
102
+ "matches": [
103
+ {
104
+ "id": "final",
105
+ "legs": [
106
+ { "team1": "Flamengo", "goals1": 1, "team2": "Nacional", "goals2": 2 }
107
+ ],
108
+ "winner": 2
109
+ }
110
+ ]
111
+ }
112
+ ]
113
+ }
114
+ ```
115
+
116
+ ## Documentation
117
+
118
+ [docs/index.md](docs/index.md) is the manual: rendering from the CLI and from Python
119
+ (e.g. a Django view), feeding live data from your own database through
120
+ `KnockoutStage`, updating the stored document with `apply_results` — including a
121
+ before/after walkthrough of one call — and running the test suite.
122
+
123
+ ## Status
124
+
125
+ Working: the language spec, JSON Schema, Python renderer, CLI and tests are in place,
126
+ and the package is installable from GitHub.
@@ -0,0 +1,57 @@
1
+ {
2
+ "tournament": "Champions League",
3
+ "season": "2025/26",
4
+ "rounds": [
5
+ {
6
+ "name": "Semifinals",
7
+ "matches": [
8
+ {
9
+ "id": "sf1",
10
+ "team1": "Real Madrid",
11
+ "team2": "Barcelona",
12
+ "legs": [
13
+ {
14
+ "goals1": 1,
15
+ "goals2": 0
16
+ },
17
+ {
18
+ "goals1": 0,
19
+ "goals2": 1,
20
+ "pen1": 4,
21
+ "pen2": 2
22
+ }
23
+ ],
24
+ "winner": 1
25
+ },
26
+ {
27
+ "id": "sf2",
28
+ "team1": "Bayern München",
29
+ "team2": "Manchester City",
30
+ "winner": 2,
31
+ "legs": [
32
+ {
33
+ "goals1": 0,
34
+ "goals2": 2
35
+ },
36
+ {
37
+ "goals1": 1,
38
+ "goals2": 1
39
+ }
40
+ ]
41
+ }
42
+ ]
43
+ },
44
+ {
45
+ "name": "Final",
46
+ "matches": [
47
+ {
48
+ "id": "f",
49
+ "winnerof1": "sf1",
50
+ "winnerof2": "sf2",
51
+ "team2": "Manchester City",
52
+ "team1": "Real Madrid"
53
+ }
54
+ ]
55
+ }
56
+ ]
57
+ }
Binary file
@@ -0,0 +1,41 @@
1
+ {
2
+ "tournament": "Champions League",
3
+ "season": "2025/26",
4
+ "rounds": [
5
+ {
6
+ "name": "Semifinals",
7
+ "matches": [
8
+ {
9
+ "id": "sf1",
10
+ "team1": "Real Madrid",
11
+ "team2": "Barcelona",
12
+ "legs": [
13
+ { "goals1": 1, "goals2": 0 },
14
+ {}
15
+ ]
16
+ },
17
+ {
18
+ "id": "sf2",
19
+ "team1": "Bayern München",
20
+ "team2": "Manchester City",
21
+ "winner": 2,
22
+ "legs": [
23
+ { "goals1": 0, "goals2": 2 },
24
+ { "goals1": 1, "goals2": 1 }
25
+ ]
26
+ }
27
+ ]
28
+ },
29
+ {
30
+ "name": "Final",
31
+ "matches": [
32
+ {
33
+ "id": "f",
34
+ "winnerof1": "sf1",
35
+ "winnerof2": "sf2",
36
+ "team2": "Manchester City"
37
+ }
38
+ ]
39
+ }
40
+ ]
41
+ }
Binary file
Binary file
Binary file