Files
lottosight/CLAUDE.md
T
2026-05-23 16:18:08 -04:00

22 KiB
Raw Blame History

LottoSight — Project Documentation

App Overview

Name: LottoSight
Type: Desktop Application
Framework: Python + Tkinter
Database: SQLite (local, zero-config)
Purpose: Analyze historical lottery drawing data and predict next winning numbers using multiple statistical strategies.


Tech Stack

Component Technology
UI Tkinter + ttk
Charts Matplotlib (embedded in Tkinter)
Database SQLite via sqlite3
Data Fetch requests + BeautifulSoup
Analysis collections, statistics, numpy
Scheduler APScheduler
Export openpyxl
Packaging PyInstaller

Supported Games

Game Main Balls Pool Bonus Ball Pool
Powerball 5 169 1 126
Mega Millions 5 170 1 125
Custom config config optional config

Data Sources

Source Game Coverage Method
NY Open Data API Powerball 2010present REST API
NY Open Data API Mega Millions 2002present REST API
Texas Lottery CSV Mega Millions 2003present CSV download

API Endpoints

  • Powerball (NY): https://data.ny.gov/resource/d6yy-54nr.json
  • Mega Millions (NY): https://data.ny.gov/resource/5xaw-6ayf.json
  • Mega Millions (TX): https://www.texaslottery.com/export/sites/lottery/Games/Mega_Millions/Winning_Numbers/download.html

File Structure

lottosight/
├── main.py                  # Entry point, app window, toolbar
├── db/
│   ├── database.py          # SQLite setup, schema, migrations
│   └── models.py            # CRUD operations, duplicate check
├── core/
│   ├── analyzer.py          # All analysis logic
│   ├── predictor.py         # Prediction strategies (5 total)
│   └── fetcher.py           # Fetch logic — auto + manual, all 3 sources
├── ui/
│   ├── dashboard.py         # Home/summary screen
│   ├── history.py           # Draw history browser
│   ├── analysis.py          # Charts and stats screen
│   ├── predictor_ui.py      # Prediction generator screen
│   ├── settings.py          # Settings screen + manual fetch button
│   └── statusbar.py         # Bottom status bar component
├── assets/
│   └── icon.png
├── exports/                 # Excel/CSV exports output folder
├── requirements.txt
└── CLAUDE.md                # This file

Database Schema

Table: games

Column Type Description
id INTEGER Primary key
name TEXT Game name (e.g. Powerball)
main_count INTEGER Number of main balls
main_max INTEGER Max value for main balls
bonus_count INTEGER Number of bonus balls (0 if none)
bonus_max INTEGER Max value for bonus ball
active INTEGER 1 = active, 0 = disabled

Table: draws

Column Type Description
id INTEGER Primary key
game_id INTEGER Foreign key → games.id
draw_date TEXT ISO date string (YYYY-MM-DD)
numbers TEXT Comma-separated main numbers
bonus TEXT Bonus ball number(s)
multiplier TEXT Power Play / Megaplier (nullable)
source TEXT Data source identifier
created_at TEXT Record insert timestamp

Table: predictions

Column Type Description
id INTEGER Primary key
game_id INTEGER Foreign key → games.id
strategy TEXT Strategy name used
numbers TEXT Predicted main numbers
bonus TEXT Predicted bonus number
created_at TEXT Prediction timestamp

Table: fetch_log

Column Type Description
id INTEGER Primary key
source TEXT Source name
fetched_at TEXT Timestamp of fetch
added INTEGER New records inserted
skipped INTEGER Duplicate records skipped
status TEXT success / error
message TEXT Error message or notes

Fetch System

Auto-Fetch

  • Triggers on app launch (background thread)
  • Repeats every 24 hours via APScheduler
  • Runs in background — does not block UI

Manual Fetch

  • Button in Toolbar (always visible, all screens)
  • Button in Settings screen
  • Shows "Fetching…" state while running

