FigFamFamilyPlinkoScoreboard/app.py

162 lines
5 KiB
Python

#!/usr/bin/env python3
"""
Fig Fam Fantasy Plinko Score Tracker
A local web app for tracking player scores with real-time updates
"""
import sqlite3
import json
from flask import Flask, render_template, request, jsonify, Response
from threading import Lock
import time
app = Flask(__name__)
db_lock = Lock()
# Database Setup
def init_db():
"""Initialize SQLite database with players table"""
with sqlite3.connect('database.db') as conn:
conn.execute('''
CREATE TABLE IF NOT EXISTS players (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL DEFAULT '',
score INTEGER DEFAULT 0
)
''')
# Initialize 14 players if empty
count = conn.execute('SELECT COUNT(*) FROM players').fetchone()[0]
if count == 0:
for i in range(1, 15):
conn.execute(
'INSERT INTO players (id, name, score) VALUES (?, ?, ?)',
(i, f'Player {i}', 0)
)
conn.commit()
# Database Operations
def get_all_players():
"""Retrieve all players sorted by ID for input page"""
with sqlite3.connect('database.db') as conn:
conn.row_factory = sqlite3.Row
return [dict(row) for row in conn.execute(
'SELECT * FROM players ORDER BY id'
).fetchall()]
def get_players_by_score():
"""Retrieve all players sorted by score (highest first) for scoreboard"""
with sqlite3.connect('database.db') as conn:
conn.row_factory = sqlite3.Row
return [dict(row) for row in conn.execute(
'SELECT * FROM players ORDER BY score DESC, name ASC'
).fetchall()]
def update_player(player_id, name=None, score=None):
"""Update player name and/or score"""
with db_lock:
with sqlite3.connect('database.db') as conn:
if name is not None and score is not None:
conn.execute(
'UPDATE players SET name = ?, score = ? WHERE id = ?',
(name, score, player_id)
)
elif name is not None:
conn.execute(
'UPDATE players SET name = ? WHERE id = ?',
(name, player_id)
)
elif score is not None:
conn.execute(
'UPDATE players SET score = ? WHERE id = ?',
(score, player_id)
)
conn.commit()
# Web Routes
@app.route('/input')
def input_page():
"""Mobile-friendly score input page"""
players = get_all_players()
return render_template('input.html', players=players)
@app.route('/')
def scoreboard():
"""TV display scoreboard page"""
players = get_players_by_score() # Sort by score for display
return render_template('scoreboard.html', players=players)
@app.route('/api/players')
def api_get_players():
"""API endpoint to get all players (sorted by ID for input)"""
return jsonify(get_all_players())
@app.route('/api/update', methods=['POST'])
def api_update_player():
"""API endpoint to update player data"""
data = request.get_json()
player_id = data.get('id')
name = data.get('name')
score = data.get('score')
if player_id and (name is not None or score is not None):
update_player(player_id, name, score)
return jsonify({'success': True})
return jsonify({'success': False, 'error': 'Invalid data'})
@app.route('/events')
def events():
"""Server-Sent Events for real-time scoreboard updates"""
def event_stream():
last_data = None
while True:
players = get_players_by_score() # Sort by score for real-time updates
current_data = json.dumps(players)
# Send update only if data changed
if current_data != last_data:
yield f"data: {current_data}\n\n"
last_data = current_data
time.sleep(1) # Check every second
return Response(
event_stream(),
mimetype='text/plain',
headers={
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
}
)
@app.route('/reset', methods=['GET', 'POST'])
def reset_database():
"""Reset database to initial state"""
try:
with sqlite3.connect('database.db') as conn:
# Clear existing data
conn.execute('DELETE FROM players')
# Re-initialize with default players
for i in range(1, 15):
conn.execute(
'INSERT INTO players (id, name, score) VALUES (?, ?, ?)',
(i, f'Player {i}', 0)
)
conn.commit()
return jsonify({
'success': True,
'message': 'Database reset successfully',
'players_created': 14
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
if __name__ == '__main__':
init_db()
app.run(host='0.0.0.0', port=8080, debug=True)