Files
SpotifyRecAlg/swingmusic/migrations/update_tracking_migration.py
T
Tomas Dvorak 6e8fedf534 first commit
2026-04-13 17:46:58 +02:00

345 lines
15 KiB
Python

"""
Migration for Update Tracking System Tables
This migration creates all the necessary tables for the artist update
tracking system, including follows, releases, notifications, and preferences.
"""
import logging
from swingmusic.db import db
from swingmusic.migrations.base import Migration
logger = logging.getLogger(__name__)
class Migration001UpdateTracking(Migration):
"""
Create tables for the update tracking system
"""
@staticmethod
def migrate():
"""
Create all update tracking tables
"""
logger.info("Starting update tracking migration")
try:
# Create artist_follows table
logger.info("Creating artist_follows table")
db.session.execute("""
CREATE TABLE IF NOT EXISTS artist_follows (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
artist_id TEXT NOT NULL UNIQUE,
artist_name TEXT NOT NULL,
follow_level TEXT NOT NULL DEFAULT 'followed',
auto_download_new_releases BOOLEAN DEFAULT FALSE,
preferred_quality TEXT DEFAULT 'flac',
notification_preferences TEXT DEFAULT '{}',
follow_date DATETIME DEFAULT CURRENT_TIMESTAMP,
last_check_date DATETIME NULL,
FOREIGN KEY (user_id) REFERENCES users (id)
)
""")
# Create release_updates table
logger.info("Creating release_updates table")
db.session.execute("""
CREATE TABLE IF NOT EXISTS release_updates (
id INTEGER PRIMARY KEY AUTOINCREMENT,
release_id TEXT NOT NULL UNIQUE,
artist_id TEXT NOT NULL,
artist_name TEXT NOT NULL,
release_title TEXT NOT NULL,
release_type TEXT NOT NULL,
release_date DATE NOT NULL,
spotify_url TEXT NOT NULL,
cover_image_url TEXT NULL,
total_tracks INTEGER NOT NULL,
popularity INTEGER DEFAULT 0,
explicit BOOLEAN DEFAULT FALSE,
discovered_at DATETIME DEFAULT CURRENT_TIMESTAMP,
processed_at DATETIME NULL,
download_status TEXT DEFAULT 'pending',
auto_downloaded BOOLEAN DEFAULT FALSE,
notification_sent BOOLEAN DEFAULT FALSE
)
""")
# Create update_notifications table
logger.info("Creating update_notifications table")
db.session.execute("""
CREATE TABLE IF NOT EXISTS update_notifications (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
release_id TEXT NOT NULL,
notification_type TEXT NOT NULL,
sent_at DATETIME DEFAULT CURRENT_TIMESTAMP,
opened_at DATETIME NULL,
action_taken TEXT NULL,
FOREIGN KEY (user_id) REFERENCES users (id),
FOREIGN KEY (release_id) REFERENCES release_updates (release_id)
)
""")
# Create update_monitoring_preferences table
logger.info("Creating update_monitoring_preferences table")
db.session.execute("""
CREATE TABLE IF NOT EXISTS update_monitoring_preferences (
user_id INTEGER PRIMARY KEY,
enable_artist_monitoring BOOLEAN DEFAULT TRUE,
check_frequency TEXT DEFAULT 'daily',
auto_download_favorites BOOLEAN DEFAULT FALSE,
auto_download_followed BOOLEAN DEFAULT FALSE,
max_auto_downloads_per_week INTEGER DEFAULT 5,
quality_preference TEXT DEFAULT 'flac',
storage_limit_mb INTEGER DEFAULT 10240,
notification_channels TEXT DEFAULT '{}',
exclude_explicit BOOLEAN DEFAULT FALSE,
preferred_release_types TEXT DEFAULT '["album", "ep", "single"]',
FOREIGN KEY (user_id) REFERENCES users (id)
)
""")
# Create download_tasks table
logger.info("Creating download_tasks table")
db.session.execute("""
CREATE TABLE IF NOT EXISTS download_tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
release_id TEXT NOT NULL,
track_id TEXT NOT NULL,
track_title TEXT NOT NULL,
artist_name TEXT NOT NULL,
album_name TEXT NOT NULL,
spotify_url TEXT NOT NULL,
quality_preference TEXT DEFAULT 'flac',
status TEXT DEFAULT 'pending',
priority TEXT DEFAULT 'normal',
progress INTEGER DEFAULT 0,
file_path TEXT NULL,
error_message TEXT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
started_at DATETIME NULL,
completed_at DATETIME NULL,
auto_downloaded BOOLEAN DEFAULT FALSE,
added_to_library BOOLEAN DEFAULT FALSE,
FOREIGN KEY (release_id) REFERENCES release_updates (release_id)
)
""")
# Create artist_follow_history table
logger.info("Creating artist_follow_history table")
db.session.execute("""
CREATE TABLE IF NOT EXISTS artist_follow_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
artist_id TEXT NOT NULL,
artist_name TEXT NOT NULL,
action TEXT NOT NULL,
old_level TEXT NULL,
new_level TEXT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)
)
""")
# Create release_update_history table
logger.info("Creating release_update_history table")
db.session.execute("""
CREATE TABLE IF NOT EXISTS release_update_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
release_id TEXT NOT NULL,
artist_id TEXT NOT NULL,
artist_name TEXT NOT NULL,
release_title TEXT NOT NULL,
release_type TEXT NOT NULL,
action TEXT NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
metadata TEXT NULL
)
""")
# Create update_tracking_stats table
logger.info("Creating update_tracking_stats table")
db.session.execute("""
CREATE TABLE IF NOT EXISTS update_tracking_stats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
stat_date DATE NOT NULL,
total_followed_artists INTEGER DEFAULT 0,
new_releases_discovered INTEGER DEFAULT 0,
auto_downloads_completed INTEGER DEFAULT 0,
manual_downloads_completed INTEGER DEFAULT 0,
notifications_sent INTEGER DEFAULT 0,
notifications_opened INTEGER DEFAULT 0,
storage_used_mb INTEGER DEFAULT 0,
FOREIGN KEY (user_id) REFERENCES users (id),
UNIQUE(user_id, stat_date)
)
""")
# Create indexes for better performance
logger.info("Creating indexes")
# Indexes for artist_follows
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_artist_follows_user_id ON artist_follows(user_id)
""")
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_artist_follows_artist_id ON artist_follows(artist_id)
""")
# Indexes for release_updates
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_release_updates_artist_id ON release_updates(artist_id)
""")
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_release_updates_release_date ON release_updates(release_date)
""")
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_release_updates_discovered_at ON release_updates(discovered_at)
""")
# Indexes for update_notifications
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_update_notifications_user_id ON update_notifications(user_id)
""")
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_update_notifications_release_id ON update_notifications(release_id)
""")
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_update_notifications_sent_at ON update_notifications(sent_at)
""")
# Indexes for download_tasks
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_download_tasks_release_id ON download_tasks(release_id)
""")
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_download_tasks_status ON download_tasks(status)
""")
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_download_tasks_priority ON download_tasks(priority)
""")
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_download_tasks_created_at ON download_tasks(created_at)
""")
# Indexes for history tables
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_artist_follow_history_user_id ON artist_follow_history(user_id)
""")
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_artist_follow_history_timestamp ON artist_follow_history(timestamp)
""")
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_release_update_history_release_id ON release_update_history(release_id)
""")
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_release_update_history_timestamp ON release_update_history(timestamp)
""")
# Indexes for stats
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_update_tracking_stats_user_id ON update_tracking_stats(user_id)
""")
db.session.execute("""
CREATE INDEX IF NOT EXISTS idx_update_tracking_stats_stat_date ON update_tracking_stats(stat_date)
""")
# Commit the transaction
db.session.commit()
logger.info("Update tracking migration completed successfully")
except Exception as e:
logger.error(f"Error during update tracking migration: {e}")
db.session.rollback()
raise
class Migration002UpdateTrackingTriggers(Migration):
"""
Create triggers for update tracking system
"""
@staticmethod
def migrate():
"""
Create triggers for automatic history tracking
"""
logger.info("Creating update tracking triggers")
try:
# Trigger for artist follow history
db.session.execute("""
CREATE TRIGGER IF NOT EXISTS artist_follow_history_insert
AFTER INSERT ON artist_follows
BEGIN
INSERT INTO artist_follow_history
(user_id, artist_id, artist_name, action, new_level, timestamp)
VALUES
(NEW.user_id, NEW.artist_id, NEW.artist_name, 'follow', NEW.follow_level, CURRENT_TIMESTAMP);
END
""")
# Trigger for artist unfollow history
db.session.execute("""
CREATE TRIGGER IF NOT EXISTS artist_follow_history_delete
AFTER DELETE ON artist_follows
BEGIN
INSERT INTO artist_follow_history
(user_id, artist_id, artist_name, action, old_level, timestamp)
VALUES
(OLD.user_id, OLD.artist_id, OLD.artist_name, 'unfollow', OLD.follow_level, CURRENT_TIMESTAMP);
END
""")
# Trigger for artist follow level change
db.session.execute("""
CREATE TRIGGER IF NOT EXISTS artist_follow_history_update
AFTER UPDATE ON artist_follows
WHEN OLD.follow_level != NEW.follow_level
BEGIN
INSERT INTO artist_follow_history
(user_id, artist_id, artist_name, action, old_level, new_level, timestamp)
VALUES
(NEW.user_id, NEW.artist_id, NEW.artist_name, 'level_change', OLD.follow_level, NEW.follow_level, CURRENT_TIMESTAMP);
END
""")
# Trigger for release update discovery
db.session.execute("""
CREATE TRIGGER IF NOT EXISTS release_update_discovered
AFTER INSERT ON release_updates
BEGIN
INSERT INTO release_update_history
(release_id, artist_id, artist_name, release_title, release_type, action, timestamp)
VALUES
(NEW.release_id, NEW.artist_id, NEW.artist_name, NEW.release_title, NEW.release_type, 'discovered', CURRENT_TIMESTAMP);
END
""")
# Trigger for release update download completion
db.session.execute("""
CREATE TRIGGER IF NOT EXISTS release_update_downloaded
AFTER UPDATE ON release_updates
WHEN OLD.download_status != 'completed' AND NEW.download_status = 'completed'
BEGIN
INSERT INTO release_update_history
(release_id, artist_id, artist_name, release_title, release_type, action, timestamp, metadata)
VALUES
(NEW.release_id, NEW.artist_id, NEW.artist_name, NEW.release_title, NEW.release_type, 'downloaded', CURRENT_TIMESTAMP,
json_object('auto_downloaded', NEW.auto_downloaded));
END
""")
db.session.commit()
logger.info("Update tracking triggers created successfully")
except Exception as e:
logger.error(f"Error creating update tracking triggers: {e}")
db.session.rollback()
raise