Duplicate Handling

  • Compare incoming records by draw_date + game_id
  • Skip if already exists in DB
  • Report result in status bar: X added, Y skipped

Status Bar Format

Last fetch: Powerball — 3 added, 2 skipped | Mega Millions — 5 added, 0 skipped | 2026-05-23 08:42 AM

Analysis Features

Feature Description
Frequency analysis Hot/cold numbers by total draw count
Gap/skip analysis How many draws since each number last appeared
Positional frequency Which numbers appear most in each draw position
Pair/triplet patterns Number combinations that appear together often
Odd/even ratio Ratio of odd vs even numbers per draw
Sum range analysis Distribution of total sums across all draws
Delta patterns Differences between consecutive numbers in a draw

Prediction Strategies

# Strategy Description
1 Hot Numbers Top N most frequent numbers in last X draws
2 Due Numbers Numbers overdue based on expected frequency gap
3 Weighted Random numpy random choice weighted by historical frequency
4 Monte Carlo 10,000 simulations, pick most common result
5 Positional Most frequent number per draw position

UI Screens

Screen Description
Dashboard Summary stats, last draw result, quick actions
History Searchable/sortable draw history table
Analysis Charts — frequency bar, heatmap, gap chart
Predictor Pick strategy → generate ticket numbers
Settings Game config, data sources, fetch interval, manual fetch

Logging

All actions are logged to console and optionally to a log file:

  • [FETCH] — auto/manual fetch events
  • [DB] — insert, skip, error events
  • [PREDICT] — prediction generated
  • [EXPORT] — export actions
  • [ERROR] — any exception with traceback

Build Phases & To-Do

Phase 0 — Planning

  • Define app scope and features
  • Choose tech stack
  • Design database schema
  • Design fetch system
  • Define data sources and API endpoints
  • Create CLAUDE.md

Phase 1 — Database Setup

  • Create lottosight/ project folder structure
  • Write db/database.py — SQLite init, create all tables
  • Write db/models.py — CRUD: insert draw, get draws, check duplicate, insert prediction, insert fetch log
  • Seed default game configs (Powerball, Mega Millions)
  • Write requirements.txt
  • Test DB creation and seed on fresh run
  • Write tests/conftest.py — shared tmp_db fixture
  • Write tests/test_database.py — 8 tests for database.py (47/47 pass)
  • Write tests/test_models.py — 39 tests for models.py (47/47 pass)
  • Write .gitea/workflows/ci.yml — CI on every push (syntax check + pytest)

Phase 2 — Fetch System

  • Write core/fetcher.py
    • fetch_powerball_ny() — NY Open Data API
    • fetch_megamillions_ny() — NY Open Data API
    • fetch_megamillions_tx() — Texas Lottery CSV
    • fetch_all() — calls all 3, aggregates results
    • Duplicate detection logic
    • Return added/skipped counts per source
  • Write ui/statusbar.py — bottom status bar widget
  • Wire auto-fetch on app launch (background thread)
  • Wire 24hr scheduled fetch (APScheduler)
  • Write main.py — app window, toolbar with manual fetch button
  • Wire manual fetch button → fetch_all() → update status bar
  • Test: fresh DB → fetch → verify records inserted (67/67 pass)
  • Test: second fetch → verify duplicates skipped, counts correct
  • Test: cross-source dedup (same date, different source → skipped)

Phase 3 — History Browser

  • Write ui/history.py
    • Treeview table with columns: Game, Date, Numbers, Bonus, Mult., Source
    • Filter by game (dropdown)
    • Search by date range (From / To entries)
    • Sort by column headers (▲/▼ indicators, numeric sort for bonus/multiplier)
    • Row count display
  • Connect history screen to DB reads via get_draws_with_game()
  • Auto-refresh after fetch completes
  • 13 tests (80/80 total passing)

