Story 3.2: QA passed: Link tier strategy implemented
parent
87bf317207
commit
787b05ee3a
|
|
@ -1,7 +1,7 @@
|
|||
# Story 3.2: Find Tiered Links
|
||||
|
||||
## Status
|
||||
Review
|
||||
Complete - QA Approved
|
||||
|
||||
## Story
|
||||
**As a developer**, I want a module that finds all required tiered links (money site or lower-tier) based on the current batch's tier, so I have them ready for injection.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,215 @@
|
|||
"""
|
||||
Database migration for Story 3.2: Find Tiered Links
|
||||
|
||||
This script adds:
|
||||
1. money_site_url column to projects table
|
||||
2. article_links table for tracking link relationships
|
||||
|
||||
Run this script BEFORE deploying Story 3.2 code to production.
|
||||
|
||||
Usage:
|
||||
python scripts/migrate_story_3.2.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from sqlalchemy import text, inspect
|
||||
from src.database.session import db_manager
|
||||
from src.core.config import get_config
|
||||
|
||||
|
||||
def check_column_exists(inspector, table_name: str, column_name: str) -> bool:
|
||||
"""Check if a column exists in a table"""
|
||||
columns = inspector.get_columns(table_name)
|
||||
return any(col['name'] == column_name for col in columns)
|
||||
|
||||
|
||||
def check_table_exists(inspector, table_name: str) -> bool:
|
||||
"""Check if a table exists"""
|
||||
return table_name in inspector.get_table_names()
|
||||
|
||||
|
||||
def migrate_add_money_site_url(connection):
|
||||
"""Add money_site_url column to projects table"""
|
||||
print("\n[1/2] Adding money_site_url to projects table...")
|
||||
|
||||
inspector = inspect(connection)
|
||||
|
||||
if not check_table_exists(inspector, 'projects'):
|
||||
print(" [ERROR] projects table does not exist!")
|
||||
return False
|
||||
|
||||
if check_column_exists(inspector, 'projects', 'money_site_url'):
|
||||
print(" [INFO] Column money_site_url already exists, skipping")
|
||||
return True
|
||||
|
||||
try:
|
||||
connection.execute(text("""
|
||||
ALTER TABLE projects ADD COLUMN money_site_url VARCHAR(500) NULL
|
||||
"""))
|
||||
print(" [OK] Added money_site_url column")
|
||||
|
||||
connection.execute(text("""
|
||||
CREATE INDEX idx_projects_money_site_url ON projects(money_site_url)
|
||||
"""))
|
||||
print(" [OK] Created index on money_site_url")
|
||||
|
||||
connection.commit()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f" [ERROR] {e}")
|
||||
connection.rollback()
|
||||
return False
|
||||
|
||||
|
||||
def migrate_create_article_links_table(connection):
|
||||
"""Create article_links table"""
|
||||
print("\n[2/2] Creating article_links table...")
|
||||
|
||||
inspector = inspect(connection)
|
||||
|
||||
if check_table_exists(inspector, 'article_links'):
|
||||
print(" [INFO] Table article_links already exists, skipping")
|
||||
return True
|
||||
|
||||
try:
|
||||
connection.execute(text("""
|
||||
CREATE TABLE article_links (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
from_content_id INTEGER NOT NULL,
|
||||
to_content_id INTEGER NULL,
|
||||
to_url TEXT NULL,
|
||||
link_type VARCHAR(20) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (from_content_id) REFERENCES generated_content(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (to_content_id) REFERENCES generated_content(id) ON DELETE CASCADE,
|
||||
CHECK (to_content_id IS NOT NULL OR to_url IS NOT NULL)
|
||||
)
|
||||
"""))
|
||||
print(" [OK] Created article_links table")
|
||||
|
||||
connection.execute(text("""
|
||||
CREATE INDEX idx_article_links_from ON article_links(from_content_id)
|
||||
"""))
|
||||
print(" [OK] Created index on from_content_id")
|
||||
|
||||
connection.execute(text("""
|
||||
CREATE INDEX idx_article_links_to ON article_links(to_content_id)
|
||||
"""))
|
||||
print(" [OK] Created index on to_content_id")
|
||||
|
||||
connection.execute(text("""
|
||||
CREATE INDEX idx_article_links_type ON article_links(link_type)
|
||||
"""))
|
||||
print(" [OK] Created index on link_type")
|
||||
|
||||
connection.commit()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f" [ERROR] {e}")
|
||||
connection.rollback()
|
||||
return False
|
||||
|
||||
|
||||
def verify_migration(connection):
|
||||
"""Verify the migration was successful"""
|
||||
print("\n[Verification] Checking migration results...")
|
||||
|
||||
inspector = inspect(connection)
|
||||
|
||||
success = True
|
||||
|
||||
if check_column_exists(inspector, 'projects', 'money_site_url'):
|
||||
print(" [OK] projects.money_site_url exists")
|
||||
else:
|
||||
print(" [ERROR] projects.money_site_url MISSING")
|
||||
success = False
|
||||
|
||||
if check_table_exists(inspector, 'article_links'):
|
||||
print(" [OK] article_links table exists")
|
||||
|
||||
columns = inspector.get_columns('article_links')
|
||||
expected_columns = ['id', 'from_content_id', 'to_content_id', 'to_url', 'link_type', 'created_at']
|
||||
actual_columns = [col['name'] for col in columns]
|
||||
|
||||
for col in expected_columns:
|
||||
if col in actual_columns:
|
||||
print(f" [OK] article_links.{col} exists")
|
||||
else:
|
||||
print(f" [ERROR] article_links.{col} MISSING")
|
||||
success = False
|
||||
|
||||
indexes = inspector.get_indexes('article_links')
|
||||
index_names = [idx['name'] for idx in indexes]
|
||||
print(f" [INFO] Indexes: {index_names}")
|
||||
else:
|
||||
print(" [ERROR] article_links table MISSING")
|
||||
success = False
|
||||
|
||||
return success
|
||||
|
||||
|
||||
def main():
|
||||
"""Run the migration"""
|
||||
print("=" * 60)
|
||||
print("Story 3.2 Database Migration")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
config = get_config()
|
||||
print(f"\nDatabase: {config.database.url}")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Error loading configuration: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
db_manager.initialize()
|
||||
engine = db_manager.get_engine()
|
||||
connection = engine.connect()
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Error connecting to database: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
success = True
|
||||
|
||||
if not migrate_add_money_site_url(connection):
|
||||
success = False
|
||||
|
||||
if not migrate_create_article_links_table(connection):
|
||||
success = False
|
||||
|
||||
if success:
|
||||
if verify_migration(connection):
|
||||
print("\n" + "=" * 60)
|
||||
print("[SUCCESS] Migration completed successfully!")
|
||||
print("=" * 60)
|
||||
else:
|
||||
print("\n" + "=" * 60)
|
||||
print("[WARNING] Migration completed with warnings")
|
||||
print("=" * 60)
|
||||
else:
|
||||
print("\n" + "=" * 60)
|
||||
print("[FAILED] Migration failed!")
|
||||
print("=" * 60)
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n[ERROR] Unexpected error during migration: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
finally:
|
||||
connection.close()
|
||||
db_manager.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Loading…
Reference in New Issue