Closes #69 - Create migrations/2026-03-12_add_evolution_tables.sql: idempotent PostgreSQL migration (BEGIN/COMMIT, all IF NOT EXISTS) that creates player_season_stats, evolution_track, evolution_card_state, evolution_tier_boost, and evolution_cosmetic tables; adds card.variant (INTEGER NULL DEFAULT NULL), battingcard.image_url (VARCHAR(500) NULL), and pitchingcard.image_url (VARCHAR(500) NULL). - Add tests/test_evolution_migration.py: 16 unit tests validate SQL file structure (tables, columns, indexes, FK references, idempotency); 6 integration tests annotated for PostgreSQL execution when POSTGRES_HOST is set. - Add tests/__init__.py and tests/conftest.py (shared test infrastructure required for import isolation). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
180 lines
7.0 KiB
PL/PgSQL
180 lines
7.0 KiB
PL/PgSQL
-- Migration: Add evolution tables and variant/image_url columns
|
|
-- Date: 2026-03-12
|
|
-- Issue: #69 (WP-04)
|
|
-- Dependencies: WP-01 (evolution models), WP-02 (PlayerSeasonStats model)
|
|
-- Purpose: Create player_season_stats, evolution_track, evolution_card_state,
|
|
-- evolution_tier_boost, evolution_cosmetic tables.
|
|
-- Add card.variant, battingcard.image_url, pitchingcard.image_url columns.
|
|
--
|
|
-- This migration is idempotent: all CREATE TABLE use IF NOT EXISTS,
|
|
-- ADD COLUMN uses IF NOT EXISTS, and all CREATE INDEX use IF NOT EXISTS.
|
|
--
|
|
-- Run on dev first, verify with:
|
|
-- SELECT table_name FROM information_schema.tables
|
|
-- WHERE table_schema = 'public'
|
|
-- AND table_name IN (
|
|
-- 'player_season_stats','evolution_track','evolution_card_state',
|
|
-- 'evolution_tier_boost','evolution_cosmetic'
|
|
-- );
|
|
--
|
|
-- Rollback: See DROP statements at bottom of file
|
|
|
|
-- ============================================
|
|
-- FORWARD MIGRATION
|
|
-- ============================================
|
|
|
|
BEGIN;
|
|
|
|
-- --------------------------------------------
|
|
-- 1. player_season_stats
|
|
-- --------------------------------------------
|
|
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,
|
|
hr INTEGER NOT NULL DEFAULT 0,
|
|
doubles INTEGER NOT NULL DEFAULT 0,
|
|
triples 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, -- pitcher Ks (named k to avoid collision with batting so)
|
|
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 NULL
|
|
);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS player_season_stats_player_team_season_uniq
|
|
ON player_season_stats (player_id, team_id, season);
|
|
|
|
CREATE INDEX IF NOT EXISTS player_season_stats_team_season_idx
|
|
ON player_season_stats (team_id, season);
|
|
|
|
CREATE INDEX IF NOT EXISTS player_season_stats_player_season_idx
|
|
ON player_season_stats (player_id, season);
|
|
|
|
-- --------------------------------------------
|
|
-- 2. evolution_track
|
|
-- --------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS evolution_track (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
card_type VARCHAR(255) NOT NULL UNIQUE, -- batter / sp / rp
|
|
formula VARCHAR(255) NOT NULL,
|
|
t1_threshold INTEGER NOT NULL,
|
|
t2_threshold INTEGER NOT NULL,
|
|
t3_threshold INTEGER NOT NULL,
|
|
t4_threshold INTEGER NOT NULL
|
|
);
|
|
|
|
-- --------------------------------------------
|
|
-- 3. evolution_card_state
|
|
-- --------------------------------------------
|
|
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),
|
|
current_tier INTEGER NOT NULL DEFAULT 0, -- valid range: 0-4
|
|
current_value DOUBLE PRECISION NOT NULL DEFAULT 0.0,
|
|
fully_evolved BOOLEAN NOT NULL DEFAULT FALSE,
|
|
last_evaluated_at TIMESTAMP NULL
|
|
);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS evolution_card_state_player_team_uniq
|
|
ON evolution_card_state (player_id, team_id);
|
|
|
|
-- --------------------------------------------
|
|
-- 4. evolution_tier_boost (Phase 2 stub)
|
|
-- --------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS evolution_tier_boost (
|
|
id SERIAL PRIMARY KEY,
|
|
card_state_id INTEGER NOT NULL REFERENCES evolution_card_state(id) ON DELETE CASCADE
|
|
);
|
|
|
|
-- --------------------------------------------
|
|
-- 5. evolution_cosmetic (Phase 2 stub)
|
|
-- --------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS evolution_cosmetic (
|
|
id SERIAL PRIMARY KEY,
|
|
card_state_id INTEGER NOT NULL REFERENCES evolution_card_state(id) ON DELETE CASCADE
|
|
);
|
|
|
|
-- --------------------------------------------
|
|
-- 6. Add card.variant column
|
|
-- --------------------------------------------
|
|
ALTER TABLE card
|
|
ADD COLUMN IF NOT EXISTS variant INTEGER NULL DEFAULT NULL;
|
|
|
|
-- --------------------------------------------
|
|
-- 7. Add battingcard.image_url column
|
|
-- --------------------------------------------
|
|
ALTER TABLE battingcard
|
|
ADD COLUMN IF NOT EXISTS image_url VARCHAR(500) NULL;
|
|
|
|
-- --------------------------------------------
|
|
-- 8. Add pitchingcard.image_url column
|
|
-- --------------------------------------------
|
|
ALTER TABLE pitchingcard
|
|
ADD COLUMN IF NOT EXISTS image_url VARCHAR(500) NULL;
|
|
|
|
COMMIT;
|
|
|
|
-- ============================================
|
|
-- VERIFICATION QUERIES
|
|
-- ============================================
|
|
-- SELECT table_name FROM information_schema.tables
|
|
-- WHERE table_schema = 'public'
|
|
-- AND table_name IN (
|
|
-- 'player_season_stats','evolution_track','evolution_card_state',
|
|
-- 'evolution_tier_boost','evolution_cosmetic'
|
|
-- );
|
|
--
|
|
-- SELECT indexname, tablename FROM pg_indexes
|
|
-- WHERE tablename IN (
|
|
-- 'player_season_stats','evolution_card_state'
|
|
-- );
|
|
--
|
|
-- SELECT column_name FROM information_schema.columns
|
|
-- WHERE table_name = 'card' AND column_name = 'variant';
|
|
--
|
|
-- SELECT column_name FROM information_schema.columns
|
|
-- WHERE table_name IN ('battingcard','pitchingcard')
|
|
-- AND column_name = 'image_url';
|
|
|
|
-- ============================================
|
|
-- 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;
|