https://github.com/jkaraskiewicz/warsaw-pool-ranking
https://github.com/jkaraskiewicz/warsaw-pool-ranking
Last synced: 5 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/jkaraskiewicz/warsaw-pool-ranking
- Owner: jkaraskiewicz
- Created: 2025-12-03T22:30:59.000Z (6 months ago)
- Default Branch: master
- Last Pushed: 2025-12-27T12:36:16.000Z (5 months ago)
- Last Synced: 2026-01-17T17:54:52.345Z (5 months ago)
- Language: Rust
- Size: 106 MB
- Stars: 1
- Watchers: 0
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
# Warsaw Pool Rankings
A skill-based rating system for Warsaw pool players using the Bradley-Terry Maximum Likelihood model with time decay. Data collected from CueScore.
## Features
- **Bradley-Terry ML Rating System** - 100 points = 2:1 winning odds
- **3-Year Exponential Time Decay** - Recent games weighted higher
- **Weekly Historical Replay** - Full simulation from scratch each week
- **Confidence Levels** - Unranked, Provisional, Emerging, Established
- **Angular Frontend** - Searchable player list with rating history charts
- **High-Performance Rust Backend** - Efficient data processing and rating calculation
- **SQLite Database** - Persistent storage with weekly snapshots
## Quick Start with Docker (Recommended)
### 1. Configure Venues
Edit `backend/src/config/venues.rs` to add Warsaw pool venues. This is a Rust file now, so the syntax will be Rust code:
```rust
pub fn get_venues() -> Vec {
vec![
VenueConfig { id: 2842336, name: "147 Break Zamieniecka".to_string(), slug: "147-break-zamieniecka".to_string() },
VenueConfig { id: 1698108, name: "147 Break Nowogrodzka".to_string(), slug: "147-break-nowogrodzka".to_string() },
]
}
```
### 2. Start Services
```bash
# Build and start all services (Backend, Frontend)
docker-compose up -d --build
# The Rust backend will automatically run `rankings refresh` on startup.
# To manually trigger data refresh:
# docker-compose exec backend ./warsaw_pool_ranking tournaments refresh
# docker-compose exec backend ./warsaw_pool_ranking rankings refresh
```
### 3. Access Application
- **Frontend:** http://localhost
- **Backend (Rust CLI):** Run `docker-compose logs backend` to see processing output.
## Using Makefile (Optional)
```bash
make up # Start all services
make tournaments # Fetch tournament data
make rankings # Calculate player ratings
make avatars # Download/update avatars
make avatars-stats # Show avatar storage statistics
make players # Show top 10 players
make database-stats # Show database statistics
make database-backup # Backup SQLite database
make logs # View logs
make down # Stop all services
```
See `make help` for all commands.
## Admin Panel Authentication
The admin panel requires password authentication to access administrative functions (data refresh, avatar refresh).
### Configuration
Set the admin password via the `ADMIN_PASSWORD` environment variable:
**Development (Local)**:
```bash
# In .env file
ADMIN_PASSWORD=admin
```
**Development (Docker)**:
```bash
# Set before running docker-compose
export ADMIN_PASSWORD=mysecurepassword
docker-compose up
```
**Production**:
```bash
# Generate a secure password
openssl rand -base64 32
# Set in your deployment environment
# For Docker:
ADMIN_PASSWORD= docker-compose up -d
# For systemd/direct deployment:
# Add to service file or export in shell
export ADMIN_PASSWORD=
```
### Default Passwords
- **Development default**: `admin` (if ADMIN_PASSWORD not set)
- **Docker default**: `changeme` (if ADMIN_PASSWORD not set)
**WARNING**: These defaults are INSECURE. Always set a strong password for production deployments.
### Security Best Practices
1. Use a strong, randomly generated password (minimum 20 characters)
2. Never commit passwords to version control
3. Rotate passwords periodically
4. Use HTTPS in production (passwords sent in Authorization header)
5. Monitor logs for failed authentication attempts
## Architecture
```
┌──────────────────────────────────────────────────┐
│ Angular Frontend (Port 80) │
│ - Player rankings table with search │
│ - Player detail overlay │
│ - Rating history chart (Chart.js) │
└────────────┬─────────────────────────────────────┘
│
│ (Accesses SQLite DB directly in backend container)
▼
┌──────────────────────────────────────────────────┐
│ Rust Backend (CLI - processes on startup) │
│ - Scrapes CueScore data │
│ - Calculates Bradley-Terry ratings │
│ - Stores data in SQLite │
└────────────┬─────────────────────────────────────┘
│
│ (Embedded within backend container)
▼
┌──────────────────────────────────────────────────┐
│ SQLite Database (File: warsaw_pool_ranking.db) │
│ - Players, Games, Ratings, Snapshots │
└──────────────────────────────────────────────────┘
```
## Rating Algorithm
### Bradley-Terry Maximum Likelihood
- Uses `choix` library for efficient ML estimation
- Scale: 100 points = 2:1 winning odds (200 pts = 4:1 odds)
- Game-level granularity (match scores expanded to individual games)
### Time Decay
- Exponential decay with 3-year half-life
- Formula: `weight = exp(-λ × days_ago)` where `λ = ln(2) / 1095`
- Applied during rating calculation, not stored
### New Player Blending
- 0-9 games: Unranked (100% starter rating of 500)
- 10-49 games: Provisional (blend starter + ML)
- 50-99 games: Emerging (mostly ML rating)
- 100+ games: Established (pure ML rating)
### Weekly Simulation
- Replays entire game history week-by-week
- Generates consistent snapshots for history charts
- Allows algorithm changes to update all historical ratings
## Project Structure
```
warsaw-pool-ranking/
├── backend/ # Rust backend
│ ├── src/ # Rust source code
│ │ ├── api/ # CueScore API client
│ │ ├── cache/ # Caching logic
│ │ ├── cli/ # Command-line interface
│ │ ├── config/ # Application configuration
│ │ ├── database/ # Database interaction
│ │ ├── domain/ # Core domain models and logic
│ │ ├── fetchers/ # Web scraping logic
│ │ ├── http/ # HTTP client with rate limiting
│ │ ├── pagination/ # Pagination logic
│ │ ├── rate_limiter/ # Rate limiting implementation
│ │ ├── rating/ # Bradley-Terry rating algorithm
│ │ └── services/ # High-level business logic (ingestion, processing)
│ ├── Cargo.toml # Rust project manifest
│ └── Dockerfile # Dockerfile for the Rust backend
├── frontend/
│ └── src/app/
│ ├── components/ # Angular components
│ ├── services/ # HTTP services
│ └── models/ # TypeScript interfaces
├── database/
│ └── schema.sql # SQLite schema
├── docker-compose.yml
└── README.md
```
## Development
### Without Docker
To run the Rust backend locally:
```bash
cd backend
# First, build the project
cargo build --release
# Then, run the processing step. It will fetch data if not cached.
./target/release/warsaw_pool_ranking rankings refresh
# To fetch new tournament data:
./target/release/warsaw_pool_ranking tournaments refresh
```
### Running Tests
```bash
# With Docker
docker-compose exec backend cargo test
# Without Docker
cd backend
cargo test
```
### Backend Development
```bash
# Docker (builds and runs)
docker-compose up -d backend
# Local (with hot reload via `cargo watch` or manual recompilation)
cd backend
cargo watch -x run -- rankings refresh # Runs `rankings refresh` on file changes
```
### Frontend Development
```bash
# Local (recommended for development)
cd frontend
npm install
npm start # http://localhost:4200
# Docker (production build)
docker-compose up frontend
```
## Data Collection
### CueScore Integration
1. **Venue Scraping** - Scrapes tournament lists from venue pages
2. **API Fetching** - Fetches tournament details via API
3. **Game Parsing** - Converts match scores to individual games
4. **Rate Limiting** - 10 requests/second with caching and graceful error handling
### Adding Venues
Find venue on CueScore, extract from URL:
```
https://cuescore.com/venue/{slug}/{id}/tournaments
```
Add to `backend/src/config/venues.rs`:
```rust
VenueConfig { id: 12345, name: "New Venue Name".to_string(), slug: "new-venue-name".to_string() },
```
## Documentation
- **[backend/tests/README.md](backend/tests/README.md)** - Testing guide
## Technology Stack
- **Backend:** Rust (1.73+), `tokio`, `reqwest`, `ndarray`, `rusqlite`, `serde`
- **Frontend:** Angular 17, TypeScript, Angular Material, Chart.js (ng2-charts)
- **Database:** SQLite (embedded within backend container)
- **Deployment:** Docker, docker-compose, Nginx
## License
MIT
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Run tests: `make test`
5. Submit a pull request
## Support
For issues and questions, please use the GitHub issue tracker.