|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Commands |
| 6 | + |
| 7 | +```bash |
| 8 | +# Install dependencies |
| 9 | +pip install -r requirements.txt |
| 10 | + |
| 11 | +# Run all tests |
| 12 | +pytest tests/ -v |
| 13 | + |
| 14 | +# Run a single test file |
| 15 | +pytest tests/test_scraper.py -v |
| 16 | + |
| 17 | +# Run a single test |
| 18 | +pytest tests/test_scraper.py::TestParseEntryList::test_kingregistration_columns -v |
| 19 | +``` |
| 20 | + |
| 21 | +## Architecture |
| 22 | + |
| 23 | +The project is a **pipeline** that builds chess opponent dossiers from tournament entry lists. Each package is a self-contained pipeline stage: |
| 24 | + |
| 25 | +``` |
| 26 | +scraper.py → fetch entry list from tournament site → player names |
| 27 | +megabase/ → one-time SQLite index of ChessBase PGN export → game PGNs by name |
| 28 | +lookup/ → Lichess + chess.com API → online profiles + game PGNs |
| 29 | +analysis/ → PGN strings → opening repertoire + tendency stats |
| 30 | +dossier/ → all of the above → rendered Markdown/JSON report |
| 31 | +``` |
| 32 | + |
| 33 | +### Data flow |
| 34 | + |
| 35 | +1. `scraper.scrape_entry_list(tournament, site)` → `list[dict]` of players with name, rating, section etc. |
| 36 | +2. `megabase.query.get_player_games(name, db_path)` → `list[dict]` each with a `pgn` key |
| 37 | +3. `lookup.lichess.search(name)` / `lookup.chesscom.find_profile(name)` → profile dicts; `get_games()` / `games_as_pgn()` → PGN strings |
| 38 | +4. `analysis.openings.analyse_openings(pgn_strings, player)` + `analysis.stats.analyse_stats(pgn_strings, player)` → dicts |
| 39 | +5. `dossier.report.build_dossier(player, pgn_strings, profiles)` → dossier dict; `render_markdown()` / `render_json()` → string output |
| 40 | + |
| 41 | +### Key design decisions |
| 42 | + |
| 43 | +- **All analysis functions are pure** — they accept `list[str]` (PGN strings) and return dicts. No I/O. CLIs and `dossier/report.py` handle all sourcing. |
| 44 | +- **Player name matching is case-insensitive substring** — `"smith"` matches `"Smith, John"`. This applies in both `scraper._HEADER_MAP` normalisation and `megabase.query` SQL `LIKE` queries. |
| 45 | +- **`scraper.parse_entry_list`** requires at least one recognised column header from `_HEADER_MAP` before accepting a table, to skip nav/layout tables. |
| 46 | +- **chess.com has no search API** — `lookup.chesscom.guess_usernames(name)` generates candidates from `Last, First` / `First Last` patterns and `find_profile()` tries each until one resolves. |
| 47 | +- **Lichess rate limiting** — `lookup.lichess` sleeps 1s before game fetch requests. |
| 48 | +- **megabase index** is built once from a ChessBase PGN export (`python -m megabase.indexer mega.pgn`) and then queried read-only. |
| 49 | + |
| 50 | +### Supported tournament sites |
| 51 | + |
| 52 | +| Site | `--site` flag | URL pattern | |
| 53 | +|---|---|---| |
| 54 | +| kingregistration.com | `kingregistration` (default) | `/entrylist/<id>` | |
| 55 | +| chessaction.com | `chessaction` | `/tournaments/advance_entry_list.php?tid=<id>` | |
| 56 | + |
| 57 | +Full URLs are auto-detected; `--site` is only needed for ID shorthands. |
| 58 | + |
| 59 | +### Roadmap |
| 60 | + |
| 61 | +- Steps 1–5 are complete (scraping, megabase indexing, online lookup, analysis, dossier generation). |
| 62 | +- Step 6 (end-to-end pipeline): tournament URL → auto-resolve Lichess/chess.com handles → fetch games → generate all dossiers as a folder of Markdown files + combined PDF. Name→handle resolution picks the best autocomplete candidate and flags low-confidence matches in the report. |
| 63 | +- MegaDatabase integration will be added to Step 6 once the SQLite index is built. |
0 commit comments