weakness-report 0.1.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.
- weakness_report-0.1.0/LICENSE +21 -0
- weakness_report-0.1.0/PKG-INFO +524 -0
- weakness_report-0.1.0/README.md +483 -0
- weakness_report-0.1.0/pyproject.toml +64 -0
- weakness_report-0.1.0/setup.cfg +4 -0
- weakness_report-0.1.0/tests/test_weakness.py +586 -0
- weakness_report-0.1.0/weakness_report/__init__.py +3 -0
- weakness_report-0.1.0/weakness_report/aggregate.py +256 -0
- weakness_report-0.1.0/weakness_report/batch.py +303 -0
- weakness_report-0.1.0/weakness_report/board.py +78 -0
- weakness_report-0.1.0/weakness_report/bridge.py +203 -0
- weakness_report-0.1.0/weakness_report/buckets.py +288 -0
- weakness_report-0.1.0/weakness_report/cli.py +473 -0
- weakness_report-0.1.0/weakness_report/exportcsv.py +62 -0
- weakness_report-0.1.0/weakness_report/features.py +218 -0
- weakness_report-0.1.0/weakness_report/findings.py +218 -0
- weakness_report-0.1.0/weakness_report/jobs.py +153 -0
- weakness_report-0.1.0/weakness_report/paths.py +82 -0
- weakness_report-0.1.0/weakness_report/pdf.py +580 -0
- weakness_report-0.1.0/weakness_report/pipeline.py +160 -0
- weakness_report-0.1.0/weakness_report/report.py +142 -0
- weakness_report-0.1.0/weakness_report/server.py +380 -0
- weakness_report-0.1.0/weakness_report/sources.py +501 -0
- weakness_report-0.1.0/weakness_report/store.py +203 -0
- weakness_report-0.1.0/weakness_report/web/app.js +700 -0
- weakness_report-0.1.0/weakness_report/web/index.html +238 -0
- weakness_report-0.1.0/weakness_report/web/style.css +428 -0
- weakness_report-0.1.0/weakness_report.egg-info/PKG-INFO +524 -0
- weakness_report-0.1.0/weakness_report.egg-info/SOURCES.txt +31 -0
- weakness_report-0.1.0/weakness_report.egg-info/dependency_links.txt +1 -0
- weakness_report-0.1.0/weakness_report.egg-info/entry_points.txt +2 -0
- weakness_report-0.1.0/weakness_report.egg-info/requires.txt +16 -0
- weakness_report-0.1.0/weakness_report.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shubhro Dev
|
|
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,524 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: weakness-report
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Review your whole game history and find out what you are actually bad at.
|
|
5
|
+
Author-email: Shubhro Dev <shubhro2004@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/spearb0lt/Lichess-Essentials
|
|
8
|
+
Project-URL: Repository, https://github.com/spearb0lt/Lichess-Essentials
|
|
9
|
+
Project-URL: Issues, https://github.com/spearb0lt/Lichess-Essentials/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/spearb0lt/Lichess-Essentials/blob/main/Weakness-Report/README.md
|
|
11
|
+
Keywords: chess,lichess,statistics,weaknesses,report,game-review
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Environment :: Web Environment
|
|
15
|
+
Classifier: Framework :: FastAPI
|
|
16
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Games/Entertainment :: Board Games
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: chess>=1.11
|
|
28
|
+
Requires-Dist: requests>=2.31
|
|
29
|
+
Requires-Dist: reportlab>=4.0
|
|
30
|
+
Requires-Dist: fastapi>=0.110
|
|
31
|
+
Requires-Dist: uvicorn[standard]>=0.27
|
|
32
|
+
Requires-Dist: pydantic>=2.0
|
|
33
|
+
Requires-Dist: chess-game-analyzer>=0.1.0
|
|
34
|
+
Provides-Extra: review
|
|
35
|
+
Requires-Dist: chess-game-analyzer>=0.1.0; extra == "review"
|
|
36
|
+
Provides-Extra: diagrams
|
|
37
|
+
Requires-Dist: lichess-study-to-pdf>=0.1.0; extra == "diagrams"
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# Weakness Report
|
|
43
|
+
|
|
44
|
+
[](https://pypi.org/project/weakness-report/)
|
|
45
|
+
[](https://pypi.org/project/weakness-report/)
|
|
46
|
+
[](https://pepy.tech/project/weakness-report)
|
|
47
|
+
[](https://pepy.tech/project/weakness-report)
|
|
48
|
+
[](LICENSE)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
Review your whole game history and find out what you are actually bad at.
|
|
52
|
+
|
|
53
|
+
A single game review tells you what went wrong in that game. Four hundred of
|
|
54
|
+
them, sliced by the kind of position you were in, tell you what keeps going
|
|
55
|
+
wrong — which is the thing you can do something about:
|
|
56
|
+
|
|
57
|
+
> **When you are ahead** — 1.46 pawns a game worse than your average, over 664
|
|
58
|
+
> moves in 45 games (76 centipawns a move against your 63).
|
|
59
|
+
|
|
60
|
+
```powershell
|
|
61
|
+
cd Weakness-Report
|
|
62
|
+
& "..\.lichess\Scripts\python.exe" -m weakness_report.cli serve # port 8781
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Then open <http://127.0.0.1:8781>.
|
|
66
|
+
|
|
67
|
+

|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Install
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install weakness-report
|
|
75
|
+
weakness serve
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`chess-game-analyzer` is a real dependency and pip installs it with this one:
|
|
79
|
+
the review has to agree with that app. Board diagrams in the PDF are
|
|
80
|
+
optional: `pip install "weakness-report[diagrams]"`.
|
|
81
|
+
|
|
82
|
+
Or take all five at once with `pip install lichess-essentials`. Installed this
|
|
83
|
+
way your files live in the usual per-user folder for your platform, and the
|
|
84
|
+
app prints the path in its startup banner. To run it from a checkout instead,
|
|
85
|
+
see [the repository README](https://github.com/spearb0lt/Lichess-Essentials/blob/main/README.md#setup-from-a-checkout).
|
|
86
|
+
|
|
87
|
+
## The number the whole thing rests on
|
|
88
|
+
|
|
89
|
+
Three ways of ranking a slice, and only one of them answers "what should I
|
|
90
|
+
work on".
|
|
91
|
+
|
|
92
|
+
| Rank by | What you get |
|
|
93
|
+
|---|---|
|
|
94
|
+
| **Rate** (bucket ACPL) | Whichever bucket is smallest. Eleven moves in opposite-side castling at 300 centipawns beats everything, for ever. |
|
|
95
|
+
| **Total loss** (centipawns given away) | The middlegame, every time, because that is where most of your moves are. It is true and it is useless. |
|
|
96
|
+
| **Excess loss** | `moves × (bucket ACPL − your overall ACPL)`. How much a kind of position costs you **beyond what you cost yourself anyway**. |
|
|
97
|
+
|
|
98
|
+
Excess is the one this report ranks by, because it multiplies *how bad you are
|
|
99
|
+
at something* by *how often it happens to you*. Divided by the number of
|
|
100
|
+
games, it becomes the sentence you actually want: **0.4 pawns a game more than
|
|
101
|
+
your average, in queenless middlegames**.
|
|
102
|
+
|
|
103
|
+
Two guards sit in front of it:
|
|
104
|
+
|
|
105
|
+
- **A sample floor.** Nothing is claimed below 40 counted moves across 5
|
|
106
|
+
games. Buckets under the floor stay in the tables, with their counts, so you
|
|
107
|
+
can see what was set aside. Both numbers are adjustable in the browser, and
|
|
108
|
+
changing them costs no engine time at all.
|
|
109
|
+
- **Deduplication by moves, not by names.** "Middlegame", "queens off" and
|
|
110
|
+
"queenless middlegames" can be the same two hundred moves; ranked naively
|
|
111
|
+
your top three findings are one finding three times. Findings are chosen
|
|
112
|
+
greedily and anything that shares most of its *actual move set* with
|
|
113
|
+
something already said is dropped. The comparison is Jaccard similarity, not
|
|
114
|
+
containment — containment would throw away every specific finding in favour
|
|
115
|
+
of the vague one that happens to contain it.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Where the review comes from
|
|
120
|
+
|
|
121
|
+
**There is one implementation of "was that a blunder", and it is not in this
|
|
122
|
+
app.** Accuracy, the move labels, where the opening stops and the middlegame
|
|
123
|
+
starts, the engine pool — all of that is ChessAnalyzer's, tuned and written
|
|
124
|
+
down in that app's README. This app aggregates it.
|
|
125
|
+
|
|
126
|
+
That is a deliberate choice, and it is the reason a figure here means exactly
|
|
127
|
+
what the same figure means over there. A second implementation would drift,
|
|
128
|
+
and a report whose numbers disagree with the app you check them in is worse
|
|
129
|
+
than no report.
|
|
130
|
+
|
|
131
|
+
It is found automatically, in this order:
|
|
132
|
+
|
|
133
|
+
1. `chess_analyzer` already installed (`pip install -e ChessAnalyzer`)
|
|
134
|
+
2. `$CHESS_ANALYZER_DIR`, for a checkout somewhere unusual
|
|
135
|
+
3. **the sibling folder in this repository**, added to `sys.path` — the same
|
|
136
|
+
trick ChessAnalyzer's own test suite uses on itself
|
|
137
|
+
|
|
138
|
+
Because of (3) a normal checkout needs no install at all. The startup banner
|
|
139
|
+
says which route it took. If it cannot be found, the app says so plainly and
|
|
140
|
+
refuses to invent a second opinion.
|
|
141
|
+
|
|
142
|
+
There is a test — `test_our_accuracy_matches_the_analyzers_exactly` — that
|
|
143
|
+
fails the moment the two could disagree.
|
|
144
|
+
|
|
145
|
+
### What this app does change
|
|
146
|
+
|
|
147
|
+
Two settings, both about making a report you can **run again in three months
|
|
148
|
+
and believe the difference**:
|
|
149
|
+
|
|
150
|
+
- **Fixed depth, not movetime.** ChessAnalyzer's presets are movetime budgets,
|
|
151
|
+
which is right for one game you are reading move by move — that app says so
|
|
152
|
+
itself about its own presets. It is wrong for a number averaged over four
|
|
153
|
+
hundred games, because every figure would then depend on how busy the
|
|
154
|
+
machine was. Batches here search to a fixed depth.
|
|
155
|
+
- **One engine thread by default.** Stockfish splits its search across threads
|
|
156
|
+
and they do not always finish in the same order, so multi-threaded search is
|
|
157
|
+
non-deterministic *even at a fixed depth*. One thread is slower and gives the
|
|
158
|
+
same answer twice.
|
|
159
|
+
|
|
160
|
+
Both are settings; trade either away for speed if you would rather.
|
|
161
|
+
|
|
162
|
+
| Preset | Depth | Roughly |
|
|
163
|
+
|---|---|---|
|
|
164
|
+
| Sweep | 10 | ~1 second per 10 moves. Finds blunders and big patterns. |
|
|
165
|
+
| Standard | 14 | The sensible default for a few hundred games. |
|
|
166
|
+
| Deep | 18 | Several times slower. Worth it for a history you intend to keep. |
|
|
167
|
+
|
|
168
|
+
Sixty games at Sweep took 65 seconds on the machine this was written on.
|
|
169
|
+
The estimate in the app is deliberately crude and labelled as such: engine
|
|
170
|
+
speed varies by an order of magnitude across machines.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Setting up
|
|
175
|
+
|
|
176
|
+
Everything shares the one virtualenv at the repository root:
|
|
177
|
+
|
|
178
|
+
```powershell
|
|
179
|
+
# Windows PowerShell
|
|
180
|
+
python -m venv .lichess
|
|
181
|
+
.\.lichess\Scripts\python.exe -m pip install -r Weakness-Report\requirements.txt
|
|
182
|
+
.\.lichess\Scripts\python.exe -m pip install -e Lichess-Study-to-PDF
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# Git Bash on Windows
|
|
187
|
+
python -m venv .lichess
|
|
188
|
+
./.lichess/Scripts/python.exe -m pip install -r Weakness-Report/requirements.txt
|
|
189
|
+
./.lichess/Scripts/python.exe -m pip install -e Lichess-Study-to-PDF
|
|
190
|
+
|
|
191
|
+
# macOS / Linux
|
|
192
|
+
python -m venv .lichess
|
|
193
|
+
./.lichess/bin/python -m pip install -r Weakness-Report/requirements.txt
|
|
194
|
+
./.lichess/bin/python -m pip install -e Lichess-Study-to-PDF
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
The study exporter is optional and buys the board diagrams in the PDF, plus
|
|
198
|
+
the font handling that lets it draw them. ChessAnalyzer needs no install as
|
|
199
|
+
long as its folder is beside this one.
|
|
200
|
+
|
|
201
|
+
A [Stockfish](https://stockfishchess.org/download/) binary in
|
|
202
|
+
`Lichess-Study-to-PDF/engine/` is what does the actual reviewing; ChessAnalyzer
|
|
203
|
+
can also download one from its own engine picker. Without an engine there is
|
|
204
|
+
nothing to review.
|
|
205
|
+
|
|
206
|
+
**No API token is needed.** Every endpoint is public. A Lichess token raises
|
|
207
|
+
the rate limit while pulling a few hundred games; paste one into the Token
|
|
208
|
+
dialog and it is kept in memory for that session only, never written to disk.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Running it
|
|
213
|
+
|
|
214
|
+
```powershell
|
|
215
|
+
# Windows PowerShell
|
|
216
|
+
cd Weakness-Report
|
|
217
|
+
& "..\.lichess\Scripts\python.exe" -m weakness_report.cli serve
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
# Git Bash on Windows
|
|
222
|
+
cd Weakness-Report
|
|
223
|
+
../.lichess/Scripts/python.exe -m weakness_report.cli serve
|
|
224
|
+
|
|
225
|
+
# macOS / Linux
|
|
226
|
+
cd Weakness-Report
|
|
227
|
+
../.lichess/bin/python -m weakness_report.cli serve
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Open <http://127.0.0.1:8781>. `Ctrl+C` stops it. `--port 8782` if 8781 is
|
|
231
|
+
taken; `--host 0.0.0.0` exposes it to your network, which has no
|
|
232
|
+
authentication unless you set the password gate below — and this app serves
|
|
233
|
+
your own games and your own numbers, so only do that on a network you trust.
|
|
234
|
+
|
|
235
|
+
A review of a few hundred games takes minutes, not seconds. It runs in the
|
|
236
|
+
background, reports progress, survives you switching tabs, and can be
|
|
237
|
+
cancelled; anything already reviewed is kept, so a cancelled run is not a
|
|
238
|
+
wasted one.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Where the games come from
|
|
243
|
+
|
|
244
|
+
| Source | |
|
|
245
|
+
|---|---|
|
|
246
|
+
| **Lichess** | One streaming request, PGN and clocks included. |
|
|
247
|
+
| **Chess.com** | Monthly archives, newest first, until there are enough. Bounded at 36 months. |
|
|
248
|
+
| **A PGN file or folder** | Anything you exported from anywhere, including games played over the board. |
|
|
249
|
+
| **ChessAnalyzer's library** | Games you have already reviewed there. Read only — this app never writes into another app's folder. |
|
|
250
|
+
|
|
251
|
+
Reviews are filed **by game id, not by dataset**, so the same game reviewed
|
|
252
|
+
once counts for every report that includes it, whichever door it came through.
|
|
253
|
+
Adding fifty games to a history of four hundred costs fifty reviews.
|
|
254
|
+
|
|
255
|
+
A saved review is only reused when it was searched **the same way**. Changing
|
|
256
|
+
the preset re-reviews rather than quietly mixing depth-10 and depth-18 numbers
|
|
257
|
+
into one average — a report that did that would look completely normal and be
|
|
258
|
+
entirely wrong. *Existing reviews* in the settings relaxes this if you would
|
|
259
|
+
rather have the speed, and the report then says its settings were mixed.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## How it slices your history
|
|
264
|
+
|
|
265
|
+
Fourteen dimensions, and a move can be in several buckets of the same one.
|
|
266
|
+
|
|
267
|
+
| | |
|
|
268
|
+
|---|---|
|
|
269
|
+
| **Kinds of position** | Queenless middlegames, middlegames with queens on, opposite-side castling, your king still in the centre, open and closed positions, rook / pawn / minor-piece / opposite-bishop endings, when you are ahead, when you are behind. A curated list rather than a full cross product: phase × queens × centre × material is 108 buckets, nearly all too small to say anything about. |
|
|
270
|
+
| **Game phase** | Opening, middlegame, endgame — boundaries found from the position, not a fixed move number. |
|
|
271
|
+
| **Queens, pawn structure, material, your king, kind of ending** | The raw position features, each computable from a FEN alone and each written out in the glossary. |
|
|
272
|
+
| **Time pressure** | Seconds left on your clock when you moved, from the PGN's own `[%clk]` comments. Games with no clock are left out of this slice entirely rather than lumped together. |
|
|
273
|
+
| **Opening** | Grouped by family, so a variation does not become its own bucket of one game. |
|
|
274
|
+
| **Colour, time control, opponent strength** | As White or Black, bullet through classical, and against players rated 100 points either side of you. |
|
|
275
|
+
| **Move label, move number** | How the move was labelled, and ten-move bands, which sometimes shows a slump the phase split hides. |
|
|
276
|
+
|
|
277
|
+

|
|
278
|
+
|
|
279
|
+
Every table shows moves, games, ACPL, accuracy, blunders and the gap to your
|
|
280
|
+
own average, so you can disagree with the ranking and read the numbers
|
|
281
|
+
yourself.
|
|
282
|
+
|
|
283
|
+
### What the features mean
|
|
284
|
+
|
|
285
|
+
All of them are computed from the position alone, and all are deliberately
|
|
286
|
+
simple, because a feature nobody can check is a feature nobody should trust:
|
|
287
|
+
|
|
288
|
+
- **queens off / one queen / queens on** — who still has a queen
|
|
289
|
+
- **open / semi-open / closed centre** — pawn pairs standing head to head:
|
|
290
|
+
none, one or two, three or more
|
|
291
|
+
- **material ahead / level / behind** — a pawn or more either way, **from your
|
|
292
|
+
side**, not White's
|
|
293
|
+
- **your king** — which wing it is standing on, counted only once the opening
|
|
294
|
+
is over, because before that the bucket is really "have you castled yet"
|
|
295
|
+
- **rook / pawn / minor piece / opposite bishops / queen ending** — by which
|
|
296
|
+
pieces are left, and only once the position is actually an ending
|
|
297
|
+
|
|
298
|
+
### An honest limit
|
|
299
|
+
|
|
300
|
+
Slices overlap and none of them is controlled for the others. A bucket that
|
|
301
|
+
happens to hold most of your middlegame will inherit how you play middlegames.
|
|
302
|
+
Near-duplicates are dropped, but a partial overlap survives — so read two
|
|
303
|
+
findings that cover the same moves as one observation, not two. The report
|
|
304
|
+
says this on its method page too.
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## The worst moves
|
|
309
|
+
|
|
310
|
+
The single moves that cost most across the whole history, at most two from any
|
|
311
|
+
one game so that one collapse does not fill the page. Forced moves are
|
|
312
|
+
excluded: there was nothing else to play.
|
|
313
|
+
|
|
314
|
+

|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Exporting
|
|
319
|
+
|
|
320
|
+
**PDF** is a document rather than a dump: what was measured and how, the
|
|
321
|
+
findings in order, what you do well, every slice as a table with a bar against
|
|
322
|
+
your average, the worst moves as diagrams, and the method. This is the new
|
|
323
|
+
layout — the study exporter lays out *lines of chess*, and a weakness report is
|
|
324
|
+
a different shape — but it borrows that app's board renderer for the diagrams
|
|
325
|
+
and its font handling, both optional.
|
|
326
|
+
|
|
327
|
+
<img src="docs/pdf-cover.png" width="420" alt="The first page of the exported PDF: the figures it was built from, then the ranked findings each with a bar and its sample, then what you do well"> <img src="docs/pdf-moments.png" width="420" alt="A PDF page of the worst moves as board diagrams, each with the move played, what it cost, the engine's move and the kind of position">
|
|
328
|
+
|
|
329
|
+
**CSV** gives every bucket of every slice as one file, with the dimension as a
|
|
330
|
+
column, for anyone who would rather sort by their own column.
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## On a phone
|
|
335
|
+
|
|
336
|
+
One column below 860px, with the report first, then the position you tapped,
|
|
337
|
+
then the history list and settings. Settings collapse into a fold, the tab bar
|
|
338
|
+
scrolls, and the board is never allowed to fill a tablet.
|
|
339
|
+
|
|
340
|
+
<img src="docs/phone.png" width="300" alt="Weakness Report on a phone: a two-row header, the summary tiles, and the findings list">
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## What is kept on disk
|
|
345
|
+
|
|
346
|
+
```
|
|
347
|
+
history/ (WEAKNESS_DIR, or ./history)
|
|
348
|
+
settings.json
|
|
349
|
+
games/lichess-you.json the games of one dataset, PGN included
|
|
350
|
+
reviews/lichess-Wi8IPxc3.json one review per game -- the expensive part
|
|
351
|
+
reports/lichess-you.json the finished aggregation
|
|
352
|
+
data/positions.json the engine's position cache
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Reviews are the only thing here that is expensive, and forgetting a report
|
|
356
|
+
keeps them. The position cache is this app's own rather than ChessAnalyzer's:
|
|
357
|
+
sharing that file would be a write into another app's folder, which the apps in
|
|
358
|
+
this repository do not do to each other.
|
|
359
|
+
|
|
360
|
+
`history/` is gitignored — it holds your games and a page of numbers about
|
|
361
|
+
your play.
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## Command line
|
|
366
|
+
|
|
367
|
+
Reviewing four hundred games is the sort of thing you start before going to
|
|
368
|
+
bed, so everything the browser does is also a command, and the long one prints
|
|
369
|
+
progress rather than sitting silent for an hour.
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
weakness run <username> --source lichess --limit 300 --preset standard
|
|
373
|
+
weakness run you --source chesscom --speed blitz --days 180
|
|
374
|
+
weakness run me --source pgn --path ~/games/otb.pgn
|
|
375
|
+
weakness run me --source analyzer # reuse ChessAnalyzer's reviews
|
|
376
|
+
weakness estimate <username> --preset deep # how long would that take?
|
|
377
|
+
weakness games <username> --limit 500 # fetch only, review nothing
|
|
378
|
+
weakness show <key> --slice situation --slice clock
|
|
379
|
+
weakness reslice <key> --min-moves 80 --min-games 10
|
|
380
|
+
weakness list
|
|
381
|
+
weakness forget <key> # the report; reviews are kept
|
|
382
|
+
weakness pdf <key> --out report.pdf
|
|
383
|
+
weakness csv <key> --out slices.csv
|
|
384
|
+
weakness serve [--host H] [--port P]
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Run them through the venv the same way as `serve`, e.g.
|
|
388
|
+
`../.lichess/Scripts/python.exe -m weakness_report.cli list`.
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## How the pieces fit
|
|
393
|
+
|
|
394
|
+
| Module | |
|
|
395
|
+
|---|---|
|
|
396
|
+
| `bridge.py` | finding ChessAnalyzer and the study exporter, and degrading honestly without them |
|
|
397
|
+
| `sources.py` | your games in, from a site, a PGN, or ChessAnalyzer's library |
|
|
398
|
+
| `store.py` | the history folder; reviews filed per game so nothing is done twice |
|
|
399
|
+
| `batch.py` | the review driver: fixed depth, resumable, cancellable, settings-checked |
|
|
400
|
+
| `features.py` | what kind of position this is, from a FEN alone |
|
|
401
|
+
| `buckets.py` | every way the report cuts your history, in one place |
|
|
402
|
+
| `aggregate.py` | ACPL, accuracy and excess loss — ChessAnalyzer's definitions, unchanged |
|
|
403
|
+
| `findings.py` | buckets into ranked claims, deduplicated by the moves they cover |
|
|
404
|
+
| `report.py` | the whole document as one object |
|
|
405
|
+
| `pipeline.py` | fetch, review, aggregate, save — shared by the CLI and the server |
|
|
406
|
+
| `pdf.py` | the new layout |
|
|
407
|
+
| `exportcsv.py` | every slice as one spreadsheet |
|
|
408
|
+
| `board.py` | board diagrams for the browser |
|
|
409
|
+
| `jobs.py` | background work with progress and cancellation |
|
|
410
|
+
| `server.py` / `web/` | the HTTP layer and the browser interface |
|
|
411
|
+
| `cli.py` | the same operations as commands, for the overnight run |
|
|
412
|
+
|
|
413
|
+
`aggregate.py` and `findings.py` are pure: no network, no engine, no disk.
|
|
414
|
+
That is what makes the evidence floor adjustable in the browser — re-slicing
|
|
415
|
+
four hundred games is milliseconds.
|
|
416
|
+
|
|
417
|
+
## Tests
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
../.lichess/Scripts/python.exe -m pytest tests -q # 42 tests
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
No network, no engine, no Stockfish. Four of them exist because of a specific
|
|
424
|
+
quiet failure this app is built to avoid:
|
|
425
|
+
|
|
426
|
+
- **`test_a_review_at_other_settings_is_not_reused`** — a depth-10 review
|
|
427
|
+
inside a depth-18 batch produces a normal-looking report with every figure
|
|
428
|
+
wrong.
|
|
429
|
+
- **`test_a_specific_bucket_survives_being_inside_a_vague_one`** — the overlap
|
|
430
|
+
check was written with containment first, which silently threw away every
|
|
431
|
+
specific finding in favour of the vague bucket holding it. A test caught it;
|
|
432
|
+
the fix was Jaccard similarity.
|
|
433
|
+
- **`test_acpl_excludes_book_moves`** — ChessAnalyzer excludes them, and a
|
|
434
|
+
report that did not would flatter anyone with preparation.
|
|
435
|
+
- **`test_no_rule_stretches_a_checkbox_across_its_container`** — the one
|
|
436
|
+
stylesheet assertion, and it is here because `.dialog input` and
|
|
437
|
+
`.check input` have identical specificity, so whichever was written last
|
|
438
|
+
won. The later one set `width: 100%`, which turned each checkbox in the
|
|
439
|
+
export dialog into a full-width slab that shouldered its own label out
|
|
440
|
+
through the right-hand edge. Nothing in Python could see it and the
|
|
441
|
+
stylesheet reads correctly. The rule now says `:not([type="checkbox"])`
|
|
442
|
+
outright, and the test asks the general question — does any selector setting
|
|
443
|
+
`width: 100%` still reach a bare `input` — so it keeps holding for rules
|
|
444
|
+
nobody has written yet.
|
|
445
|
+
|
|
446
|
+
Two more were written wrong before the code was: a test assumed a rare
|
|
447
|
+
disaster must outrank a common leak, and the arithmetic says it depends on the
|
|
448
|
+
product. That is the intended behaviour, and it is now an assertion rather
|
|
449
|
+
than an assumption.
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
## Hosting it for free
|
|
454
|
+
|
|
455
|
+
Same profile as the other four apps — FastAPI/Uvicorn needing a real
|
|
456
|
+
container, not a serverless host. This one needs **two** siblings, so the
|
|
457
|
+
Docker build context has to be the **repository root**.
|
|
458
|
+
|
|
459
|
+
[`Dockerfile`](https://github.com/spearb0lt/Lichess-Essentials/blob/main/Weakness-Report/Dockerfile) lives here but must be built with the repo root as
|
|
460
|
+
its context. Docker keeps "where is the Dockerfile" and "what can `COPY` see"
|
|
461
|
+
separate, which is what Render's **Root Directory** and **Dockerfile Path**
|
|
462
|
+
fields are for. It installs Stockfish via `apt-get` and installs ChessAnalyzer
|
|
463
|
+
as a package, so there is still exactly one implementation of the review in the
|
|
464
|
+
image.
|
|
465
|
+
|
|
466
|
+
**Reviewing on a free container is a bad idea** and worth saying plainly: a
|
|
467
|
+
shared vCPU searching to depth 14 is slow, and `history/` is wiped by every
|
|
468
|
+
redeploy, so the reviews — the expensive part — do not survive. Host it to
|
|
469
|
+
*read* reports if you like; review locally.
|
|
470
|
+
|
|
471
|
+
### Render
|
|
472
|
+
|
|
473
|
+
1. Push this repo to GitHub.
|
|
474
|
+
2. **New Web Service** → connect the repo → **Root Directory**: leave blank →
|
|
475
|
+
**Dockerfile Path**: `Weakness-Report/Dockerfile` → **Free** instance.
|
|
476
|
+
3. Optional secrets: `WEAKNESS_AUTH_USER` / `WEAKNESS_AUTH_PASS`.
|
|
477
|
+
4. Deploy.
|
|
478
|
+
|
|
479
|
+
### Hugging Face Spaces
|
|
480
|
+
|
|
481
|
+
1. **New Space** → **SDK: Docker** → **CPU basic (free)**.
|
|
482
|
+
2. Clone the Space's repo and copy in `Lichess-Study-to-PDF/`,
|
|
483
|
+
`ChessAnalyzer/` and `Weakness-Report/`, then copy
|
|
484
|
+
`Weakness-Report/Dockerfile` to the Space repo's own root.
|
|
485
|
+
3. Add to the top of the Space's `README.md`:
|
|
486
|
+
```yaml
|
|
487
|
+
---
|
|
488
|
+
title: Weakness Report
|
|
489
|
+
sdk: docker
|
|
490
|
+
app_port: 7860
|
|
491
|
+
---
|
|
492
|
+
```
|
|
493
|
+
4. **Settings → Repository secrets**: the same variables as Render.
|
|
494
|
+
|
|
495
|
+
### Locking it behind a password
|
|
496
|
+
|
|
497
|
+
[`server.py`](https://github.com/spearb0lt/Lichess-Essentials/blob/main/Weakness-Report/weakness_report/server.py) has an HTTP Basic Auth gate that only
|
|
498
|
+
activates when both `WEAKNESS_AUTH_USER` and `WEAKNESS_AUTH_PASS` are set, so
|
|
499
|
+
local use is never asked for credentials. Do **not** set `LICHESS_TOKEN` on a
|
|
500
|
+
public deployment: it would be shared by every visitor.
|
|
501
|
+
|
|
502
|
+
---
|
|
503
|
+
|
|
504
|
+
## If something goes wrong
|
|
505
|
+
|
|
506
|
+
| Symptom | Cause and fix |
|
|
507
|
+
|---|---|
|
|
508
|
+
| `No module named weakness_report` | You are in the wrong directory. `cd` into `Weakness-Report` first. |
|
|
509
|
+
| "ChessAnalyzer could not be found" | Keep its folder beside this one, set `CHESS_ANALYZER_DIR`, or `pip install -e ChessAnalyzer` from the repo root. |
|
|
510
|
+
| "No Stockfish found" | Put a binary in `Lichess-Study-to-PDF/engine/`, or download one from ChessAnalyzer's engine picker. |
|
|
511
|
+
| Nothing clears the evidence floor | Too few games. Review more, or lower *least moves* / *least games* — that re-slices instantly, no engine. |
|
|
512
|
+
| The report says settings were mixed | Some reviews came from another preset. Set *Existing reviews* to *ignore* and run again for a clean set. |
|
|
513
|
+
| Reviewing seems stuck | It is not: a few hundred games at depth 14 is genuinely tens of minutes. The progress bar counts games. Stop it and every finished review is kept. |
|
|
514
|
+
| A second run takes as long as the first | You changed the preset, so nothing could be reused. That is deliberate — mixing depths would silently corrupt every average. |
|
|
515
|
+
| The PDF has no diagrams | The study exporter is not installed. `pip install -e Lichess-Study-to-PDF`. |
|
|
516
|
+
| Lichess says 429 | You pulled a lot of games. Wait a minute; a token raises the limit. |
|
|
517
|
+
|
|
518
|
+
## Licence
|
|
519
|
+
|
|
520
|
+
MIT — see [LICENSE](https://github.com/spearb0lt/Lichess-Essentials/blob/main/LICENSE). Chess piece artwork in the diagrams comes from
|
|
521
|
+
python-chess (Colin M.L. Burnett's Cburnett set, CC BY-SA 3.0); opening names
|
|
522
|
+
come from [lichess-org/chess-openings](https://github.com/lichess-org/chess-openings)
|
|
523
|
+
(CC0). The accuracy formula and move-label rules are ChessAnalyzer's, which
|
|
524
|
+
takes the published Lichess thresholds for the three Lichess judgments.
|