Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#
# Template for environment variables to be used.
# Template for environment variables used by `python -m chesscom`.
# Copy this file to .env and fill in the values.
#
MATCH_ID=[MATCH ID] # match_strengthening_extract.py (optional — prompted at runtime if absent)
CLUB_REF=[CLUB REF] # generate_club_member_report.py, club_contribution_report.py, match_strengthening_extract.py
CLUB_NAME=[CLUB NAME] # club_contribution_report.py, match_strengthening_extract.py
DATA_ANALYSIS_YEAR=[YEAR] # club_contribution_report.py
LIST_OF_CLUBS=[COMMA SEPARATED LIST OF CLUBS] # generate_prospect_data.py
EXCLUSION_CLUB=[CLUB REF] # optional: exclude this club's members from the prospect report
MATCH_ID=[MATCH ID] # match-eligibility (optional — can also be passed via --match-id)
CLUB_REF=[CLUB REF] # member-summary, match-participation, match-eligibility
CLUB_NAME=[CLUB NAME] # member-summary, match-participation, match-eligibility
DATA_ANALYSIS_YEAR=[YEAR] # match-participation
LIST_OF_CLUBS=[COMMA SEPARATED LIST OF CLUBS] # prospects
EXCLUSION_CLUB=[CLUB REF] # prospects (optional — members of this club are excluded from the prospect list)
21 changes: 10 additions & 11 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
{
"python.pythonPath": "${workspaceFolder}/.venv/bin/python",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.linting.flake8Enabled": true,
"python.linting.mypyEnabled": true,
"python.linting.pylintArgs": [
"--load-plugins=pylint_django"
],
"python.formatting.provider": "black",
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"python.testing.nosetestsEnabled": false,
"python.envFile": "${workspaceFolder}/.env"
"python.envFile": "${workspaceFolder}/.env",
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.ruff": "explicit",
"source.organizeImports.ruff": "explicit"
}
}
}
31 changes: 31 additions & 0 deletions DESIGN-REVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -442,3 +442,34 @@ Each action below is a self-contained prompt that should be executed sequentiall
---

*This review recommends executing the 13 remedial actions in order. Actions 1–3 are preparatory (docs, deps, tests). Actions 4–10 are the core refactoring. Actions 11–13 are integration and finalisation. Do not skip Action 3 — tests must exist before refactoring begins.*

---

## 9. Resolution Status

All 13 remedial actions have been completed. The table below maps each action to
its corresponding pull request and the test count at the time of merge.

