Bets verification fixed

parent 7ea5a863
#!/usr/bin/env python3
"""
Script to check recent bets in the database
"""
import sys
from pathlib import Path
# Add the project root to Python path
project_root = Path(__file__).parent
sys.path.insert(0, str(project_root))
from mbetterclient.database.models import BetModel, BetDetailModel
from mbetterclient.database.manager import DatabaseManager
from mbetterclient.config.settings import get_user_data_dir
def check_recent_bets():
"""Check recent bets in the database"""
# Use the default database path
db_path = get_user_data_dir() / "mbetterclient.db"
db_manager = DatabaseManager(str(db_path))
if not db_manager.initialize():
print("Failed to initialize database")
return False
session = db_manager.get_session()
try:
# Get recent bets (last 10)
recent_bets = session.query(BetModel).order_by(BetModel.bet_datetime.desc()).limit(10).all()
print(f'Found {len(recent_bets)} recent bets:')
for bet in recent_bets:
details = session.query(BetDetailModel).filter_by(bet_id=bet.uuid).all()
print(f' Bet {bet.uuid[:8]}... - {len(details)} details - {bet.bet_datetime}')
for detail in details:
print(f' * {detail.outcome} - ${detail.amount}')
return True
except Exception as e:
print(f"Error checking bets: {e}")
return False
finally:
session.close()
db_manager.close()
if __name__ == "__main__":
check_recent_bets()
\ No newline at end of file
#!/usr/bin/env python3
"""
Script to create a bet with one bet detail for every possible outcome on the next match
"""
import sys
from pathlib import Path
from datetime import datetime, timezone
import uuid as uuid_lib
# Add the project root to Python path
project_root = Path(__file__).parent
sys.path.insert(0, str(project_root))
from mbetterclient.database.models import MatchModel, MatchOutcomeModel, BetModel, BetDetailModel
from mbetterclient.database.manager import DatabaseManager
from mbetterclient.config.settings import get_user_data_dir
def create_bet_for_all_outcomes():
"""Create a bet with one bet detail for every possible outcome on the next match"""
# Use the default database path
db_path = get_user_data_dir() / "mbetterclient.db"
db_manager = DatabaseManager(str(db_path))
if not db_manager.initialize():
print("Failed to initialize database")
return False
session = db_manager.get_session()
try:
# Find the next match - look for matches with status 'bet' (available for betting)
# Order by start_time ascending to get the next one
next_match = session.query(MatchModel).filter(
MatchModel.status == 'bet'
).order_by(MatchModel.start_time.asc()).first()
if not next_match:
print("No upcoming matches found with status 'bet' or 'scheduled'")
return False
print(f"Found next match: #{next_match.match_number} - {next_match.fighter1_township} vs {next_match.fighter2_township}")
print(f"Match status: {next_match.status}, Start time: {next_match.start_time}")
# Get all possible outcomes for this match
outcomes = session.query(MatchOutcomeModel).filter_by(match_id=next_match.id).all()
if not outcomes:
print(f"No outcomes found for match {next_match.id}")
return False
print(f"Found {len(outcomes)} possible outcomes:")
for outcome in outcomes:
print(f" - {outcome.column_name}: {outcome.float_value}")
# Create separate bets for each outcome
bet_datetime = datetime.now(timezone.utc)
created_bets = []
for outcome in outcomes:
# Generate UUID for each bet
bet_uuid = str(uuid_lib.uuid4())
# Create the bet record (disable barcode for test bets to avoid conflicts)
new_bet = BetModel(
uuid=bet_uuid,
bet_datetime=bet_datetime,
fixture_id=next_match.fixture_id,
barcode_standard='none',
barcode_data=None
)
session.add(new_bet)
# Create bet detail for this outcome
bet_detail = BetDetailModel(
bet_id=bet_uuid,
match_id=next_match.id,
outcome=outcome.column_name,
amount=1.0, # Fixed amount of 1.0 for each bet
result='pending'
)
session.add(bet_detail)
created_bets.append({
'uuid': bet_uuid,
'outcome': outcome.column_name,
'amount': 1.0
})
# Commit the transaction
try:
session.commit()
print(f"\n✅ Successfully created {len(created_bets)} separate bets")
print(f" - Match: #{next_match.match_number} ({next_match.fighter1_township} vs {next_match.fighter2_township})")
print(f" - Bet datetime: {bet_datetime}")
print(" - Bets created:")
for bet in created_bets:
print(f" * {bet['uuid'][:8]}... - {bet['outcome']} - ${bet['amount']}")
return True
except Exception as commit_error:
print(f"Commit failed: {commit_error}")
session.rollback()
return False
except Exception as e:
print(f"Error creating bet: {e}")
session.rollback()
return False
finally:
session.close()
db_manager.close()
if __name__ == "__main__":
print("Creating bet for all possible outcomes on the next match...")
success = create_bet_for_all_outcomes()
if success:
print("\n🎯 Bet created successfully!")
print("You can now view the bet in the web dashboard.")
else:
print("\n❌ Failed to create bet.")
sys.exit(1)
\ No newline at end of file
#!/usr/bin/env python3
"""
Script to delete all bets from the database
"""
import sys
from pathlib import Path
# Add the project root to Python path
project_root = Path(__file__).parent
sys.path.insert(0, str(project_root))
from mbetterclient.database.models import BetModel, BetDetailModel
from mbetterclient.database.manager import DatabaseManager
from mbetterclient.config.settings import get_user_data_dir
def delete_all_bets():
"""Delete all bets and their details from the database"""
# Use the default database path
db_path = get_user_data_dir() / "mbetterclient.db"
db_manager = DatabaseManager(str(db_path))
if not db_manager.initialize():
print("Failed to initialize database")
return False
session = db_manager.get_session()
try:
# Get count of bets before deletion
total_bets = session.query(BetModel).count()
total_details = session.query(BetDetailModel).count()
if total_bets == 0:
print("No bets found in the database")
return True
print(f"Found {total_bets} bets with {total_details} total bet details")
# Delete all bet details first (due to foreign key constraints)
deleted_details = session.query(BetDetailModel).delete()
print(f"Deleted {deleted_details} bet details")
# Delete all bets
deleted_bets = session.query(BetModel).delete()
print(f"Deleted {deleted_bets} bets")
# Commit the transaction
session.commit()
print("✅ Successfully deleted all bets from the database")
return True
except Exception as e:
print(f"Error deleting bets: {e}")
session.rollback()
return False
finally:
session.close()
db_manager.close()
if __name__ == "__main__":
print("Deleting all bets from the database...")
success = delete_all_bets()
if success:
print("\n🗑️ All bets have been deleted successfully!")
else:
print("\n❌ Failed to delete bets.")
sys.exit(1)
\ No newline at end of file
...@@ -2369,9 +2369,13 @@ class GamesThread(ThreadedComponent): ...@@ -2369,9 +2369,13 @@ class GamesThread(ThreadedComponent):
under_over_result = match.under_over_result if match else None under_over_result = match.under_over_result if match else None
logger.info(f"🎯 [EXTRACTION DEBUG] Stored under_over_result: '{under_over_result}'") logger.info(f"🎯 [EXTRACTION DEBUG] Stored under_over_result: '{under_over_result}'")
# Get UNDER/OVER coefficients from fixture
under_coeff, over_coeff = self._get_fixture_coefficients(fixture_id, session)
logger.info(f"🎯 [EXTRACTION DEBUG] Retrieved coefficients - UNDER: {under_coeff}, OVER: {over_coeff}")
# Calculate UNDER/OVER payouts # Calculate UNDER/OVER payouts
under_payout = self._calculate_payout(match_id, 'UNDER', under_coeff, session) under_payout = self._calculate_payout(match_id, 'UNDER', under_coeff, session) if under_coeff else 0.0
over_payout = self._calculate_payout(match_id, 'OVER', over_coeff, session) over_payout = self._calculate_payout(match_id, 'OVER', over_coeff, session) if over_coeff else 0.0
logger.info(f"🎯 [EXTRACTION DEBUG] UNDER payout: {under_payout:.2f}, OVER payout: {over_payout:.2f}") logger.info(f"🎯 [EXTRACTION DEBUG] UNDER payout: {under_payout:.2f}, OVER payout: {over_payout:.2f}")
# Get redistribution CAP # Get redistribution CAP
...@@ -2423,12 +2427,12 @@ class GamesThread(ThreadedComponent): ...@@ -2423,12 +2427,12 @@ class GamesThread(ThreadedComponent):
winning_outcomes = session.query(ExtractionAssociationModel.outcome_name).filter( winning_outcomes = session.query(ExtractionAssociationModel.outcome_name).filter(
ExtractionAssociationModel.extraction_result == selected_result ExtractionAssociationModel.extraction_result == selected_result
).distinct().all() ).distinct().all()
winning_outcome_names = [outcome.outcome_name for outcome in winning_outcomes] extraction_winning_outcome_names = [outcome.outcome_name for outcome in winning_outcomes]
logger.info(f"🏆 [EXTRACTION DEBUG] Winning outcomes for result '{selected_result}': {winning_outcome_names}") logger.info(f"🏆 [EXTRACTION DEBUG] Winning outcomes for result '{selected_result}': {extraction_winning_outcome_names}")
# Step 8: Update bet results # Step 8: Update bet results
logger.info(f"💾 [EXTRACTION DEBUG] Step 8: Updating bet results for match {match_id}") logger.info(f"💾 [EXTRACTION DEBUG] Step 8: Updating bet results for match {match_id}")
self._update_bet_results(match_id, selected_result, session) self._update_bet_results(match_id, selected_result, under_over_result, extraction_winning_outcome_names, session)
# Step 9: Collect statistics # Step 9: Collect statistics
logger.info(f"📈 [EXTRACTION DEBUG] Step 9: Collecting match statistics") logger.info(f"📈 [EXTRACTION DEBUG] Step 9: Collecting match statistics")
...@@ -2446,7 +2450,7 @@ class GamesThread(ThreadedComponent): ...@@ -2446,7 +2450,7 @@ class GamesThread(ThreadedComponent):
logger.error(f"❌ [EXTRACTION DEBUG] CRITICAL: selected_result is None! This should not happen.") logger.error(f"❌ [EXTRACTION DEBUG] CRITICAL: selected_result is None! This should not happen.")
return self._fallback_result_selection(), [] return self._fallback_result_selection(), []
return selected_result, winning_outcome_names return selected_result, extraction_winning_outcome_names
finally: finally:
session.close() session.close()
...@@ -2523,23 +2527,22 @@ class GamesThread(ThreadedComponent): ...@@ -2523,23 +2527,22 @@ class GamesThread(ThreadedComponent):
logger.error(f"Failed to get coefficient for outcome {outcome}: {e}") logger.error(f"Failed to get coefficient for outcome {outcome}: {e}")
return 1.0 return 1.0
def _update_bet_results(self, match_id: int, selected_result: str, session): def _update_bet_results(self, match_id: int, selected_result: str, under_over_result: Optional[str], extraction_winning_outcome_names: List[str], session):
"""Update bet results for UNDER/OVER and selected result with win amount calculation""" """Update bet results for UNDER/OVER and selected result with win amount calculation"""
try: try:
logger.info(f"DEBUG _update_bet_results: Starting for match {match_id}, selected_result='{selected_result}'") logger.info(f"DEBUG _update_bet_results: Starting for match {match_id}, selected_result='{selected_result}'")
# Initialize variables to avoid UnboundLocalError
extraction_winning_outcome_names = []
match = None
# Get coefficient for the selected result # Get coefficient for the selected result
win_coefficient = self._get_outcome_coefficient(match_id, selected_result, session) win_coefficient = self._get_outcome_coefficient(match_id, selected_result, session)
logger.info(f"DEBUG _update_bet_results: win_coefficient = {win_coefficient}") logger.info(f"DEBUG _update_bet_results: win_coefficient = {win_coefficient}")
# Get the stored UNDER/OVER result from video selection calculation # Use the passed under_over_result
match = session.query(MatchModel).filter_by(id=match_id).first() under_over_outcome = under_over_result
under_over_outcome = match.under_over_result if match else None logger.info(f"DEBUG _update_bet_results: under_over_outcome = '{under_over_outcome}'")
logger.info(f"DEBUG _update_bet_results: under_over_outcome from stored value = '{under_over_outcome}'")
# DEBUG: Log the current match result before updating
if match:
logger.info(f"DEBUG _update_bet_results: Current match.result before formatting = '{match.result}'")
if under_over_outcome: if under_over_outcome:
# UNDER/OVER bet wins # UNDER/OVER bet wins
...@@ -2553,7 +2556,8 @@ class GamesThread(ThreadedComponent): ...@@ -2553,7 +2556,8 @@ class GamesThread(ThreadedComponent):
for bet in under_over_bets: for bet in under_over_bets:
win_amount = bet.amount * win_coefficient win_amount = bet.amount * win_coefficient
bet.set_result('win', win_amount) bet.result = 'win'
bet.win_amount = win_amount
logger.info(f"DEBUG _update_bet_results: Set bet {bet.id} to win with amount {win_amount}") logger.info(f"DEBUG _update_bet_results: Set bet {bet.id} to win with amount {win_amount}")
# Other UNDER/OVER bet loses # Other UNDER/OVER bet loses
...@@ -2585,7 +2589,8 @@ class GamesThread(ThreadedComponent): ...@@ -2585,7 +2589,8 @@ class GamesThread(ThreadedComponent):
for bet in winning_bets: for bet in winning_bets:
win_amount = bet.amount * win_coefficient win_amount = bet.amount * win_coefficient
bet.set_result('win', win_amount) bet.result = 'win'
bet.win_amount = win_amount
logger.info(f"DEBUG _update_bet_results: Set bet {bet.id} to win with amount {win_amount}") logger.info(f"DEBUG _update_bet_results: Set bet {bet.id} to win with amount {win_amount}")
# Update bets for associated winning outcomes to 'win' # Update bets for associated winning outcomes to 'win'
...@@ -2610,7 +2615,8 @@ class GamesThread(ThreadedComponent): ...@@ -2610,7 +2615,8 @@ class GamesThread(ThreadedComponent):
for bet in associated_winning_bets: for bet in associated_winning_bets:
win_amount = bet.amount * associated_coefficient win_amount = bet.amount * associated_coefficient
bet.set_result('win', win_amount) bet.result = 'win'
bet.win_amount = win_amount
logger.info(f"DEBUG _update_bet_results: Set associated bet {bet.id} ({outcome_name}) to win with amount {win_amount}") logger.info(f"DEBUG _update_bet_results: Set associated bet {bet.id} ({outcome_name}) to win with amount {win_amount}")
# Update all other bets to 'lost' # Update all other bets to 'lost'
...@@ -2630,12 +2636,8 @@ class GamesThread(ThreadedComponent): ...@@ -2630,12 +2636,8 @@ class GamesThread(ThreadedComponent):
if match: if match:
logger.info(f"DEBUG _update_bet_results: Before update - match.result = '{match.result}'") logger.info(f"DEBUG _update_bet_results: Before update - match.result = '{match.result}'")
# Get winning outcomes for the selected result from extraction associations # extraction_winning_outcome_names is already passed as parameter
extraction_winning_outcomes = session.query(ExtractionAssociationModel.outcome_name).filter( logger.info(f"DEBUG _update_bet_results: Using passed extraction_winning_outcome_names: {extraction_winning_outcome_names}")
ExtractionAssociationModel.extraction_result == selected_result
).distinct().all()
extraction_winning_outcome_names = [outcome.outcome_name for outcome in extraction_winning_outcomes]
logger.info(f"DEBUG _update_bet_results: Found {len(extraction_winning_outcomes)} winning outcomes from extraction associations for '{selected_result}': {extraction_winning_outcome_names}")
# Get possible outcomes for this match # Get possible outcomes for this match
possible_outcomes = session.query(MatchOutcomeModel.column_name).filter( possible_outcomes = session.query(MatchOutcomeModel.column_name).filter(
...@@ -2653,9 +2655,9 @@ class GamesThread(ThreadedComponent): ...@@ -2653,9 +2655,9 @@ class GamesThread(ThreadedComponent):
logger.info(f"DEBUG _update_bet_results: Set match.result to '{selected_result}'") logger.info(f"DEBUG _update_bet_results: Set match.result to '{selected_result}'")
# Set winning outcomes as JSON array in separate field # Set winning outcomes as JSON array in separate field
if winning_outcome_names: if extraction_winning_outcome_names:
match.winning_outcomes = json.dumps(winning_outcome_names) match.winning_outcomes = json.dumps(extraction_winning_outcome_names)
logger.info(f"DEBUG _update_bet_results: Set match.winning_outcomes to {winning_outcome_names}") logger.info(f"DEBUG _update_bet_results: Set match.winning_outcomes to {extraction_winning_outcome_names}")
else: else:
match.winning_outcomes = None match.winning_outcomes = None
logger.info(f"DEBUG _update_bet_results: No winning outcomes, set match.winning_outcomes to None") logger.info(f"DEBUG _update_bet_results: No winning outcomes, set match.winning_outcomes to None")
......
...@@ -4293,6 +4293,9 @@ def get_cashier_bets(): ...@@ -4293,6 +4293,9 @@ def get_cashier_bets():
session = api_bp.db_manager.get_session() session = api_bp.db_manager.get_session()
try: try:
# Import models
from ..database.models import BetModel, BetDetailModel, MatchModel
# Get all bets for the target date # Get all bets for the target date
# The date picker sends dates in YYYY-MM-DD format representing local dates # The date picker sends dates in YYYY-MM-DD format representing local dates
# Since the user is in UTC+2, we need to find bets from the previous day in UTC # Since the user is in UTC+2, we need to find bets from the previous day in UTC
...@@ -4318,24 +4321,13 @@ def get_cashier_bets(): ...@@ -4318,24 +4321,13 @@ def get_cashier_bets():
logger.info(f"Querying bets for local date {date_param}: naive UTC range {start_datetime} to {end_datetime}") logger.info(f"Querying bets for local date {date_param}: naive UTC range {start_datetime} to {end_datetime}")
# DEBUG: Log all bets in database for debugging
all_bets = session.query(BetModel).all()
logger.info(f"DEBUG: Total bets in database: {len(all_bets)}")
for bet in all_bets:
logger.info(f"DEBUG: Bet {bet.uuid} datetime: {bet.bet_datetime} (type: {type(bet.bet_datetime)}, tz: {bet.bet_datetime.tzinfo if hasattr(bet.bet_datetime, 'tzinfo') else 'naive'})")
bets_query = session.query(BetModel).filter( bets_query = session.query(BetModel).filter(
BetModel.bet_datetime >= start_datetime, BetModel.bet_datetime >= start_datetime,
BetModel.bet_datetime <= end_datetime BetModel.bet_datetime <= end_datetime
).order_by(BetModel.bet_datetime.desc()) ).order_by(BetModel.bet_datetime.desc())
# DEBUG: Log the raw query
logger.info(f"DEBUG: Executing query: {bets_query}")
bets = bets_query.all() bets = bets_query.all()
logger.info(f"DEBUG: Found {len(bets)} bets in database for date {date_param}") logger.info(f"Found {len(bets)} bets in database for date {date_param}")
for bet in bets:
logger.info(f"DEBUG: Bet {bet.uuid} has datetime: {bet.bet_datetime} (type: {type(bet.bet_datetime)}, tz: {bet.bet_datetime.tzinfo if hasattr(bet.bet_datetime, 'tzinfo') else 'naive'})")
bets_data = [] bets_data = []
# Statistics counters # Statistics counters
...@@ -4357,12 +4349,28 @@ def get_cashier_bets(): ...@@ -4357,12 +4349,28 @@ def get_cashier_bets():
bet_data['total_amount'] = bet_total bet_data['total_amount'] = bet_total
total_amount += bet_total total_amount += bet_total
# Determine overall bet status for statistics # Determine overall bet status for statistics using same logic as bet details page
if bet_details: if bet_details:
results = [detail.result for detail in bet_details] won_count = 0
if all(result == 'won' for result in results): lost_count = 0
pending_count = 0
for detail in bet_details:
# Get match information for winning check
match = session.query(MatchModel).filter_by(id=detail.match_id).first()
if is_bet_detail_winning(detail, match, session):
won_count += 1
elif detail.result == 'lost':
lost_count += 1
elif detail.result == 'pending':
pending_count += 1
elif detail.result == 'cancelled':
lost_count += 1 # Treat cancelled as lost for statistics
if won_count > 0 and lost_count == 0 and pending_count == 0:
won_bets += 1 won_bets += 1
elif any(result == 'lost' for result in results): elif lost_count > 0:
lost_bets += 1 lost_bets += 1
else: else:
pending_bets += 1 pending_bets += 1
......
...@@ -499,7 +499,7 @@ function updateBetsTable(data, container) { ...@@ -499,7 +499,7 @@ function updateBetsTable(data, container) {
if (bet.details && bet.details.length > 0) { if (bet.details && bet.details.length > 0) {
const statuses = bet.details.map(detail => detail.result); const statuses = bet.details.map(detail => detail.result);
if (statuses.every(status => status === 'won')) { if (statuses.every(status => status === 'win')) {
overallStatus = 'won'; overallStatus = 'won';
statusBadge = '<span class="badge bg-success"><i class="fas fa-trophy me-1"></i>Won</span>'; statusBadge = '<span class="badge bg-success"><i class="fas fa-trophy me-1"></i>Won</span>';
} else if (statuses.some(status => status === 'lost')) { } else if (statuses.some(status => status === 'lost')) {
......
...@@ -499,7 +499,7 @@ function updateBetsTable(data, container) { ...@@ -499,7 +499,7 @@ function updateBetsTable(data, container) {
if (bet.details && bet.details.length > 0) { if (bet.details && bet.details.length > 0) {
const statuses = bet.details.map(detail => detail.result); const statuses = bet.details.map(detail => detail.result);
if (statuses.every(status => status === 'won')) { if (statuses.every(status => status === 'win')) {
overallStatus = 'won'; overallStatus = 'won';
statusBadge = '<span class="badge bg-success"><i class="fas fa-trophy me-1"></i>Won</span>'; statusBadge = '<span class="badge bg-success"><i class="fas fa-trophy me-1"></i>Won</span>';
} else if (statuses.some(status => status === 'lost')) { } else if (statuses.some(status => status === 'lost')) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment