05/23 Phase 14

This commit is contained in:
2026-05-23 12:58:57 -04:00
parent ae0c64f731
commit 8eea1e976f
9 changed files with 851 additions and 2 deletions
+112 -1
View File
@@ -7,13 +7,14 @@ on_fetch: callable injected by main.py to trigger the shared fetch thread.
"""
import tkinter as tk
from tkinter import ttk
from tkinter import ttk, messagebox
import logging
from db.database import get_db_stats
from db.models import (
get_all_games, get_draw_count, set_game_active,
get_last_fetch_per_source, get_predictions,
add_game, delete_game, _BUILTIN_GAMES,
)
logger = logging.getLogger(__name__)
@@ -131,6 +132,17 @@ class SettingsScreen(ttk.Frame):
foreground="#777777",
).pack(side="left", padx=(16, 0))
if game["name"] not in _BUILTIN_GAMES:
ttk.Button(
row, text="Delete",
command=lambda gid=game["id"], gname=game["name"]: self._delete_game(gid, gname),
).pack(side="left", padx=(12, 0))
ttk.Button(
self._games_body, text="+ Add Custom Game",
command=self._open_add_game_dialog,
).pack(anchor="w", pady=(8, 0))
def _refresh_sources(self):
for w in self._sources_body.winfo_children():
w.destroy()
@@ -190,3 +202,102 @@ class SettingsScreen(ttk.Frame):
self._on_fetch()
else:
self._fetch_msg_var.set("Fetch not available.")
def _open_add_game_dialog(self):
_AddGameDialog(self, on_save=self._refresh_games)
def _delete_game(self, game_id: int, game_name: str):
count = get_draw_count(game_id)
if count > 0:
messagebox.showwarning(
"Cannot Delete",
f"'{game_name}' has {count:,} draw records.\n"
"Delete all draws for this game before removing it.",
)
return
if not messagebox.askyesno("Delete Game",
f"Permanently delete '{game_name}'?"):
return
if delete_game(game_id):
self._refresh_games()
else:
messagebox.showerror("Error", f"Could not delete '{game_name}'.")
class _AddGameDialog(tk.Toplevel):
"""Modal dialog for adding a custom lottery game."""
def __init__(self, parent, on_save):
super().__init__(parent)
self.title("Add Custom Game")
self.resizable(False, False)
self.grab_set() # modal
self._on_save = on_save
self._build()
self.transient(parent)
self.wait_visibility()
self.focus_set()
def _build(self):
pad = {"padx": 10, "pady": 4}
# Name
r = ttk.Frame(self, padding=(14, 14, 14, 4))
r.pack(fill="x")
ttk.Label(r, text="Game name:", width=18, anchor="w").pack(side="left")
self._name_var = tk.StringVar()
ttk.Entry(r, textvariable=self._name_var, width=22).pack(side="left")
# Main balls
r2 = ttk.Frame(self, padding=(14, 4, 14, 4))
r2.pack(fill="x")
ttk.Label(r2, text="Main balls:", width=18, anchor="w").pack(side="left")
self._main_count = tk.IntVar(value=5)
ttk.Spinbox(r2, from_=1, to=10, textvariable=self._main_count,
width=5).pack(side="left")
ttk.Label(r2, text=" out of 1", foreground="#555").pack(side="left")
self._main_max = tk.IntVar(value=69)
ttk.Spinbox(r2, from_=1, to=99, textvariable=self._main_max,
width=5).pack(side="left")
# Bonus balls
r3 = ttk.Frame(self, padding=(14, 4, 14, 4))
r3.pack(fill="x")
ttk.Label(r3, text="Bonus balls:", width=18, anchor="w").pack(side="left")
self._bonus_count = tk.IntVar(value=1)
ttk.Spinbox(r3, from_=0, to=5, textvariable=self._bonus_count,
width=5).pack(side="left")
ttk.Label(r3, text=" out of 1", foreground="#555").pack(side="left")
self._bonus_max = tk.IntVar(value=26)
ttk.Spinbox(r3, from_=0, to=99, textvariable=self._bonus_max,
width=5).pack(side="left")
# Error label
self._err_var = tk.StringVar()
ttk.Label(self, textvariable=self._err_var,
foreground="#c0392b",
padding=(14, 2)).pack(fill="x")
# Buttons
btn_row = ttk.Frame(self, padding=(14, 4, 14, 14))
btn_row.pack(fill="x")
ttk.Button(btn_row, text="Add Game", command=self._save).pack(side="right")
ttk.Button(btn_row, text="Cancel", command=self.destroy).pack(side="right", padx=(0, 6))
def _save(self):
name = self._name_var.get().strip()
if not name:
self._err_var.set("Game name is required.")
return
try:
add_game(
name=name,
main_count=int(self._main_count.get()),
main_max=int(self._main_max.get()),
bonus_count=int(self._bonus_count.get()),
bonus_max=int(self._bonus_max.get()),
)
self._on_save()
self.destroy()
except ValueError as e:
self._err_var.set(str(e))