| Action | Title | PR | Tests at merge |
|---|---|---|---|
| 1 | Add README and pyproject.toml | [#2](https://github.com/ldastey-dev/chesscom/pull/2) | — |
| 2 | Pin dependencies in pyproject.toml | [#3](https://github.com/ldastey-dev/chesscom/pull/3) | — |
| 3 | Add initial test suite | [#4](https://github.com/ldastey-dev/chesscom/pull/4) | 27 |
| 4 | Add GitHub Actions CI workflow | [#5](https://github.com/ldastey-dev/chesscom/pull/5) | 27 |
| 5 | Extract shared utilities | [#6](https://github.com/ldastey-dev/chesscom/pull/6) | 27 |
| 6 | Add ChessComClient API wrapper | [#7](https://github.com/ldastey-dev/chesscom/pull/7) | 56 |
| 7 | Add domain models | [#8](https://github.com/ldastey-dev/chesscom/pull/8) / [#9](https://github.com/ldastey-dev/chesscom/pull/9) | 163 |
| 8 | Add ExcelReportWriter | [#10](https://github.com/ldastey-dev/chesscom/pull/10) | 185 |
| 9 | Add AppConfig | [#11](https://github.com/ldastey-dev/chesscom/pull/11) | 214 |
| 10 | Add report classes (BaseReport + 4 concrete reports) | [#12](https://github.com/ldastey-dev/chesscom/pull/12) | 255 |
| 11 | Add CLI entry point; remove legacy scripts | [#13](https://github.com/ldastey-dev/chesscom/pull/13) | 240 |
| 12 | Fix GitHub Actions workflow | Completed as part of Actions 4 & 11 | 240 |
| 13 | Update all documentation | [#14](https://github.com/ldastey-dev/chesscom/pull/14) | 240 |

### Final state (post Action 13)

- **240 tests** passing across Python 3.11 and 3.12
- **ruff** clean (0 errors)
- All legacy scripts removed; single `python -m chesscom <subcommand>` entry point
- Full Google-style docstrings on every public class and function
- README, `.env.template`, and `.vscode/settings.json` reflect the current codebase
180 changes: 119 additions & 61 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
# chesscom

Python tools for managing a Chess.com club. Generates Excel reports covering member statistics, match participation, prospect identification, and match eligibility.
Python tools for managing a Chess.com club. Generates Excel reports covering
member statistics, match participation, prospect identification, and match
eligibility.

[![CI](https://github.com/ldastey-dev/chesscom/actions/workflows/ci.yml/badge.svg)](https://github.com/ldastey-dev/chesscom/actions/workflows/ci.yml)

---

## Reports

| Script | Description |
|---|---|
| `club_contribution_report.py` | Generates a match participation report showing each member's win rate, timeout rate, and participation percentage across all club matches in a given year. |
| `generate_club_member_report.py` | Generates a full club member summary with ratings, Chess960 ratings, FIDE titles, join dates, and last-online dates. |
| `generate_prospect_data.py` | Generates a prospect list by aggregating members from a set of target clubs and excluding current club members. |
| `match_strengthening_extract.py` | Generates a match eligibility report listing club members who meet the rating threshold for a specific upcoming match, including sign-up status. |
| Subcommand | Output file | Description |
|---|---|---|
| `member-summary` | `Club Member Summary Report.xlsx` | Roster of all club members with ratings, Chess960 ratings, FIDE titles, join dates, and last-online dates. |
| `match-participation` | `<Club> Club Contribution Report <Year>.xlsx` | Two-sheet workbook: per-member win rate, timeout rate, and participation %; plus a match-by-match breakdown. |
| `prospects` | `Member Prospects.xlsx` | De-duplicated prospect list sourced from one or more target clubs, with current members excluded. |
| `match-eligibility` | `Match Eligibility <match name>.xlsx` | Club members eligible for a specific match (within its rating cap), showing sign-up status. |

---

## Prerequisites

- Python 3.10 or higher
- A [Chess.com](https://www.chess.com) club (you must have access to your club reference ID)
- Python 3.11 or higher
- A [Chess.com](https://www.chess.com) club (you must have access to your club's URL slug)
- `git`

---
Expand Down Expand Up @@ -50,124 +54,178 @@ source .venv/bin/activate
pip install -e .
```

For development (includes `pytest`, `pytest-cov`, `responses`, `ruff`):

```bash
pip install -e ".[dev]"
```

### 4. Configure environment variables

Copy the template and fill in your values:

```bash
cp .env.template .env
```

Open `.env` and set each variable — see the [Environment Variables](#environment-variables) section below.
Open `.env` and fill in your values — see [Environment Variables](#environment-variables) below.

---

## Usage

All scripts are run from the `src/` directory.

```bash
cd src
```
All subcommands read configuration from environment variables (or a `.env` file
in the project root).

### Club Member Summary

Produces an Excel file (`output/Club Member Summary Report.xlsx`) with one row per club member containing their rating, Chess960 rating, FIDE title, join date, and last-online date.

```bash
python generate_club_member_report.py
python -m chesscom member-summary
```

**Required env vars:** `CLUB_REF`
**Required:** `CLUB_REF`, `CLUB_NAME`

---

### Match Participation Report

Produces an Excel file (`output/<Club Name> Club Contribution Report <Year>.xlsx`) with two sheets: member metrics (participation %, win rate, timeout count) and a match-by-match breakdown.

```bash
python club_contribution_report.py
python -m chesscom match-participation
```

**Required env vars:** `CLUB_REF`, `CLUB_NAME`, `DATA_ANALYSIS_YEAR`
**Required:** `CLUB_REF`, `CLUB_NAME`, `DATA_ANALYSIS_YEAR`

---

### Prospect Report

Produces an Excel file (`output/Member Prospects.xlsx`) listing players from specified clubs who are not already members of your club.

```bash
python generate_prospect_data.py
python -m chesscom prospects
```

**Required env vars:** `LIST_OF_CLUBS` (comma-separated)
**Required:** `LIST_OF_CLUBS`
**Optional:** `EXCLUSION_CLUB`

---

### Match Eligibility Report

Produces an Excel file (`output/Match Eligibility <match name>.xlsx`) listing club members who are eligible for a specific match based on its rating cap and chess variant, showing whether each member has already signed up.

```bash
python match_strengthening_extract.py
# Match ID from MATCH_ID env var:
python -m chesscom match-eligibility

# Or supply it directly:
python -m chesscom match-eligibility --match-id 12345
```

**Required env vars:** `CLUB_REF`, `CLUB_NAME`
**Required:** `CLUB_REF`, `CLUB_NAME`
**Optional:** `MATCH_ID` (can also be passed via `--match-id`)

---

### Help

`MATCH_ID` is optional — if not set, the script will prompt for it at runtime.
```bash
python -m chesscom --help
python -m chesscom match-eligibility --help
```

---

## Environment Variables

Copy `.env.template` to `.env` and populate the following variables:
Copy `.env.template` to `.env` and populate:

| Variable | Required By | Description |
| Variable | Required by | Description |
|---|---|---|
| `CLUB_REF` | `generate_club_member_report.py`, `club_contribution_report.py`, `match_strengthening_extract.py` | Your club's Chess.com URL slug (e.g. `team-scotland`) |
| `CLUB_NAME` | `club_contribution_report.py`, `match_strengthening_extract.py` | Your club's display name as it appears on Chess.com (e.g. `Team Scotland`) |
| `DATA_ANALYSIS_YEAR` | `club_contribution_report.py` | The year to analyse match participation for (e.g. `2025`) |
| `MATCH_ID` | `match_strengthening_extract.py` | The numeric Chess.com match ID (optional — prompted at runtime if absent) |
| `LIST_OF_CLUBS` | `generate_prospect_data.py` | Comma-separated list of club slugs to source prospects from |
| `CLUB_REF` | `member-summary`, `match-participation`, `match-eligibility` | Club URL slug, e.g. `team-scotland` |
| `CLUB_NAME` | `member-summary`, `match-participation`, `match-eligibility` | Display name, e.g. `Team Scotland` |
| `DATA_ANALYSIS_YEAR` | `match-participation` | Four-digit year to analyse, e.g. `2025` |
| `MATCH_ID` | `match-eligibility` | Chess.com match ID (optional — also accepted via `--match-id`) |
| `LIST_OF_CLUBS` | `prospects` | Comma-separated club slugs to source prospects from |
| `EXCLUSION_CLUB` | `prospects` | Optional club slug whose members are excluded from the prospect list |

---

## Output

All generated Excel files are written to the `output/` directory. The directory is created automatically if it does not exist. Filenames are unique — if a file with the same name already exists, an incrementing counter suffix is appended (e.g. `report_1.xlsx`, `report_2.xlsx`) to prevent overwriting local edits.
All Excel files are written to the `output/` directory (created automatically).
Filenames are unique — if a file already exists an incrementing counter suffix
is appended (`report_1.xlsx`, `report_2.xlsx`, …) to avoid overwriting
previous output.

The `output/` directory is excluded from version control via `.gitignore`.
The `output/` directory is excluded from version control.

---

## Project Structure

```
chesscom/
├── .env.template # Environment variable template
├── pyproject.toml # Python dependencies and tooling configuration
├── .env.template # Environment variable template
├── pyproject.toml # Dependencies and tooling configuration
├── src/
│ ├── club_contribution_report.py
│ ├── generate_club_member_report.py
│ ├── generate_prospect_data.py
│ ├── match_strengthening_extract.py
│ └── utils/
│ └── __init__.py # Shared HTTP, file, and timing utilities
└── output/ # Generated Excel reports (git-ignored)
│ └── chesscom/
│ ├── __init__.py
│ ├── __main__.py # Enables `python -m chesscom`
│ ├── cli.py # argparse entry point (build_parser / main)
│ ├── config.py # AppConfig dataclass + from_env()
│ ├── api/
│ │ └── client.py # ChessComClient — all HTTP calls
│ ├── domain/
│ │ ├── models.py # Member, Match, MatchResult, MemberParticipation
│ │ └── services.py # Pure calculation and filtering functions
│ ├── export/
│ │ └── excel.py # ExcelReportWriter + SheetConfig
│ └── reports/
│ ├── base.py # BaseReport ABC
│ ├── match_eligibility.py
│ ├── match_participation.py
│ ├── member_summary.py
│ └── prospect.py
├── tests/
│ ├── integration/ # Live-API tests (require network)
│ └── unit/ # Fast, dependency-free unit tests
└── output/ # Generated Excel reports (git-ignored)
```

---

## Development

### Install dev dependencies

```bash
pip install -e ".[dev]"
```

### Run tests

```bash
pytest
```

With coverage:

```bash
pytest --cov=src --cov-report=term-missing
```

### Lint

```bash
ruff check .
```

Auto-fix lint issues:

```bash
ruff check --fix .
```

### CI

The GitHub Actions workflow (`.github/workflows/ci.yml`) runs on every push and
pull request to `master`. It runs `ruff check` and `pytest --cov` against
Python 3.11 and 3.12.

---

## Notes

- The Chess.com public API is rate-limited. Scripts include automatic retry logic with exponential backoff for transient failures.
- Scripts use a Chrome browser `User-Agent` header to ensure compatibility with the Chess.com API.
- Long-running scripts prevent the system from sleeping for the duration of execution (Windows and Linux supported).
- The Chess.com public API is rate-limited. Requests are retried automatically
with exponential back-off on transient failures.
- A Chrome browser `User-Agent` header is used to ensure compatibility with
the Chess.com API.