- CRITICAL: Fix migration FK refs player(id) → player(player_id) - Remove dead is_start flag from pitching groups (no starts column) - Fix hr → homerun in test make_play helper - Add explanatory comment to ruff.toml - Replace print() with logging in seed script Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
204 lines
7.9 KiB
PL/PgSQL
204 lines
7.9 KiB
PL/PgSQL
-- Migration: Add card evolution tables and column extensions
|
|
-- Date: 2026-03-17
|
|
-- Issue: WP-04
|
|
-- Purpose: Support the Card Evolution system — tracks player season stats,
|
|
-- evolution tracks with tier thresholds, per-card evolution state,
|
|
-- tier-based stat boosts, and cosmetic unlocks. Also extends the
|
|
-- card, battingcard, and pitchingcard tables with variant and
|
|
-- image_url columns required by the evolution display layer.
|
|
--
|
|
-- Run on dev first, verify with:
|
|
-- SELECT count(*) FROM player_season_stats;
|
|
-- SELECT count(*) FROM evolution_track;
|
|
-- SELECT count(*) FROM evolution_card_state;
|
|
-- SELECT count(*) FROM evolution_tier_boost;
|
|
-- SELECT count(*) FROM evolution_cosmetic;
|
|
-- SELECT column_name FROM information_schema.columns
|
|
-- WHERE table_name IN ('card', 'battingcard', 'pitchingcard')
|
|
-- AND column_name IN ('variant', 'image_url')
|
|
-- ORDER BY table_name, column_name;
|
|
--
|
|
-- Rollback: See DROP/ALTER statements at bottom of file
|
|
|
|
-- ============================================
|
|
-- FORWARD MIGRATION
|
|
-- ============================================
|
|
|
|
BEGIN;
|
|
|
|
-- --------------------------------------------
|
|
-- Table 1: player_season_stats
|
|
-- Accumulates per-player per-team per-season
|
|
-- batting and pitching totals for evolution
|
|
-- formula evaluation.
|
|
-- --------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS player_season_stats (
|
|
id SERIAL PRIMARY KEY,
|
|
player_id INTEGER NOT NULL REFERENCES player(player_id) ON DELETE CASCADE,
|
|
team_id INTEGER NOT NULL REFERENCES team(id) ON DELETE CASCADE,
|
|
season INTEGER NOT NULL,
|
|
-- Batting stats
|
|
games_batting INTEGER NOT NULL DEFAULT 0,
|
|
pa INTEGER NOT NULL DEFAULT 0,
|
|
ab INTEGER NOT NULL DEFAULT 0,
|
|
hits INTEGER NOT NULL DEFAULT 0,
|
|
doubles INTEGER NOT NULL DEFAULT 0,
|
|
triples INTEGER NOT NULL DEFAULT 0,
|
|
hr INTEGER NOT NULL DEFAULT 0,
|
|
bb INTEGER NOT NULL DEFAULT 0,
|
|
hbp INTEGER NOT NULL DEFAULT 0,
|
|
so INTEGER NOT NULL DEFAULT 0,
|
|
rbi INTEGER NOT NULL DEFAULT 0,
|
|
runs INTEGER NOT NULL DEFAULT 0,
|
|
sb INTEGER NOT NULL DEFAULT 0,
|
|
cs INTEGER NOT NULL DEFAULT 0,
|
|
-- Pitching stats
|
|
games_pitching INTEGER NOT NULL DEFAULT 0,
|
|
outs INTEGER NOT NULL DEFAULT 0,
|
|
k INTEGER NOT NULL DEFAULT 0,
|
|
bb_allowed INTEGER NOT NULL DEFAULT 0,
|
|
hits_allowed INTEGER NOT NULL DEFAULT 0,
|
|
hr_allowed INTEGER NOT NULL DEFAULT 0,
|
|
wins INTEGER NOT NULL DEFAULT 0,
|
|
losses INTEGER NOT NULL DEFAULT 0,
|
|
saves INTEGER NOT NULL DEFAULT 0,
|
|
holds INTEGER NOT NULL DEFAULT 0,
|
|
blown_saves INTEGER NOT NULL DEFAULT 0,
|
|
-- Meta
|
|
last_game_id INTEGER REFERENCES stratgame(id) ON DELETE SET NULL,
|
|
last_updated_at TIMESTAMP
|
|
);
|
|
|
|
-- One row per player per team per season
|
|
CREATE UNIQUE INDEX IF NOT EXISTS player_season_stats_player_team_season_uniq
|
|
ON player_season_stats (player_id, team_id, season);
|
|
|
|
-- Fast lookup by team + season (e.g. leaderboard queries)
|
|
CREATE INDEX IF NOT EXISTS player_season_stats_team_season_idx
|
|
ON player_season_stats (team_id, season);
|
|
|
|
-- Fast lookup by player across seasons
|
|
CREATE INDEX IF NOT EXISTS player_season_stats_player_season_idx
|
|
ON player_season_stats (player_id, season);
|
|
|
|
-- --------------------------------------------
|
|
-- Table 2: evolution_track
|
|
-- Defines the available evolution tracks
|
|
-- (e.g. "HR Mastery", "Ace SP"), their
|
|
-- metric formula, and the four tier thresholds.
|
|
-- --------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS evolution_track (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) UNIQUE NOT NULL,
|
|
card_type VARCHAR(50) NOT NULL, -- 'batting' or 'pitching'
|
|
formula VARCHAR(255) NOT NULL, -- e.g. 'hr', 'k_per_9', 'ops'
|
|
t1_threshold INTEGER NOT NULL,
|
|
t2_threshold INTEGER NOT NULL,
|
|
t3_threshold INTEGER NOT NULL,
|
|
t4_threshold INTEGER NOT NULL
|
|
);
|
|
|
|
-- --------------------------------------------
|
|
-- Table 3: evolution_card_state
|
|
-- Records each card's current evolution tier,
|
|
-- running metric value, and the track it
|
|
-- belongs to. One state row per card (player
|
|
-- + team combination uniquely identifies a
|
|
-- card in a given season).
|
|
-- --------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS evolution_card_state (
|
|
id SERIAL PRIMARY KEY,
|
|
player_id INTEGER NOT NULL REFERENCES player(player_id) ON DELETE CASCADE,
|
|
team_id INTEGER NOT NULL REFERENCES team(id) ON DELETE CASCADE,
|
|
track_id INTEGER NOT NULL REFERENCES evolution_track(id) ON DELETE CASCADE,
|
|
current_tier INTEGER NOT NULL DEFAULT 0,
|
|
current_value DOUBLE PRECISION NOT NULL DEFAULT 0.0,
|
|
fully_evolved BOOLEAN NOT NULL DEFAULT FALSE,
|
|
last_evaluated_at TIMESTAMP
|
|
);
|
|
|
|
-- One evolution state per card (player + team)
|
|
CREATE UNIQUE INDEX IF NOT EXISTS evolution_card_state_player_team_uniq
|
|
ON evolution_card_state (player_id, team_id);
|
|
|
|
-- --------------------------------------------
|
|
-- Table 4: evolution_tier_boost
|
|
-- Defines the stat boosts unlocked at each
|
|
-- tier within a track. A single tier may
|
|
-- grant multiple boosts (e.g. +1 HR and
|
|
-- +1 power rating).
|
|
-- --------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS evolution_tier_boost (
|
|
id SERIAL PRIMARY KEY,
|
|
track_id INTEGER NOT NULL REFERENCES evolution_track(id) ON DELETE CASCADE,
|
|
tier INTEGER NOT NULL, -- 1-4
|
|
boost_type VARCHAR(50) NOT NULL, -- e.g. 'rating_bump', 'display_only'
|
|
boost_target VARCHAR(50) NOT NULL, -- e.g. 'hr_rating', 'contact_rating'
|
|
boost_value DOUBLE PRECISION NOT NULL DEFAULT 0.0
|
|
);
|
|
|
|
-- Prevent duplicate boost definitions for the same track/tier/type/target
|
|
CREATE UNIQUE INDEX IF NOT EXISTS evolution_tier_boost_track_tier_type_target_uniq
|
|
ON evolution_tier_boost (track_id, tier, boost_type, boost_target);
|
|
|
|
-- --------------------------------------------
|
|
-- Table 5: evolution_cosmetic
|
|
-- Catalogue of unlockable visual treatments
|
|
-- (borders, foils, badges, etc.) tied to
|
|
-- minimum tier requirements.
|
|
-- --------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS evolution_cosmetic (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) UNIQUE NOT NULL,
|
|
tier_required INTEGER NOT NULL DEFAULT 0,
|
|
cosmetic_type VARCHAR(50) NOT NULL, -- e.g. 'border', 'foil', 'badge'
|
|
css_class VARCHAR(255),
|
|
asset_url VARCHAR(500)
|
|
);
|
|
|
|
-- --------------------------------------------
|
|
-- Column extensions for existing tables
|
|
-- --------------------------------------------
|
|
|
|
-- Track which visual variant a card is displaying
|
|
-- (NULL = base card, 1+ = evolved variants)
|
|
ALTER TABLE card ADD COLUMN IF NOT EXISTS variant INTEGER DEFAULT NULL;
|
|
|
|
-- Store pre-rendered or externally-hosted card image URLs
|
|
ALTER TABLE battingcard ADD COLUMN IF NOT EXISTS image_url VARCHAR(500);
|
|
ALTER TABLE pitchingcard ADD COLUMN IF NOT EXISTS image_url VARCHAR(500);
|
|
|
|
COMMIT;
|
|
|
|
-- ============================================
|
|
-- VERIFICATION QUERIES
|
|
-- ============================================
|
|
-- \d player_season_stats
|
|
-- \d evolution_track
|
|
-- \d evolution_card_state
|
|
-- \d evolution_tier_boost
|
|
-- \d evolution_cosmetic
|
|
-- SELECT indexname FROM pg_indexes
|
|
-- WHERE tablename IN (
|
|
-- 'player_season_stats',
|
|
-- 'evolution_card_state',
|
|
-- 'evolution_tier_boost'
|
|
-- )
|
|
-- ORDER BY tablename, indexname;
|
|
-- SELECT column_name, data_type FROM information_schema.columns
|
|
-- WHERE table_name IN ('card', 'battingcard', 'pitchingcard')
|
|
-- AND column_name IN ('variant', 'image_url')
|
|
-- ORDER BY table_name, column_name;
|
|
|
|
-- ============================================
|
|
-- ROLLBACK (if needed)
|
|
-- ============================================
|
|
-- ALTER TABLE pitchingcard DROP COLUMN IF EXISTS image_url;
|
|
-- ALTER TABLE battingcard DROP COLUMN IF EXISTS image_url;
|
|
-- ALTER TABLE card DROP COLUMN IF EXISTS variant;
|
|
-- DROP TABLE IF EXISTS evolution_cosmetic CASCADE;
|
|
-- DROP TABLE IF EXISTS evolution_tier_boost CASCADE;
|
|
-- DROP TABLE IF EXISTS evolution_card_state CASCADE;
|
|
-- DROP TABLE IF EXISTS evolution_track CASCADE;
|
|
-- DROP TABLE IF EXISTS player_season_stats CASCADE;
|