This commit is contained in:
2026-02-18 08:30:47 -05:00
parent fdba869a8d
commit 6d3475efe9

203
updates.md Normal file
View File

@@ -0,0 +1,203 @@
# Lottery Tracker — Update Log
## Overview
Complete codebase overhaul: cleanup, modernization, new features, and full Next.js frontend build.
---
## Phase 1: Code Review & Cleanup
### Files Deleted
- `email_sender.py` — Email sending logic (removed entire email system)
- `send_email_now.py` — Manual email trigger
- `test_email.py` — Email tests
- `analyze_excel.py` — Excel analysis utility
- `read_excel.py` — Excel reader utility
- `import requests.py` — Misnamed utility script
- `powerball_numbers.html` — Debug HTML file
- `megamillions_debug.html` — Debug HTML file
- `Dockerfile.email` — Email service Docker image
- `EMAIL_SETUP.md` — Email configuration docs
### Issues Fixed
- **Hardcoded credentials** removed (Gmail password in docker docs)
- **`verify=False`** on all HTTP requests replaced with default SSL verification
- **Code duplication** — scraping logic was copy-pasted across 3 files, consolidated into one
- **No tests** — full test suite added
- **No linting** — ruff configured
- **Flask `debug=True`** hardcoded — replaced with env var
- **Docker healthcheck** used `curl` (not installed in slim image) — switched to Python urllib
- **`.dockerignore`** missing `.venv/` — build context was 456 MB, now much smaller
---
## Phase 2: Backend Rewrite
### New Modules
#### `config.py`
- `AppConfig` dataclass with nested `ScraperURLs`, `TaxConfig`, `InvestmentDefaults`
- `STATE_TAX_RATES` — all 51 US states + DC with 2024-2025 tax rates
- `LOTTERY_ODDS` — odds and ticket costs for all 4 lotteries
- `ANNUITY_YEARS`, `ANNUITY_ANNUAL_INCREASE` constants
- `load_config()` reads from environment variables with sensible defaults
#### `scrapers.py`
- Unified scraping with TTL cache (6-hour default via `cachetools`)
- `scrape_powerball()` / `scrape_mega_millions()` — requests + BeautifulSoup from lotto.net
- `scrape_canadian_lotteries()` — Playwright sync API from olg.ca
- `get_all_jackpots()` — returns all 4 jackpots with cache
- `clear_cache()` — force refresh
#### `app.py` (Rewritten)
Flask app factory pattern (`create_app()`) with endpoints:
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/jackpots` | GET | Current jackpots (cached) |
| `/api/jackpots/refresh` | POST | Force-refresh jackpots |
| `/api/calculate` | POST | Full investment calculation with state tax |
| `/api/states` | GET | All US states with tax rates |
| `/api/states/<code>` | GET | Single state info |
| `/api/compare` | GET | Side-by-side all 4 lotteries |
| `/api/calculate/breakeven` | POST | Break-even jackpot analysis |
| `/api/calculate/annuity` | POST | Annuity payment schedule |
| `/api/calculate/group` | POST | Group play split |
| `/api/odds` | GET | Probability data for all lotteries |
| `/api/health` | GET | Health check |
#### `lottery_calculator.py` (Rewritten)
Pure calculation functions with explicit parameters:
- `_run_investment_cycles()` — shared 90-day reinvestment cycle logic
- `calculate_us_lottery()` — lump sum + federal/state tax + CAD conversion + cycles
- `calculate_canadian_lottery()` — tax-free + investment cycles
- `calculate_break_even()` — EV formula: required jackpot for positive expected value
- `calculate_annuity()` — 30-year schedule with configurable annual increase
- `calculate_group_split()` — N-way split with custom share ratios
### Configuration Files
- `requirements.txt` — pinned versions, removed openpyxl/pandas/schedule
- `requirements-dev.txt` — pytest, pytest-mock, pytest-cov, httpx, ruff
- `pyproject.toml` — ruff config (line-length=100, py311 target), pytest config
- `.env.example` — all environment variables documented
### Test Suite (64 tests, all passing)
- `tests/conftest.py` — Flask test fixtures
- `tests/test_lottery_calculator.py` — ~25 tests (US calc, Canadian calc, break-even, annuity, group split)
- `tests/test_api.py` — ~15 endpoint integration tests with mocked scrapers
- `tests/test_config.py` — config loading, env override, state validation
- `tests/test_scrapers.py` — parser tests, mocked HTTP, cache behavior
---
## Phase 3: Frontend (Next.js 15 + MUI 6)
### Scaffold
- `frontend/package.json` — Next.js 15.1.4, MUI 6.3.1, Recharts 2.15, Axios
- `frontend/tsconfig.json` — standard Next.js TypeScript config with `@/*` paths
- `frontend/next.config.js` — standalone output, `/api/*` rewrite to backend
- `frontend/.env.local``NEXT_PUBLIC_API_URL=http://localhost:5000`
### Shared Libraries
- `frontend/lib/types.ts` — Full TypeScript interfaces (JackpotData, Calculation, StateInfo, BreakEvenResult, AnnuityResult, GroupResult, OddsInfo, etc.)
- `frontend/lib/api.ts` — Typed Axios client with all API endpoint functions
- `frontend/lib/format.ts``formatCurrency`, `formatCurrencyFull`, `formatPercent`, `formatNumber`
- `frontend/lib/theme.ts` — Dark MUI theme (primary: `#4fc3f7`, background: `#0a1929`)
### Components
- `frontend/components/ThemeRegistry.tsx` — MUI ThemeProvider wrapper with CssBaseline
- `frontend/components/NavBar.tsx` — Sticky app bar with navigation links
### Pages (7 routes)
| Route | File | Description |
|-------|------|-------------|
| `/` | `app/page.tsx` | Home — jackpot cards grid (4 lotteries) + tool cards grid (6 tools) |
| `/calculator` | `app/calculator/page.tsx` | Investment calculator — jackpot input, lottery type, state selector with tax rates, invest % slider, annual return slider, cycles, results summary, 90-day cycle table |
| `/compare` | `app/compare/page.tsx` | Side-by-side comparison — state selector, 4 lottery cards with jackpot, odds, after-tax take-home, daily/annual income |
| `/breakeven` | `app/breakeven/page.tsx` | Break-even analysis — lottery/state selectors, required jackpot, odds, probability, take-home fraction, expected value |
| `/annuity` | `app/annuity/page.tsx` | Annuity calculator — jackpot, type, years, annual increase slider, summary cards, year-by-year payment schedule table |
| `/group` | `app/group/page.tsx` | Group play — member count, custom share weights, per-member breakdown table (share %, jackpot share, after-tax, daily/annual income) |
| `/odds` | `app/odds/page.tsx` | Probability display — odds, percentage, ticket cost, log-scale relative bar, "50% chance" ticket count |
---
## Phase 4: Docker & Infrastructure
### Docker Images Built
| Image | Size | Description |
|-------|------|-------------|
| `lottery-tracker-backend` | 2.28 GB | Python 3.13-slim + Playwright Chromium + Gunicorn |
| `lottery-tracker-frontend` | 285 MB | Node.js 20-alpine + Next.js standalone |
### Docker Fixes Applied
1. **Removed `playwright install-deps chromium`** from Dockerfile.backend — system deps already installed manually; the auto-installer fails on newer Debian due to renamed font packages (`ttf-unifont``fonts-unifont`)
2. **Generated `package-lock.json`** — required by `npm ci` in frontend Dockerfile
3. **Created `frontend/public/`** directory with `manifest.json` — Dockerfile COPY step requires it
4. **Added `.venv/` to `.dockerignore`** — was inflating build context
5. **Removed `version: '3.8'`** warning — obsolete in modern Docker Compose
6. **Removed email-scheduler service** from `docker-compose.yml`
7. **Switched healthcheck** from `curl` to `python -c "import urllib.request; ..."`
8. **Backend CMD** changed to gunicorn (2 workers, 120s timeout)
### MUI Grid Fix
All 7 frontend pages used `<Grid size={{ xs: 12, sm: 6 }}>` syntax (Grid2 API), but imported `Grid` from `@mui/material` (legacy Grid v1). Fixed by changing imports to `Grid2 as Grid` in all pages.
### Running Containers
| Container | Status | Port Mapping |
|-----------|--------|-------------|
| `lottery-backend` | Up (healthy) | `0.0.0.0:5000 → 5000` |
| `lottery-frontend` | Up | `0.0.0.0:3003 → 3000` |
### Access URLs
- **Frontend**: http://localhost:3003
- **Backend API**: http://localhost:5000/api/health
---
## Phase 5: Documentation & Config
### Updated Files
- **`.gitignore`** — added `.next/`, `*.xlsx`, `*.html`, `ssl/` certs, coverage outputs
- **`AGENTS.md`** — filled in repo facts (Python 3.13/Flask + Next.js 15/MUI 6, pytest, ruff, docker compose)
- **`DOCKER_README.md`** — rewritten: removed email references, removed hardcoded password, added `.env.example` workflow, config table, production deployment guide
- **`DOCKER_QUICKSTART.md`** — rewritten: removed email service, updated container descriptions, simplified quick start
---
## Verification
- **64/64 tests passing** (`pytest -q`)
- **ruff lint clean** (`ruff check .` — all checks passed)
- **Docker build successful** — both images built
- **Containers running** — backend healthy, frontend serving
---
## Architecture Summary
```
┌─────────────────────────────────────────────────┐
│ nginx (prod) │
│ Rate limiting + SSL │
├─────────────────┬───────────────────────────────┤
│ Frontend │ Backend │
│ Next.js 15 │ Flask 3.1 │
│ MUI 6 │ Gunicorn │
│ TypeScript │ Python 3.13 │
│ Port 3000 │ Port 5000 │
│ │ │
│ 7 pages │ 11 API endpoints │
│ Dark theme │ 4 lottery scrapers │
│ Axios client │ TTL cache (6h) │
│ │ 51 state tax rates │
│ │ Playwright (Canadian) │
└─────────────────┴───────────────────────────────┘
```
### Tech Stack
- **Backend**: Python 3.13, Flask 3.1, Gunicorn, BeautifulSoup, Playwright
- **Frontend**: Next.js 15, TypeScript, Material-UI 6, Recharts, Axios
- **Testing**: pytest (64 tests), ruff (lint)
- **Docker**: Multi-container (backend + frontend + nginx for prod)
- **Config**: Environment variables via `.env` + `python-dotenv`