fix reversed A/C chair mapping and update hot/cold on round end
CHAIRS mapping was {1:A, 2:B, 3:C} but the API's country field has
1=C and 3=A. Fixed the mapping in backend and both frontends, added a
startup migration to swap A↔C columns in existing DB data, corrected
multiIf SQL queries that hardcoded the wrong winner→column mapping,
and moved _save_round() to the ENDED status block so hot/cold stats
reflect the latest round immediately.
This commit is contained in:
@@ -33,7 +33,7 @@ VALUES = {1: "A", 2: "2", 3: "3", 4: "4", 5: "5", 6: "6", 7: "7",
|
|||||||
8: "8", 9: "9", 10: "10", 11: "J", 12: "Q", 13: "K", 14: "A"}
|
8: "8", 9: "9", 10: "10", 11: "J", 12: "Q", 13: "K", 14: "A"}
|
||||||
HAND_TYPES = {1: "High Card", 2: "Pair", 3: "Flush", 4: "Straight",
|
HAND_TYPES = {1: "High Card", 2: "Pair", 3: "Flush", 4: "Straight",
|
||||||
5: "Straight Flush", 6: "Trail"}
|
5: "Straight Flush", 6: "Trail"}
|
||||||
CHAIRS = {1: "A", 2: "B", 3: "C"}
|
CHAIRS = {1: "C", 2: "B", 3: "A"}
|
||||||
STATUS_NAMES = {0: "NEW", 1: "BETTING", 2: "REVEALING", 3: "ENDED"}
|
STATUS_NAMES = {0: "NEW", 1: "BETTING", 2: "REVEALING", 3: "ENDED"}
|
||||||
|
|
||||||
# Environment
|
# Environment
|
||||||
|
|||||||
35
app/db.py
35
app/db.py
@@ -13,6 +13,7 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
_client = None
|
_client = None
|
||||||
_lock = threading.Lock()
|
_lock = threading.Lock()
|
||||||
|
_migrations_applied = False
|
||||||
|
|
||||||
|
|
||||||
def get_client():
|
def get_client():
|
||||||
@@ -34,6 +35,34 @@ def get_client():
|
|||||||
return _client
|
return _client
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations():
|
||||||
|
"""Run one-time data migrations on startup."""
|
||||||
|
global _migrations_applied
|
||||||
|
if _migrations_applied:
|
||||||
|
return
|
||||||
|
client = get_client()
|
||||||
|
client.command(
|
||||||
|
"CREATE TABLE IF NOT EXISTS _migrations ("
|
||||||
|
" name String, applied_at DateTime DEFAULT now()"
|
||||||
|
") ENGINE = MergeTree() ORDER BY name"
|
||||||
|
)
|
||||||
|
result = client.query(
|
||||||
|
"SELECT count() FROM _migrations WHERE name = 'swap_ac_chairs'"
|
||||||
|
)
|
||||||
|
if result.result_rows[0][0] == 0:
|
||||||
|
log.info("Running migration: swap_ac_chairs")
|
||||||
|
client.command(
|
||||||
|
"ALTER TABLE games UPDATE "
|
||||||
|
"hand_a = hand_c, hand_c = hand_a, "
|
||||||
|
"bet_a = bet_c, bet_c = bet_a, "
|
||||||
|
"hand_type_a = hand_type_c, hand_type_c = hand_type_a "
|
||||||
|
"WHERE 1=1"
|
||||||
|
)
|
||||||
|
client.insert("_migrations", [["swap_ac_chairs"]], column_names=["name"])
|
||||||
|
log.info("Migration swap_ac_chairs applied")
|
||||||
|
_migrations_applied = True
|
||||||
|
|
||||||
|
|
||||||
def _with_lock(fn):
|
def _with_lock(fn):
|
||||||
"""Decorator to serialize all ClickHouse operations."""
|
"""Decorator to serialize all ClickHouse operations."""
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
@@ -213,7 +242,7 @@ def get_win_distribution() -> dict:
|
|||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
bet_a, bet_b, bet_c,
|
bet_a, bet_b, bet_c,
|
||||||
multiIf(winner = 1, bet_a, winner = 2, bet_b, bet_c) AS winner_bet
|
multiIf(winner = 3, bet_a, winner = 2, bet_b, bet_c) AS winner_bet
|
||||||
FROM games
|
FROM games
|
||||||
WHERE bet_a + bet_b + bet_c > 0
|
WHERE bet_a + bet_b + bet_c > 0
|
||||||
)
|
)
|
||||||
@@ -414,7 +443,7 @@ def get_analytics(period: str = "all") -> dict:
|
|||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
bet_a, bet_b, bet_c,
|
bet_a, bet_b, bet_c,
|
||||||
multiIf(winner = 1, bet_a, winner = 2, bet_b, bet_c) AS winner_bet
|
multiIf(winner = 3, bet_a, winner = 2, bet_b, bet_c) AS winner_bet
|
||||||
FROM games
|
FROM games
|
||||||
{game_where + ' AND' if game_where else 'WHERE'} bet_a + bet_b + bet_c > 0
|
{game_where + ' AND' if game_where else 'WHERE'} bet_a + bet_b + bet_c > 0
|
||||||
)
|
)
|
||||||
@@ -431,7 +460,7 @@ def get_analytics(period: str = "all") -> dict:
|
|||||||
hand_type_result = client.query(
|
hand_type_result = client.query(
|
||||||
f"""
|
f"""
|
||||||
SELECT hand_type, count() AS cnt FROM (
|
SELECT hand_type, count() AS cnt FROM (
|
||||||
SELECT multiIf(winner = 1, hand_type_a, winner = 2, hand_type_b, hand_type_c) AS hand_type
|
SELECT multiIf(winner = 3, hand_type_a, winner = 2, hand_type_b, hand_type_c) AS hand_type
|
||||||
FROM games
|
FROM games
|
||||||
{game_where}
|
{game_where}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -152,6 +152,8 @@ class GamePoller:
|
|||||||
start_ts = self.round_data.get("time_start_ts", 0)
|
start_ts = self.round_data.get("time_start_ts", 0)
|
||||||
self.round_data["duration_s"] = round(time.time() - start_ts)
|
self.round_data["duration_s"] = round(time.time() - start_ts)
|
||||||
|
|
||||||
|
self._save_round()
|
||||||
|
|
||||||
await self.broadcast("round_result", {
|
await self.broadcast("round_result", {
|
||||||
"game_no": gn,
|
"game_no": gn,
|
||||||
"winner": gi.get("gameResult"),
|
"winner": gi.get("gameResult"),
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import signal
|
|||||||
from .server import WebServer
|
from .server import WebServer
|
||||||
from .streamkar_ws import StreamKarWSClient
|
from .streamkar_ws import StreamKarWSClient
|
||||||
from .game_poller import GamePoller
|
from .game_poller import GamePoller
|
||||||
|
from . import db
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
@@ -39,6 +40,7 @@ async def main():
|
|||||||
loop.add_signal_handler(sig, shutdown)
|
loop.add_signal_handler(sig, shutdown)
|
||||||
|
|
||||||
log.info("Starting Teen Patti Live Monitor")
|
log.info("Starting Teen Patti Live Monitor")
|
||||||
|
await loop.run_in_executor(None, db.run_migrations)
|
||||||
log.info("Dashboard: http://localhost:8765")
|
log.info("Dashboard: http://localhost:8765")
|
||||||
|
|
||||||
tasks = [
|
tasks = [
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ const escHtml = s => {
|
|||||||
return d.innerHTML;
|
return d.innerHTML;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CHAIRS = {1:'A', 2:'B', 3:'C'};
|
const CHAIRS = {1:'C', 2:'B', 3:'A'};
|
||||||
const CHAIR_COLORS = {A:'#3b82f6', B:'#ec4899', C:'#f59e0b'};
|
const CHAIR_COLORS = {A:'#3b82f6', B:'#ec4899', C:'#f59e0b'};
|
||||||
const HAND_TYPES = {1:'High Card', 2:'Pair', 3:'Flush', 4:'Straight', 5:'Str. Flush', 6:'Trail'};
|
const HAND_TYPES = {1:'High Card', 2:'Pair', 3:'Flush', 4:'Straight', 5:'Str. Flush', 6:'Trail'};
|
||||||
|
|
||||||
|
|||||||
@@ -775,7 +775,7 @@ const fmtFull = n => {
|
|||||||
return Number(n).toLocaleString();
|
return Number(n).toLocaleString();
|
||||||
};
|
};
|
||||||
|
|
||||||
const CHAIRS = {1:'A', 2:'B', 3:'C'};
|
const CHAIRS = {1:'C', 2:'B', 3:'A'};
|
||||||
const CHAIR_COLORS = {A:'#3b82f6', B:'#ec4899', C:'#f59e0b'};
|
const CHAIR_COLORS = {A:'#3b82f6', B:'#ec4899', C:'#f59e0b'};
|
||||||
const HAND_TYPES = {1:'High Card', 2:'Pair', 3:'Flush', 4:'Straight', 5:'Str. Flush', 6:'Trail'};
|
const HAND_TYPES = {1:'High Card', 2:'Pair', 3:'Flush', 4:'Straight', 5:'Str. Flush', 6:'Trail'};
|
||||||
const HAND_RANK = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6};
|
const HAND_RANK = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6};
|
||||||
|
|||||||
Reference in New Issue
Block a user