Phase 4 — Analysis Engine + Charts

  • Write core/analyzer.py
    • frequency_analysis(game_id, last_n)
    • gap_analysis(game_id)
    • positional_frequency(game_id)
    • pair_analysis(game_id)
    • odd_even_ratio(game_id)
    • sum_range_analysis(game_id)
    • delta_analysis(game_id)
  • Write ui/analysis.py
    • Frequency bar chart (Matplotlib + FigureCanvasTkAgg)
    • Positional frequency heatmap (imshow, YlOrRd colormap)
    • Gap chart (color-coded: blue=recent, red=due)
    • Game dropdown + frequency window selector
    • NavigationToolbar on each tab for zoom/pan
  • 26 tests for all 7 analysis functions (106/106 total passing)

Phase 5 — Prediction Engine

  • Write core/predictor.py
    • hot_numbers(game_id, last_n=100) — top-frequency + most-frequent bonus
    • due_numbers(game_id) — highest gap numbers from pool
    • weighted_random(game_id) — numpy weighted choice (min weight 1 for unseen)
    • monte_carlo(game_id, simulations=10000) — tally-based selection
    • positional_pick(game_id) — per-position best with dedup
    • All strategies: random fallback on empty DB
  • Write ui/predictor_ui.py
    • Game + strategy + ticket count dropdowns
    • Generate button (disables during generation)
    • Treeview results: #, zero-padded numbers, bonus
    • Save to DB (insert_prediction per ticket) + Clear
    • Strategy description label
  • 18 tests — all 5 strategies × validity + empty DB + edge cases (124/124 total)

Phase 6 — Settings Screen

  • Write ui/settings.py
    • Game enable/disable checkboxes (set_game_active on toggle)
    • Fetch interval display (24 hours, read-only)
    • Manual fetch button (shared on_fetch callback from main.py)
    • Last fetch timestamp + added/skipped per source (colour-coded)
    • DB stats (draws per game + prediction count)
    • Scrollable canvas layout for future growth
  • Wire settings to DB reads/writes via main.py callback injection
  • 14 tests (138/138 total passing)

Phase 7 — Export + Polish

  • Write core/exporter.py
    • export_draws_excel(filepath, game_id, date_from, date_to) — openpyxl, styled header
    • export_draws_csv(filepath, game_id, date_from, date_to) — stdlib csv
    • export_predictions_excel(filepath, game_id) — openpyxl
    • export_predictions_csv(filepath, game_id) — stdlib csv
    • export_frequency_excel(filepath, game_id, last_n) — number + freq + gap
    • create_icon_png(path, size) — valid PNG, stdlib only (struct + zlib)
  • Add Export Excel + Export CSV buttons to History screen (filter-aware)
  • Add Export Excel + Export CSV buttons to Predictor screen (DB predictions)
  • Add Export Frequency button to Analysis screen (respects frequency window)
  • Auto-create assets/icon.png on startup if missing
  • Window min-size set (900×600) + resizable layout (all screens)
  • Error handling — try/except + messagebox.showerror on all export paths
  • 27 tests for exporter (165/165 total passing)

Phase 13 — Ticket Checker

  • Write core/checker.py
    • prize_tier(main_matches, bonus_match) — maps match counts to Jackpot/Match 5/Match 4+Bonus/… tier labels
    • check_ticket(game_id, numbers, bonus) — compares ticket against all draws, returns matches sorted by quality desc
    • parse_numbers(raw) — parses space- or comma-separated user input, raises ValueError on bad input
  • Add "Check Ticket" tab to ui/predictor_ui.py (3rd tab in Notebook)
    • Game dropdown, Numbers entry, Bonus entry, Check / Clear buttons
    • Hint label with accepted input format
    • Results treeview: Draw Date, Draw Numbers, Bonus, Main Hits, Bonus Hit, Prize Tier
    • Row colours: purple=Jackpot, green=≥3 matches or bonus hit, grey=low match
    • Summary bar: "N draws matched • Best: "
    • Input validation: number count, range check, bonus range
  • 27 tests in tests/test_checker.py (254/254 total passing)

