amarorn / api /user_bets_store.py
beAnalytic's picture
feat: sync main with feature/superbet-live-inplay
5c04262 verified
Raw
History Blame Contribute Delete
3.76 kB
"""Persistência simples de apostas abertas do usuário em JSON local."""
from __future__ import annotations
import json
from datetime import datetime
from pathlib import Path
from typing import Any
from pydantic import BaseModel
from config import settings
_USER_BETS_FILE = Path(settings.lake_root) / "user_open_bets.json"
class PickData(BaseModel):
market: str
outcome: str
target_value: str | None = None
class UserOpenBet(BaseModel):
id: str
event_name: str
home_team: str
away_team: str
picks: list[PickData]
stake: float
odds_placed: float
potential_return: float
cashout_value: float | None = None
ticket_code: str | None = None
status: str = "open"
source: str = "manual"
captured_at: str = ""
superbet_event_id: int | None = None
user_id: str | None = None
def model_post_init(self, __context: Any) -> None:
if not self.captured_at:
self.captured_at = datetime.now().isoformat()
def _load_store() -> dict[str, Any]:
if not _USER_BETS_FILE.exists():
return {"version": 1, "bets": []}
return json.loads(_USER_BETS_FILE.read_text(encoding="utf-8"))
def _save_store(store: dict[str, Any]) -> None:
_USER_BETS_FILE.parent.mkdir(parents=True, exist_ok=True)
_USER_BETS_FILE.write_text(json.dumps(store, ensure_ascii=False, indent=2), encoding="utf-8")
def add_open_bet(ub_kwargs: dict[str, Any]) -> UserOpenBet:
store = _load_store()
ub = UserOpenBet(**ub_kwargs)
store["bets"].append(ub.model_dump(mode="json"))
_save_store(store)
return ub
def list_open_bets(user_id: str | None = None) -> list[UserOpenBet]:
store = _load_store()
bets = [UserOpenBet(**b) for b in store.get("bets", [])]
if user_id:
bets = [b for b in bets if b.user_id == user_id]
return [b for b in bets if b.status == "open"]
def find_open_bet(bet_id: str) -> UserOpenBet | None:
store = _load_store()
for b in store.get("bets", []):
if b.get("id") == bet_id:
return UserOpenBet(**b)
return None
def update_bet_status(bet_id: str, status: str) -> bool:
store = _load_store()
for b in store.get("bets", []):
if b.get("id") == bet_id:
b["status"] = status
_save_store(store)
return True
return False
def get_bets_for_event(
home_team: str,
away_team: str,
*,
status: str = "open",
) -> list[dict]:
"""Retorna apostas do evento filtradas por time, já deduplicadas.
Deduplicação: mesmo stake + odds + market + outcome = duplicata de captura repetida.
"""
store = _load_store()
all_bets = store.get("bets", [])
home_lower = home_team.lower()
away_lower = away_team.lower()
relevant = []
for b in all_bets:
if status and b.get("status") != status:
continue
event = (b.get("event_name") or "").lower()
b_home = (b.get("home_team") or "").lower()
b_away = (b.get("away_team") or "").lower()
# Match flexível por substring nos times ou event_name
match = (
(home_lower in b_home or home_lower in event)
and (away_lower in b_away or away_lower in event)
)
if match:
relevant.append(b)
# Deduplicar por (stake, odds, market, outcome)
seen: set[tuple] = set()
unique: list[dict] = []
for b in relevant:
picks = b.get("picks") or []
key = (
b.get("stake"),
b.get("odds_placed"),
picks[0].get("market") if picks else "",
picks[0].get("outcome") if picks else "",
)
if key not in seen:
seen.add(key)
unique.append(b)
return unique