Readding Files

This commit is contained in:
kiyreload27
2025-12-28 17:11:39 +00:00
parent 72dfc34893
commit 4b944c1a53
1045 changed files with 4793 additions and 0 deletions

154
db/db_init.py Normal file
View File

@@ -0,0 +1,154 @@
"""
Database initialization module
Creates the SQLite database schema for Umamusume support cards
"""
import sqlite3
import os
DB_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "database", "umamusume.db")
def get_conn():
"""Get database connection"""
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
return sqlite3.connect(DB_PATH)
def init_db(reset=False):
"""
Initialize the database with schema
Args:
reset: If True, drops all existing tables first
"""
conn = get_conn()
cur = conn.cursor()
if reset:
print("Resetting database...")
cur.execute("DROP TABLE IF EXISTS deck_slots")
cur.execute("DROP TABLE IF EXISTS user_decks")
cur.execute("DROP TABLE IF EXISTS event_skills")
cur.execute("DROP TABLE IF EXISTS support_events")
cur.execute("DROP TABLE IF EXISTS support_hints")
cur.execute("DROP TABLE IF EXISTS support_effects")
cur.execute("DROP TABLE IF EXISTS owned_cards")
cur.execute("DROP TABLE IF EXISTS support_cards")
# Support Cards - main card info
cur.execute("""
CREATE TABLE IF NOT EXISTS support_cards (
card_id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
rarity TEXT,
card_type TEXT,
max_level INTEGER DEFAULT 50,
gametora_url TEXT UNIQUE,
image_path TEXT
)
""")
# Effects by level - stores effect values at each level
cur.execute("""
CREATE TABLE IF NOT EXISTS support_effects (
effect_id INTEGER PRIMARY KEY AUTOINCREMENT,
card_id INTEGER,
level INTEGER,
effect_name TEXT,
effect_value TEXT,
FOREIGN KEY (card_id) REFERENCES support_cards(card_id)
)
""")
# Support Hints - training skills that can be learned
cur.execute("""
CREATE TABLE IF NOT EXISTS support_hints (
hint_id INTEGER PRIMARY KEY AUTOINCREMENT,
card_id INTEGER,
hint_name TEXT,
hint_description TEXT,
FOREIGN KEY (card_id) REFERENCES support_cards(card_id)
)
""")
# Training Events
cur.execute("""
CREATE TABLE IF NOT EXISTS support_events (
event_id INTEGER PRIMARY KEY AUTOINCREMENT,
card_id INTEGER,
event_name TEXT,
event_type TEXT,
FOREIGN KEY (card_id) REFERENCES support_cards(card_id)
)
""")
# Event Skills
cur.execute("""
CREATE TABLE IF NOT EXISTS event_skills (
skill_id INTEGER PRIMARY KEY AUTOINCREMENT,
event_id INTEGER,
skill_name TEXT,
FOREIGN KEY (event_id) REFERENCES support_events(event_id)
)
""")
# User's owned cards - which cards the user personally owns
cur.execute("""
CREATE TABLE IF NOT EXISTS owned_cards (
owned_id INTEGER PRIMARY KEY AUTOINCREMENT,
card_id INTEGER UNIQUE,
level INTEGER DEFAULT 50,
limit_break INTEGER DEFAULT 0,
owned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (card_id) REFERENCES support_cards(card_id)
)
""")
# User's saved decks
cur.execute("""
CREATE TABLE IF NOT EXISTS user_decks (
deck_id INTEGER PRIMARY KEY AUTOINCREMENT,
deck_name TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
# Cards in each deck (6 slots max)
cur.execute("""
CREATE TABLE IF NOT EXISTS deck_slots (
slot_id INTEGER PRIMARY KEY AUTOINCREMENT,
deck_id INTEGER,
card_id INTEGER,
slot_position INTEGER,
level INTEGER DEFAULT 50,
FOREIGN KEY (deck_id) REFERENCES user_decks(deck_id),
FOREIGN KEY (card_id) REFERENCES support_cards(card_id)
)
""")
# Create indexes for better query performance
cur.execute("CREATE INDEX IF NOT EXISTS idx_effects_card_level ON support_effects(card_id, level)")
cur.execute("CREATE INDEX IF NOT EXISTS idx_hints_card ON support_hints(card_id)")
cur.execute("CREATE INDEX IF NOT EXISTS idx_events_card ON support_events(card_id)")
cur.execute("CREATE INDEX IF NOT EXISTS idx_cards_type ON support_cards(card_type)")
cur.execute("CREATE INDEX IF NOT EXISTS idx_cards_rarity ON support_cards(rarity)")
cur.execute("CREATE INDEX IF NOT EXISTS idx_owned_card ON owned_cards(card_id)")
cur.execute("CREATE INDEX IF NOT EXISTS idx_deck_slots ON deck_slots(deck_id)")
conn.commit()
conn.close()
print("Database initialized successfully!")
def migrate_add_image_path():
"""Add image_path column if it doesn't exist"""
conn = get_conn()
cur = conn.cursor()
try:
cur.execute("ALTER TABLE support_cards ADD COLUMN image_path TEXT")
conn.commit()
print("Added image_path column")
except sqlite3.OperationalError:
pass # Column already exists
conn.close()
if __name__ == "__main__":
init_db(reset=True)

702
db/db_queries.py Normal file
View File

@@ -0,0 +1,702 @@
"""
Database query functions for Umamusume support cards
"""
import sqlite3
import os
import sys
import shutil
if getattr(sys, 'frozen', False):
# In frozen state (exe), we need to ensure the database is in a writable location
# sys.executable points to the .exe file
base_dir = os.path.dirname(sys.executable)
db_dir = os.path.join(base_dir, "database")
DB_PATH = os.path.join(db_dir, "umamusume.db")
# Function to check if DB is effectively empty
def is_db_empty(path):
try:
conn = sqlite3.connect(path)
cur = conn.cursor()
cur.execute("SELECT COUNT(*) FROM support_cards")
count = cur.fetchone()[0]
conn.close()
return count == 0
except:
return True
# Check state: Missing OR Empty
should_copy_seed = False
if not os.path.exists(DB_PATH):
should_copy_seed = True
elif is_db_empty(DB_PATH):
# exists but empty - overwrite it
should_copy_seed = True
if should_copy_seed:
try:
# Ensure directory exists
os.makedirs(db_dir, exist_ok=True)
# Check for bundled seed database
bundled_seed_path = os.path.join(sys._MEIPASS, "database", "umamusume_seed.db")
if os.path.exists(bundled_seed_path):
# Copy seed database to user location (overwrite if exists)
shutil.copy2(bundled_seed_path, DB_PATH)
# Else: will be initialized by get_conn -> init_database
except Exception as e:
pass
else:
DB_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "database", "umamusume.db")
# Add VERSION import
if not getattr(sys, 'frozen', False):
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
try:
from version import VERSION
except ImportError:
VERSION = "2.1.0" # Fallback
def get_conn():
"""Get database connection"""
# Initialize if missing
if not os.path.exists(DB_PATH):
init_database()
# Check for updates and migrate if needed
check_for_updates()
return sqlite3.connect(DB_PATH)
def check_for_updates():
"""Check if database version matches app version, sync if outdated"""
if getattr(sys, 'frozen', False):
bundled_seed_path = os.path.join(sys._MEIPASS, "database", "umamusume_seed.db")
if not os.path.exists(bundled_seed_path):
return
try:
conn = sqlite3.connect(DB_PATH)
cur = conn.cursor()
# Check for metadata table
cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='system_metadata'")
if not cur.fetchone():
# No metadata, likely old version. Create it.
cur.execute("CREATE TABLE IF NOT EXISTS system_metadata (key TEXT PRIMARY KEY, value TEXT)")
cur.execute("INSERT OR REPLACE INTO system_metadata (key, value) VALUES (?, ?)", ('app_version', "0.0.0"))
conn.commit()
db_version = "0.0.0"
else:
cur.execute("SELECT value FROM system_metadata WHERE key='app_version'")
row = cur.fetchone()
db_version = row[0] if row else "0.0.0"
conn.close()
# Compare versions (simple string compare works for semver if zero-padded, but valid enough here)
# Or just check inequality. If different, try to update.
if db_version != VERSION:
sync_from_seed(bundled_seed_path)
except Exception as e:
print(f"Update check failed: {e}")
def sync_from_seed(seed_path):
"""Merge new data from seed into user database"""
print(f"Syncing database from {seed_path}...")
try:
conn = sqlite3.connect(DB_PATH)
cur = conn.cursor()
# Attach seed database
cur.execute("ATTACH DATABASE ? AS seed", (seed_path,))
# 1. Insert New Cards (match by gametora_url)
# We assume gametora_url is unique and stable
cur.execute("""
INSERT INTO main.support_cards (name, rarity, card_type, max_level, gametora_url, image_path)
SELECT name, rarity, card_type, max_level, gametora_url, image_path
FROM seed.support_cards
WHERE gametora_url NOT IN (SELECT gametora_url FROM main.support_cards)
""")
# 2. Sync Child Tables
# Since effects/hints/events don't have stable IDs, we wipe and re-import them for ALL cards.
# But we must map seed_card_id to main_card_id.
# First, ensure we don't break foreign keys temporarily
cur.execute("PRAGMA foreign_keys = OFF")
tables_to_sync = ['support_effects', 'support_hints', 'support_events', 'event_skills']
for table in tables_to_sync:
cur.execute(f"DELETE FROM main.{table}")
# Migrate Support Effects
# Map: seed.card_id -> gametora_url -> main.card_id
cur.execute("""
INSERT INTO main.support_effects (card_id, level, effect_name, effect_value)
SELECT m.card_id, s.level, s.effect_name, s.effect_value
FROM seed.support_effects s
JOIN seed.support_cards sc ON s.card_id = sc.card_id
JOIN main.support_cards m ON sc.gametora_url = m.gametora_url
""")
# Migrate Support Hints
cur.execute("""
INSERT INTO main.support_hints (card_id, hint_name, hint_description)
SELECT m.card_id, s.hint_name, s.hint_description
FROM seed.support_hints s
JOIN seed.support_cards sc ON s.card_id = sc.card_id
JOIN main.support_cards m ON sc.gametora_url = m.gametora_url
""")
# Migrate Support Events
# We need to preserve event_id mapping for event_skills?
# Actually no, we deleted event_skills too.
# But we need to insert events first to get new IDs, then insert skills linking to those new IDs?
# That's tricky in SQL.
# Easier: Insert events, then resolving event_id is hard without a map.
# Alternative: Just copy the tables matching on card_id if we assume card_ids are consistent?
# If user has same cards as seed, IDs might be consistent.
# But if we added a card in the middle, IDs shift.
# Let's assume we can just drop event/skills for now or try to match them.
# The logic below is complex for events+skills because of the 2-level hierarchy.
# Strategy for Events/Skills:
# Since we just deleted them, we can re-insert.
# But main.event_id will be auto-incremented.
# We need to insert event, get ID, then insert skill? No, bulk insert.
# We can't easily map seed.event_id to main.event_id in bulk SQL across DBs easily without a temp table.
# Simplified Approach for Events/Skills:
# Iterate in Python? Slower but safer.
pass # Placeholder for python logic below
# Update Version
cur.execute("INSERT OR REPLACE INTO system_metadata (key, value) VALUES (?, ?)", ('app_version', VERSION))
conn.commit()
# Python Logic for Events/Skills
# Fetch all events from seed with their card's gametora_url
cur.execute("""
SELECT sc.gametora_url, se.event_name, se.event_type, se.event_id
FROM seed.support_events se
JOIN seed.support_cards sc ON se.card_id = sc.card_id
""")
seed_events = cur.fetchall()
# Prepare Skill map: seed_event_id -> list of (skill_name)
cur.execute("SELECT event_id, skill_name FROM seed.event_skills")
seed_skills = {}
for ev_id, sk_name in cur.fetchall():
if ev_id not in seed_skills: seed_skills[ev_id] = []
seed_skills[ev_id].append(sk_name)
# Main Card Map: gametora_url -> main_card_id
cur.execute("SELECT gametora_url, card_id FROM main.support_cards")
url_to_main_id = dict(cur.fetchall())
for url, ev_name, ev_type, seed_ev_id in seed_events:
if url in url_to_main_id:
main_card_id = url_to_main_id[url]
# Insert Event
cur.execute("INSERT INTO main.support_events (card_id, event_name, event_type) VALUES (?, ?, ?)",
(main_card_id, ev_name, ev_type))
new_event_id = cur.lastrowid
# Insert Skills
if seed_ev_id in seed_skills:
for sk_name in seed_skills[seed_ev_id]:
cur.execute("INSERT INTO main.event_skills (event_id, skill_name) VALUES (?, ?)",
(new_event_id, sk_name))
cur.execute("PRAGMA foreign_keys = ON")
conn.commit()
conn.close()
print(f"Database sync complete. Updated to version {VERSION}")
except Exception as e:
print(f"Sync failed: {e}")
def init_database():
"""Initialize fresh database with schema"""
# Ensure directory exists
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
conn = sqlite3.connect(DB_PATH)
cur = conn.cursor()
# Create tables
cur.execute("""
CREATE TABLE IF NOT EXISTS support_cards (
card_id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
rarity TEXT,
card_type TEXT,
max_level INTEGER DEFAULT 50,
gametora_url TEXT UNIQUE,
image_path TEXT
)
""")
cur.execute("""
CREATE TABLE IF NOT EXISTS support_effects (
effect_id INTEGER PRIMARY KEY AUTOINCREMENT,
card_id INTEGER,
level INTEGER,
effect_name TEXT,
effect_value TEXT,
FOREIGN KEY (card_id) REFERENCES support_cards(card_id)
)
""")
cur.execute("""
CREATE TABLE IF NOT EXISTS support_hints (
hint_id INTEGER PRIMARY KEY AUTOINCREMENT,
card_id INTEGER,
hint_name TEXT,
hint_description TEXT,
FOREIGN KEY (card_id) REFERENCES support_cards(card_id)
)
""")
cur.execute("""
CREATE TABLE IF NOT EXISTS support_events (
event_id INTEGER PRIMARY KEY AUTOINCREMENT,
card_id INTEGER,
event_name TEXT,
event_type TEXT,
FOREIGN KEY (card_id) REFERENCES support_cards(card_id)
)
""")
cur.execute("""
CREATE TABLE IF NOT EXISTS event_skills (
skill_id INTEGER PRIMARY KEY AUTOINCREMENT,
event_id INTEGER,
skill_name TEXT,
FOREIGN KEY (event_id) REFERENCES support_events(event_id)
)
""")
# User tables
cur.execute("""
CREATE TABLE IF NOT EXISTS owned_cards (
owned_id INTEGER PRIMARY KEY AUTOINCREMENT,
card_id INTEGER UNIQUE,
level INTEGER DEFAULT 50,
limit_break INTEGER DEFAULT 0,
FOREIGN KEY (card_id) REFERENCES support_cards(card_id)
)
""")
cur.execute("""
CREATE TABLE IF NOT EXISTS user_decks (
deck_id INTEGER PRIMARY KEY AUTOINCREMENT,
deck_name TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
cur.execute("""
CREATE TABLE IF NOT EXISTS deck_slots (
slot_id INTEGER PRIMARY KEY AUTOINCREMENT,
deck_id INTEGER,
card_id INTEGER,
slot_position INTEGER,
level INTEGER DEFAULT 50,
FOREIGN KEY (deck_id) REFERENCES user_decks(deck_id),
FOREIGN KEY (card_id) REFERENCES support_cards(card_id)
)
""")
# Create indexes for performance
cur.execute("CREATE INDEX IF NOT EXISTS idx_effects_card_level ON support_effects(card_id, level)")
cur.execute("CREATE INDEX IF NOT EXISTS idx_hints_card ON support_hints(card_id)")
cur.execute("CREATE INDEX IF NOT EXISTS idx_events_card ON support_events(card_id)")
conn.commit()
conn.close()
# ============================================
# Card Queries
# ============================================
def get_all_cards(rarity_filter=None, type_filter=None, search_term=None, owned_only=False):
"""
Get all support cards with optional filtering
"""
conn = get_conn()
cur = conn.cursor()
query = """
SELECT sc.card_id, sc.name, sc.rarity, sc.card_type, sc.max_level, sc.image_path,
CASE WHEN oc.card_id IS NOT NULL THEN 1 ELSE 0 END as is_owned,
oc.level as owned_level
FROM support_cards sc
LEFT JOIN owned_cards oc ON sc.card_id = oc.card_id
WHERE 1=1
"""
params = []
if rarity_filter:
query += " AND sc.rarity = ?"
params.append(rarity_filter)
if type_filter:
query += " AND sc.card_type = ?"
params.append(type_filter)
if search_term:
query += " AND sc.name LIKE ?"
params.append(f"%{search_term}%")
if owned_only:
query += " AND oc.card_id IS NOT NULL"
query += " ORDER BY sc.rarity DESC, sc.name"
cur.execute(query, params)
rows = cur.fetchall()
conn.close()
return rows
def get_card_by_id(card_id):
"""Get a single card by ID"""
conn = get_conn()
cur = conn.cursor()
cur.execute("""
SELECT sc.card_id, sc.name, sc.rarity, sc.card_type, sc.max_level, sc.gametora_url, sc.image_path,
CASE WHEN oc.card_id IS NOT NULL THEN 1 ELSE 0 END as is_owned,
oc.level as owned_level
FROM support_cards sc
LEFT JOIN owned_cards oc ON sc.card_id = oc.card_id
WHERE sc.card_id = ?
""", (card_id,))
row = cur.fetchone()
conn.close()
return row
def get_card_count():
"""Get total number of cards in database"""
conn = get_conn()
cur = conn.cursor()
cur.execute("SELECT COUNT(*) FROM support_cards")
count = cur.fetchone()[0]
conn.close()
return count
# ============================================
# Effect Queries
# ============================================
def get_effects_at_level(card_id, level):
"""Get all effects for a card at a specific level"""
conn = get_conn()
cur = conn.cursor()
cur.execute("""
SELECT effect_name, effect_value
FROM support_effects
WHERE card_id = ? AND level = ?
ORDER BY effect_name
""", (card_id, level))
rows = cur.fetchall()
conn.close()
return rows
def get_all_effects(card_id):
"""Get all effects for a card at all levels"""
conn = get_conn()
cur = conn.cursor()
cur.execute("""
SELECT level, effect_name, effect_value
FROM support_effects
WHERE card_id = ?
ORDER BY level, effect_name
""", (card_id,))
rows = cur.fetchall()
conn.close()
return rows
def get_unique_effect_names(card_id):
"""Get list of unique effect names for a card"""
conn = get_conn()
cur = conn.cursor()
cur.execute("""
SELECT DISTINCT effect_name
FROM support_effects
WHERE card_id = ?
ORDER BY effect_name
""", (card_id,))
rows = [r[0] for r in cur.fetchall()]
conn.close()
return rows
# ============================================
# Hint Queries
# ============================================
def get_hints(card_id):
"""Get all hints for a card"""
conn = get_conn()
cur = conn.cursor()
cur.execute("""
SELECT hint_name, hint_description
FROM support_hints
WHERE card_id = ?
ORDER BY hint_name
""", (card_id,))
rows = cur.fetchall()
conn.close()
return rows
# ============================================
# Event Queries
# ============================================
def get_events(card_id):
"""Get all events for a card"""
conn = get_conn()
cur = conn.cursor()
cur.execute("""
SELECT event_id, event_name, event_type
FROM support_events
WHERE card_id = ?
ORDER BY event_name
""", (card_id,))
rows = cur.fetchall()
conn.close()
return rows
def get_all_event_skills(card_id):
"""Get all events and their skills for a card"""
conn = get_conn()
cur = conn.cursor()
cur.execute("""
SELECT se.event_name, es.skill_name
FROM support_events se
LEFT JOIN event_skills es ON se.event_id = es.event_id
WHERE se.card_id = ?
ORDER BY se.event_name, es.skill_name
""", (card_id,))
result = {}
for event_name, skill_name in cur.fetchall():
if event_name not in result:
result[event_name] = []
if skill_name:
result[event_name].append(skill_name)
conn.close()
return result
# ============================================
# Owned Cards (Collection) Queries
# ============================================
def is_card_owned(card_id):
"""Check if a card is owned"""
conn = get_conn()
cur = conn.cursor()
cur.execute("SELECT 1 FROM owned_cards WHERE card_id = ?", (card_id,))
result = cur.fetchone() is not None
conn.close()
return result
def set_card_owned(card_id, owned=True, level=50):
"""Set a card as owned or not owned"""
conn = get_conn()
cur = conn.cursor()
if owned:
cur.execute("""
INSERT OR REPLACE INTO owned_cards (card_id, level)
VALUES (?, ?)
""", (card_id, level))
else:
cur.execute("DELETE FROM owned_cards WHERE card_id = ?", (card_id,))
conn.commit()
conn.close()
def update_owned_card_level(card_id, level):
"""Update the level of an owned card"""
conn = get_conn()
cur = conn.cursor()
cur.execute("UPDATE owned_cards SET level = ? WHERE card_id = ?", (level, card_id))
conn.commit()
conn.close()
def get_owned_cards():
"""Get all owned cards"""
conn = get_conn()
cur = conn.cursor()
cur.execute("""
SELECT sc.card_id, sc.name, sc.rarity, sc.card_type, oc.level, sc.image_path
FROM owned_cards oc
JOIN support_cards sc ON oc.card_id = sc.card_id
ORDER BY sc.rarity DESC, sc.name
""")
rows = cur.fetchall()
conn.close()
return rows
def get_owned_count():
"""Get count of owned cards"""
conn = get_conn()
cur = conn.cursor()
cur.execute("SELECT COUNT(*) FROM owned_cards")
count = cur.fetchone()[0]
conn.close()
return count
# ============================================
# Deck Queries
# ============================================
def create_deck(name):
"""Create a new deck"""
conn = get_conn()
cur = conn.cursor()
cur.execute("INSERT INTO user_decks (deck_name) VALUES (?)", (name,))
deck_id = cur.lastrowid
conn.commit()
conn.close()
return deck_id
def get_all_decks():
"""Get all saved decks"""
conn = get_conn()
cur = conn.cursor()
cur.execute("SELECT deck_id, deck_name, created_at FROM user_decks ORDER BY created_at DESC")
rows = cur.fetchall()
conn.close()
return rows
def delete_deck(deck_id):
"""Delete a deck and its slots"""
conn = get_conn()
cur = conn.cursor()
cur.execute("DELETE FROM deck_slots WHERE deck_id = ?", (deck_id,))
cur.execute("DELETE FROM user_decks WHERE deck_id = ?", (deck_id,))
conn.commit()
conn.close()
def add_card_to_deck(deck_id, card_id, slot_position, level=50):
"""Add a card to a deck slot"""
conn = get_conn()
cur = conn.cursor()
# Remove existing card in that slot
cur.execute("DELETE FROM deck_slots WHERE deck_id = ? AND slot_position = ?", (deck_id, slot_position))
# Add new card
cur.execute("""
INSERT INTO deck_slots (deck_id, card_id, slot_position, level)
VALUES (?, ?, ?, ?)
""", (deck_id, card_id, slot_position, level))
conn.commit()
conn.close()
def remove_card_from_deck(deck_id, slot_position):
"""Remove a card from a deck slot"""
conn = get_conn()
cur = conn.cursor()
cur.execute("DELETE FROM deck_slots WHERE deck_id = ? AND slot_position = ?", (deck_id, slot_position))
conn.commit()
conn.close()
def get_deck_cards(deck_id):
"""Get all cards in a deck with their effects"""
conn = get_conn()
cur = conn.cursor()
cur.execute("""
SELECT ds.slot_position, ds.level, sc.card_id, sc.name, sc.rarity, sc.card_type, sc.image_path
FROM deck_slots ds
JOIN support_cards sc ON ds.card_id = sc.card_id
WHERE ds.deck_id = ?
ORDER BY ds.slot_position
""", (deck_id,))
rows = cur.fetchall()
conn.close()
return rows
def get_deck_combined_effects(deck_id):
"""
Get combined effects for all cards in a deck
Returns dict: {effect_name: {'total': value, 'breakdown': [(card_name, value), ...]}}
"""
conn = get_conn()
cur = conn.cursor()
# Get cards in deck with their levels
cur.execute("""
SELECT ds.card_id, ds.level, sc.name
FROM deck_slots ds
JOIN support_cards sc ON ds.card_id = sc.card_id
WHERE ds.deck_id = ?
""", (deck_id,))
deck_cards = cur.fetchall()
combined = {}
for card_id, level, card_name in deck_cards:
# Get effects for this card at this level
cur.execute("""
SELECT effect_name, effect_value
FROM support_effects
WHERE card_id = ? AND level = ?
""", (card_id, level))
for effect_name, effect_value in cur.fetchall():
if effect_name not in combined:
combined[effect_name] = {'total': 0, 'breakdown': []}
# Parse value (remove % and convert to number)
try:
num_value = float(effect_value.replace('%', '').replace('+', ''))
except:
num_value = 0
combined[effect_name]['total'] += num_value
combined[effect_name]['breakdown'].append((card_name, effect_value))
conn.close()
return combined
# ============================================
# Statistics
# ============================================
def get_database_stats():
"""Get statistics about the database"""
conn = get_conn()
cur = conn.cursor()
stats = {}
cur.execute("SELECT COUNT(*) FROM support_cards")
stats['total_cards'] = cur.fetchone()[0]
cur.execute("SELECT rarity, COUNT(*) FROM support_cards GROUP BY rarity")
stats['by_rarity'] = dict(cur.fetchall())
cur.execute("SELECT card_type, COUNT(*) FROM support_cards GROUP BY card_type")
stats['by_type'] = dict(cur.fetchall())
cur.execute("SELECT COUNT(*) FROM support_effects")
stats['total_effects'] = cur.fetchone()[0]
cur.execute("SELECT COUNT(*) FROM owned_cards")
stats['owned_cards'] = cur.fetchone()[0]
cur.execute("SELECT COUNT(*) FROM user_decks")
stats['saved_decks'] = cur.fetchone()[0]
conn.close()
return stats