Phase 17 — Predictor Power Features

  • Add quick_pick(game_id, exclude=None) to core/predictor.py — pure random, no draw history required
  • Add exclude: set | None = None parameter to all 5 existing strategies + _random_ticket + _fill_to_count
    • Falls back silently to full pool if excluded numbers would leave fewer candidates than main_count
    • Add _safe_pool(game, exclude) helper used by weighted_random and monte_carlo
  • Update ui/predictor_ui.py
    • Add "Quick Pick" to _STRATEGIES and _DESCRIPTIONS
    • Add "Exclude:" entry field to Generate tab bar (comma/space-separated integers)
    • Pass parsed exclusion set to strategy on generate
    • Add "Copy" button — copies all generated tickets to clipboard as formatted text; enabled/disabled with generate/clear
  • Write tests/test_quick_pick.py — 20 tests (326/326 total passing)

Phase 16 — Lottery Ball Display

  • Write ui/widgets.py
    • ball_color(number, is_bonus) -> (bg, fg) — range-based colour lookup (pure, no Tk)
    • BallsBar(tk.Canvas) — draws numbered balls; supports highlights dict for per-ball colour override; inherits parent background
  • Update ui/dashboard.py — last-draw cards use BallsBar for numbers; hot-numbers section uses small-radius (r=11) balls
  • Update ui/history.py — ball detail strip below treeview; shows BallsBar for selected row; clears on filter/populate
  • Update ui/predictor_ui.py
    • Generate tab: detail strip shows selected ticket as balls
    • Check Ticket tab: detail strip shows ticket (green=matched, grey=unmatched) and draw numbers (green=matched) side by side
  • Write tests/test_widgets.py — 12 tests (308/308 total passing)

Phase 15 — Odds Calculator

  • Write core/odds.py
    • total_combinations(main_count, main_max, bonus_count, bonus_max) -> int
    • prize_odds(main_count, main_max, bonus_count, bonus_max) -> list[dict] — generates all prize tiers dynamically, sorted hardest-first by odds
    • game_odds(game_id) -> list[dict] — DB-backed convenience wrapper
    • Handles 0-bonus and 1-bonus games; bonus_count ≥ 2 returns jackpot only
    • Verified against known Powerball (1 in 292,201,338) and Mega Millions (1 in 302,575,350) jackpot odds
  • Add "Odds" tab to ui/analysis.py (8th tab, ttk.Treeview — no matplotlib)
    • Columns: Prize Tier | Odds (1 in X) | Probability
    • Refreshes when game dropdown changes
  • Write tests/test_odds.py — 22 tests (296/296 total passing)

Phase 14 — Custom Game Management

  • Add _BUILTIN_GAMES = {"Powerball", "Mega Millions"} constant to db/models.py
  • Add add_game(name, main_count, main_max, bonus_count, bonus_max) -> int
    • Rejects blank names, duplicate names, and builtin game names (ValueError)
    • Inserts with active=1, returns new row id
  • Add delete_game(game_id) -> bool
    • Refuses builtin games → returns False
    • Refuses games with existing draw records → returns False
    • Cascades: deletes predictions for game first, then removes game → returns True
  • Update ui/settings.py
    • Delete button on each non-builtin game row; warns if draws exist, asks confirm
    • "+ Add Custom Game" button at bottom of Games section
    • _AddGameDialog modal (tk.Toplevel): name + main_count/max + bonus_count/max spinboxes
    • Error label in dialog for inline validation feedback
    • Refresh games list after add or delete
  • Write tests/test_custom_games.py — 20 tests (274/274 total passing)

