Fix multiple today fixtures creation and reset fixtures foreign key error

- Fix _get_or_create_today_fixture() to use created_at as fallback when start_time is NULL
- Replace _initialize_new_fixture() calls with _get_or_create_today_fixture() in _handle_start_game()
- Fix reset_fixtures() to use raw SQL DELETE statements for proper MySQL foreign key handling
- Delete tables in correct order: extraction_stats, bets_details, bets, match_outcomes, matches, match_outcomes_templates, matches_templates
parent 074153e0
......@@ -466,7 +466,8 @@ class GamesThread(ThreadedComponent):
"""Get existing today's fixture or create a new one.
This method ensures that new matches are created in today's fixture,
not in yesterday's fixture.
not in yesterday's fixture. It uses multiple strategies to find existing
today fixtures to prevent creating duplicates.
"""
try:
session = self.db_manager.get_session()
......@@ -481,17 +482,29 @@ class GamesThread(ThreadedComponent):
utc_start = venue_to_utc_datetime(venue_start, self.db_manager)
utc_end = venue_to_utc_datetime(venue_end, self.db_manager)
# Check if there's already a fixture with today's matches
today_match = session.query(MatchModel).filter(
# Strategy 1: Check for matches with start_time in today's range
today_match_with_start_time = session.query(MatchModel).filter(
MatchModel.start_time.isnot(None),
MatchModel.start_time >= utc_start,
MatchModel.start_time < utc_end,
MatchModel.active_status == True
).order_by(MatchModel.created_at.desc()).first()
if today_match:
logger.info(f"Found existing today fixture: {today_match.fixture_id}")
return today_match.fixture_id
if today_match_with_start_time:
logger.info(f"Found existing today fixture (by start_time): {today_match_with_start_time.fixture_id}")
return today_match_with_start_time.fixture_id
# Strategy 2: Check for matches created today (using created_at as fallback)
# This catches matches that were just created and don't have start_time yet
today_match_by_created = session.query(MatchModel).filter(
MatchModel.created_at >= utc_start,
MatchModel.created_at < utc_end,
MatchModel.active_status == True
).order_by(MatchModel.created_at.asc()).first()
if today_match_by_created:
logger.info(f"Found existing today fixture (by created_at): {today_match_by_created.fixture_id}")
return today_match_by_created.fixture_id
# No today fixture exists - create a new one
logger.info("No today fixture found - creating new fixture")
......@@ -826,18 +839,18 @@ class GamesThread(ThreadedComponent):
is_yesterday_fixture = self._is_fixture_from_yesterday(fixture_id, session)
if is_yesterday_fixture:
# Fixture is from yesterday - create new fixture for today but activate yesterday first
logger.info(f"Fixture {fixture_id} is from yesterday - creating today fixture and activating yesterday to play remaining matches first")
new_fixture_id = self._initialize_new_fixture()
# Fixture is from yesterday - get or create today fixture but activate yesterday first
logger.info(f"Fixture {fixture_id} is from yesterday - getting/creating today fixture and activating yesterday to play remaining matches first")
new_fixture_id = self._get_or_create_today_fixture()
if new_fixture_id:
logger.info(f"Created today fixture {new_fixture_id} - will play yesterday matches first, then switch to today")
logger.info(f"Today fixture {new_fixture_id} ready - will play yesterday matches first, then switch to today")
# Store the today fixture ID for later use
self.pending_today_fixture_id = new_fixture_id
# Activate yesterday fixture to play remaining matches
self._activate_fixture(fixture_id, message)
return
else:
logger.warning("Could not create today fixture - activating yesterday fixture as fallback")
logger.warning("Could not get/create today fixture - activating yesterday fixture as fallback")
self._activate_fixture(fixture_id, message)
return
else:
......@@ -916,33 +929,33 @@ class GamesThread(ThreadedComponent):
).count()
if completed_count == total_fixture_matches:
# All matches completed, create new fixture from templates
logger.info(f"All {total_fixture_matches} matches in fixture {fixture_id} are completed - creating new fixture from templates")
new_fixture_id = self._initialize_new_fixture()
# All matches completed, get or create today fixture
logger.info(f"All {total_fixture_matches} matches in fixture {fixture_id} are completed - getting/creating today fixture")
new_fixture_id = self._get_or_create_today_fixture()
if new_fixture_id:
self._activate_fixture(new_fixture_id, message)
return
else:
logger.warning("Could not create new fixture from templates")
self._send_response(message, "error", "Could not create new fixture")
logger.warning("Could not get/create today fixture")
self._send_response(message, "error", "Could not get/create today fixture")
return
else:
# Some matches are not completed, check if fixture is from yesterday
is_yesterday_fixture = self._is_fixture_from_yesterday(fixture_id, session)
if is_yesterday_fixture:
# Fixture is from yesterday - activate yesterday fixture first to play remaining matches, create today fixture for later
logger.info(f"Fixture {fixture_id} is from yesterday and has remaining matches - activating yesterday fixture first, will create today fixture after completion")
new_fixture_id = self._initialize_new_fixture()
# Fixture is from yesterday - activate yesterday fixture first to play remaining matches, get/create today fixture for later
logger.info(f"Fixture {fixture_id} is from yesterday and has remaining matches - activating yesterday fixture first, will get/create today fixture after completion")
new_fixture_id = self._get_or_create_today_fixture()
if new_fixture_id:
# Store the today fixture ID for later use
self.pending_today_fixture_id = new_fixture_id
logger.info(f"Created today fixture {new_fixture_id} - will play yesterday matches first, then switch to today")
logger.info(f"Today fixture {new_fixture_id} ready - will play yesterday matches first, then switch to today")
# Activate yesterday fixture to play remaining matches
self._activate_fixture(fixture_id, message)
return
else:
logger.warning("Could not create today fixture - activating yesterday fixture as fallback")
logger.warning("Could not get/create today fixture - activating yesterday fixture as fallback")
self._activate_fixture(fixture_id, message)
return
else:
......@@ -985,13 +998,13 @@ class GamesThread(ThreadedComponent):
else:
logger.info("No matches available in database - creating new fixture from templates")
# Create new fixture from templates
new_fixture_id = self._initialize_new_fixture()
# Get or create today fixture
new_fixture_id = self._get_or_create_today_fixture()
if new_fixture_id:
self._activate_fixture(new_fixture_id, message)
return
else:
logger.warning("Could not create new fixture from templates - waiting for templates to become available")
logger.warning("Could not get or create today fixture - waiting for templates to become available")
self._send_response(message, "waiting_for_downloads", "Waiting for match templates to be downloaded and validated")
return
......
......@@ -4370,20 +4370,34 @@ def reset_fixtures():
extraction_stats_count = session.query(ExtractionStatsModel).count()
# Delete in correct order to handle foreign key constraints
# Use raw SQL to ensure proper deletion with MySQL foreign key constraints
# 1. Delete extraction_stats first (references matches)
deleted_extraction_stats = session.query(ExtractionStatsModel).delete()
deleted_extraction_stats = session.execute(text("DELETE FROM extraction_stats"))
session.commit()
# 2. Delete bet_details first (references both bets and matches)
deleted_bet_details = session.execute(text("DELETE FROM bets_details"))
session.commit()
# 3. Delete bets
deleted_bets = session.execute(text("DELETE FROM bets"))
session.commit()
# 4. Delete match_outcomes (references matches)
deleted_outcomes = session.execute(text("DELETE FROM match_outcomes"))
session.commit()
# 2. Delete bets (will cascade to bet_details due to CASCADE constraint)
deleted_bets = session.query(BetModel).delete()
# 5. Delete matches
deleted_matches = session.execute(text("DELETE FROM matches"))
session.commit()
# 3. Delete matches (will cascade to match_outcomes due to CASCADE constraint)
deleted_matches = session.query(MatchModel).delete()
# 6. Delete match_outcomes_templates (references matches_templates)
deleted_template_outcomes = session.execute(text("DELETE FROM match_outcomes_templates"))
session.commit()
# 4. Delete match templates (will cascade to match_outcomes_templates due to CASCADE constraint)
deleted_templates = session.query(MatchTemplateModel).delete()
# 7. Delete match templates
deleted_templates = session.execute(text("DELETE FROM matches_templates"))
session.commit()
# Clear ZIP files from persistent storage
......
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