Addition Of Files
This commit is contained in:
154
db/db_init.py
Normal file
154
db/db_init.py
Normal 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
702
db/db_queries.py
Normal 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
|
||||
Reference in New Issue
Block a user