Phase 12 — Number Search + Next Draw Schedule

  • Number search in History screen
    • Add number param to get_draws_with_game() — SQL: ',' || numbers || ',' LIKE ? for exact boundary matching
    • Add "Number:" entry field to History filter bar; bound to <Return> for quick search
    • Wire through _apply_filter() and _clear_filter()
    • 7 new tests — exact match, boundary false-positive prevention, combined with game/date filter
  • Next draw schedule on Dashboard
    • _next_draw_date(game_name, from_date) — computes next Powerball (Mon/Wed/Sat) or Mega Millions (Tue/Fri) draw date
    • Shown on each Last Draw card as "Next draw: Sat, May 24" in green
    • 6 new tests — day-of-week transitions, unknown game, always-in-future guarantee
  • 227/227 tests passing

Phase 11 — Saved Predictions Viewer

  • Add delete_prediction(pred_id) and delete_all_predictions(game_id=None) to db/models.py
  • Refactor ui/predictor_ui.py to ttk.Notebook with two tabs
    • Generate tab — existing UI unchanged
    • Saved tab — treeview of all DB predictions (ID, Game, Strategy, Numbers, Bonus, Matches, Saved)
    • Match column — shows "X/5" comparing prediction vs last real draw (green if > 0, grey if zero)
    • Game filter dropdown — show all games or filter to one
    • Delete Selected — removes checked rows from DB + refreshes
    • Clear All — confirm dialog, then wipes all (or game-filtered) predictions
    • Auto-refreshes Saved tab after Save to DB
  • _count_matches() helper — set intersection between prediction and last draw numbers
  • tests/test_saved_predictions.py — 17 tests (214/214 total passing)

Phase 10 — Complete Analysis Screen (7 Charts)

  • Extend ui/analysis.py with 4 new chart tabs (was 3, now 7)
    • Pairs — horizontal bar chart, top-20 most common number pairs
    • Odd/Even — bar chart of draw-split distribution (e.g. 3O/2E = 38%)
    • Sum Range — histogram of total draw-sum distribution
    • Deltas — bar chart of gaps between consecutive numbers within draws
  • Add _draw_pairs, _draw_odd_even, _draw_sum_range, _draw_deltas pure functions
  • tests/test_analysis_charts.py — 23 tests using Agg backend (no display needed)
  • 200/200 total tests passing

Phase 9 — Dashboard Screen

  • Write ui/dashboard.py
    • Last Draw Results — card per active game (date, numbers, bonus, multiplier)
    • Database Summary — draw counts per game + prediction total
    • Hot Numbers — top-5 most-frequent per game (last 100 draws) with counts
    • Scrollable canvas layout (same pattern as Settings)
    • on_fetch callback injection for future toolbar wiring
  • Wire Dashboard into main.py — replaces "coming soon" placeholder; opens on launch
  • 12 tests (177/177 total passing)

Phase 8 — Packaging

  • Write core/paths.pyuser_data_dir() + bundled_asset() helpers (frozen-aware)
  • Update db/database.py + core/exporter.py to use user_data_dir() so DB + exports land next to the .exe when frozen
  • Update main.py to use bundled_asset() for icon lookup
  • Write lottosight.spec — directory build, no console, bundles assets/, matplotlib data, openpyxl templates, APScheduler hidden imports
  • Build verified: pyinstaller lottosight.spec --clean succeeds → dist/LottoSight/ (~115 MB)
  • assets/icon.png confirmed present in dist/LottoSight/_internal/assets/
  • Fix .gitignore — un-ignore *.spec, add data/*.db + exports/
  • 165/165 tests still passing after paths refactor
  • Test packaged app on a clean machine (no Python installed)

Notes

  • SQLite DB file stored at: lottosight/data/lottosight.db
  • Exports land in: lottosight/exports/
  • All fetch operations run in background threads to keep UI responsive
  • NY Open Data API supports $limit and $offset params for pagination
  • Texas Lottery CSV requires parsing — columns vary, needs header detection
  • Powerball matrix changed Oct 7, 2015 (5/69+1/26) — pre-2015 data has different pool sizes, flag in DB or filter