combat testing and polishing in the dev console, many bug fixes

This commit is contained in:
2025-11-27 20:37:53 -06:00
parent 94c4ca9e95
commit dd92cf5991
45 changed files with 8157 additions and 1106 deletions

View File

@@ -0,0 +1,245 @@
#!/usr/bin/env python3
"""
Combat Data Migration Script.
This script migrates existing inline combat encounter data from game_sessions
to the dedicated combat_encounters table.
The migration is idempotent - it's safe to run multiple times. Sessions that
have already been migrated (have active_combat_encounter_id) are skipped.
Usage:
python scripts/migrate_combat_data.py
Note:
- Run this after deploying the new combat database schema
- The application handles automatic migration on-demand, so this is optional
- This script is useful for proactively migrating all data at once
"""
import sys
import os
import json
from pathlib import Path
# Add project root to path
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
from dotenv import load_dotenv
# Load environment variables before importing app modules
load_dotenv()
from app.services.database_service import get_database_service
from app.services.combat_repository import get_combat_repository
from app.models.session import GameSession
from app.models.combat import CombatEncounter
from app.utils.logging import get_logger
logger = get_logger(__file__)
def migrate_inline_combat_encounters() -> dict:
"""
Migrate all inline combat encounters to the dedicated table.
Scans all game sessions for inline combat_encounter data and migrates
them to the combat_encounters table. Updates sessions to use the new
active_combat_encounter_id reference.
Returns:
Dict with migration statistics:
- total_sessions: Number of sessions scanned
- migrated: Number of sessions with combat data migrated
- skipped: Number of sessions already migrated or without combat
- errors: Number of sessions that failed to migrate
"""
db = get_database_service()
repo = get_combat_repository()
stats = {
'total_sessions': 0,
'migrated': 0,
'skipped': 0,
'errors': 0,
'error_details': []
}
print("Scanning game_sessions for inline combat data...")
# Query all sessions (paginated)
offset = 0
limit = 100
while True:
try:
rows = db.list_rows(
table_id='game_sessions',
limit=limit,
offset=offset
)
except Exception as e:
logger.error("Failed to query sessions", error=str(e))
print(f"Error querying sessions: {e}")
break
if not rows:
break
for row in rows:
stats['total_sessions'] += 1
session_id = row.id
try:
# Parse session data
session_json = row.data.get('sessionData', '{}')
session_data = json.loads(session_json)
# Check if already migrated (has reference, no inline data)
if (session_data.get('active_combat_encounter_id') and
not session_data.get('combat_encounter')):
stats['skipped'] += 1
continue
# Check if has inline combat data to migrate
combat_data = session_data.get('combat_encounter')
if not combat_data:
stats['skipped'] += 1
continue
# Parse combat encounter
encounter = CombatEncounter.from_dict(combat_data)
user_id = session_data.get('user_id', row.data.get('userId', ''))
logger.info("Migrating inline combat encounter",
session_id=session_id,
encounter_id=encounter.encounter_id)
# Check if encounter already exists in repository
existing = repo.get_encounter(encounter.encounter_id)
if existing:
# Already migrated, just update session reference
session_data['active_combat_encounter_id'] = encounter.encounter_id
session_data['combat_encounter'] = None
else:
# Save to repository
repo.create_encounter(
encounter=encounter,
session_id=session_id,
user_id=user_id
)
session_data['active_combat_encounter_id'] = encounter.encounter_id
session_data['combat_encounter'] = None
# Update session
db.update_row(
table_id='game_sessions',
row_id=session_id,
data={'sessionData': json.dumps(session_data)}
)
stats['migrated'] += 1
print(f" Migrated: {session_id} -> {encounter.encounter_id}")
except Exception as e:
stats['errors'] += 1
error_msg = f"Session {session_id}: {str(e)}"
stats['error_details'].append(error_msg)
logger.error("Failed to migrate session",
session_id=session_id,
error=str(e))
print(f" Error: {session_id} - {e}")
offset += limit
# Safety check to prevent infinite loop
if offset > 10000:
print("Warning: Stopped after 10000 sessions (safety limit)")
break
return stats
def main():
"""Run the migration."""
print("=" * 60)
print("Code of Conquest - Combat Data Migration")
print("=" * 60)
print()
# Verify environment variables
required_vars = [
'APPWRITE_ENDPOINT',
'APPWRITE_PROJECT_ID',
'APPWRITE_API_KEY',
'APPWRITE_DATABASE_ID'
]
missing_vars = [var for var in required_vars if not os.getenv(var)]
if missing_vars:
print("ERROR: Missing required environment variables:")
for var in missing_vars:
print(f" - {var}")
print()
print("Please ensure your .env file is configured correctly.")
sys.exit(1)
print("Environment configuration:")
print(f" Endpoint: {os.getenv('APPWRITE_ENDPOINT')}")
print(f" Project: {os.getenv('APPWRITE_PROJECT_ID')}")
print(f" Database: {os.getenv('APPWRITE_DATABASE_ID')}")
print()
# Confirm before proceeding
print("This script will migrate inline combat data to the dedicated")
print("combat_encounters table. This operation is safe and idempotent.")
print()
response = input("Proceed with migration? (y/N): ").strip().lower()
if response != 'y':
print("Migration cancelled.")
sys.exit(0)
print()
print("Starting migration...")
print()
try:
stats = migrate_inline_combat_encounters()
print()
print("=" * 60)
print("Migration Results")
print("=" * 60)
print()
print(f"Total sessions scanned: {stats['total_sessions']}")
print(f"Successfully migrated: {stats['migrated']}")
print(f"Skipped (no combat): {stats['skipped']}")
print(f"Errors: {stats['errors']}")
print()
if stats['error_details']:
print("Error details:")
for error in stats['error_details'][:10]: # Show first 10
print(f" - {error}")
if len(stats['error_details']) > 10:
print(f" ... and {len(stats['error_details']) - 10} more")
print()
if stats['errors'] > 0:
print("Some sessions failed to migrate. Check logs for details.")
sys.exit(1)
else:
print("Migration completed successfully!")
except Exception as e:
logger.error("Migration failed", error=str(e))
print()
print(f"MIGRATION FAILED: {str(e)}")
print()
print("Check logs for details.")
sys.exit(1)
if __name__ == '__main__':
main()