| """SQLite database module for music memories app.""" |
|
|
| import sqlite3 |
| from contextlib import contextmanager |
| from datetime import datetime |
| from typing import Optional |
|
|
| DATABASE_PATH = "./music_memories.db" |
|
|
|
|
| @contextmanager |
| def get_db_connection(): |
| """Get a database connection with row factory.""" |
| conn = sqlite3.connect(DATABASE_PATH) |
| conn.row_factory = sqlite3.Row |
| try: |
| yield conn |
| finally: |
| conn.close() |
|
|
|
|
| def init_database() -> None: |
| """Initialize the SQLite database with all tables.""" |
| with get_db_connection() as conn: |
| |
| conn.execute(""" |
| CREATE TABLE IF NOT EXISTS songs ( |
| id INTEGER PRIMARY KEY AUTOINCREMENT, |
| title TEXT NOT NULL, |
| artist TEXT NOT NULL, |
| album TEXT, |
| duration INTEGER, |
| bpm INTEGER, |
| energy_level INTEGER CHECK(energy_level BETWEEN 1 AND 10), |
| audio_file_path TEXT, |
| file_size INTEGER |
| ) |
| """) |
| |
| |
| conn.execute(""" |
| CREATE TABLE IF NOT EXISTS users ( |
| id INTEGER PRIMARY KEY AUTOINCREMENT, |
| name TEXT NOT NULL, |
| created_at TEXT DEFAULT CURRENT_TIMESTAMP |
| ) |
| """) |
| |
| |
| conn.execute(""" |
| CREATE TABLE IF NOT EXISTS playlists ( |
| id INTEGER PRIMARY KEY AUTOINCREMENT, |
| name TEXT NOT NULL, |
| vibe_code TEXT, |
| created_at TEXT DEFAULT CURRENT_TIMESTAMP |
| ) |
| """) |
| |
| |
| conn.execute(""" |
| CREATE TABLE IF NOT EXISTS contexts ( |
| id INTEGER PRIMARY KEY AUTOINCREMENT, |
| weather TEXT, |
| time_of_day TEXT, |
| location_type TEXT |
| ) |
| """) |
| |
| |
| conn.execute(""" |
| CREATE TABLE IF NOT EXISTS memories ( |
| id INTEGER PRIMARY KEY AUTOINCREMENT, |
| user_id INTEGER NOT NULL, |
| description TEXT NOT NULL, |
| date TEXT, |
| song_id INTEGER, |
| FOREIGN KEY (user_id) REFERENCES users(id), |
| FOREIGN KEY (song_id) REFERENCES songs(id) |
| ) |
| """) |
| |
| |
| conn.execute(""" |
| CREATE TABLE IF NOT EXISTS play_history ( |
| user_id INTEGER NOT NULL, |
| song_id INTEGER NOT NULL, |
| played_at TEXT DEFAULT CURRENT_TIMESTAMP, |
| context_id INTEGER, |
| PRIMARY KEY (user_id, song_id, played_at), |
| FOREIGN KEY (user_id) REFERENCES users(id), |
| FOREIGN KEY (song_id) REFERENCES songs(id), |
| FOREIGN KEY (context_id) REFERENCES contexts(id) |
| ) |
| """) |
| |
| conn.commit() |
|
|
|
|
| |
|
|
| def add_song(title: str, artist: str, album: str = None, duration: int = None, |
| bpm: int = None, energy_level: int = None) -> dict: |
| """Add a new song.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute( |
| """INSERT INTO songs (title, artist, album, duration, bpm, energy_level) |
| VALUES (?, ?, ?, ?, ?, ?)""", |
| (title, artist, album, duration, bpm, energy_level) |
| ) |
| conn.commit() |
| song_id = cursor.lastrowid |
| return {"id": song_id, "title": title, "artist": artist, "album": album, |
| "duration": duration, "bpm": bpm, "energy_level": energy_level} |
|
|
|
|
| def get_all_songs() -> list[dict]: |
| """Get all songs.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute("SELECT * FROM songs") |
| return [dict(row) for row in cursor.fetchall()] |
|
|
|
|
| def get_song_by_id(song_id: int) -> Optional[dict]: |
| """Get a song by ID.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute("SELECT * FROM songs WHERE id = ?", (song_id,)) |
| row = cursor.fetchone() |
| return dict(row) if row else None |
|
|
|
|
| def delete_song(song_id: int) -> bool: |
| """Delete a song.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute("DELETE FROM songs WHERE id = ?", (song_id,)) |
| conn.commit() |
| return cursor.rowcount > 0 |
|
|
|
|
| def update_song_file(song_id: int, audio_file_path: str, file_size: int) -> bool: |
| """Update song with MinIO file path and size.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute( |
| """UPDATE songs SET audio_file_path = ?, file_size = ? WHERE id = ?""", |
| (audio_file_path, file_size, song_id) |
| ) |
| conn.commit() |
| return cursor.rowcount > 0 |
|
|
|
|
| |
|
|
| def add_user(name: str) -> dict: |
| """Add a new user.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute("INSERT INTO users (name) VALUES (?)", (name,)) |
| conn.commit() |
| user_id = cursor.lastrowid |
| cursor = conn.execute("SELECT * FROM users WHERE id = ?", (user_id,)) |
| return dict(cursor.fetchone()) |
|
|
|
|
| def get_all_users() -> list[dict]: |
| """Get all users.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute("SELECT * FROM users") |
| return [dict(row) for row in cursor.fetchall()] |
|
|
|
|
| def get_user_by_id(user_id: int) -> Optional[dict]: |
| """Get a user by ID.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute("SELECT * FROM users WHERE id = ?", (user_id,)) |
| row = cursor.fetchone() |
| return dict(row) if row else None |
|
|
|
|
| |
|
|
| def add_playlist(name: str, vibe_code: str = None) -> dict: |
| """Add a new playlist.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute( |
| "INSERT INTO playlists (name, vibe_code) VALUES (?, ?)", |
| (name, vibe_code) |
| ) |
| conn.commit() |
| playlist_id = cursor.lastrowid |
| cursor = conn.execute("SELECT * FROM playlists WHERE id = ?", (playlist_id,)) |
| return dict(cursor.fetchone()) |
|
|
|
|
| def get_all_playlists() -> list[dict]: |
| """Get all playlists.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute("SELECT * FROM playlists") |
| return [dict(row) for row in cursor.fetchall()] |
|
|
|
|
| def delete_playlist(playlist_id: int) -> bool: |
| """Delete a playlist.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute("DELETE FROM playlists WHERE id = ?", (playlist_id,)) |
| conn.commit() |
| return cursor.rowcount > 0 |
|
|
|
|
| |
|
|
| def add_memory(user_id: int, description: str, date: str = None, song_id: int = None) -> dict: |
| """Add a new memory.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute( |
| "INSERT INTO memories (user_id, description, date, song_id) VALUES (?, ?, ?, ?)", |
| (user_id, description, date, song_id) |
| ) |
| conn.commit() |
| memory_id = cursor.lastrowid |
| cursor = conn.execute("SELECT * FROM memories WHERE id = ?", (memory_id,)) |
| return dict(cursor.fetchone()) |
|
|
|
|
| def get_all_memories() -> list[dict]: |
| """Get all memories.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute("SELECT * FROM memories") |
| return [dict(row) for row in cursor.fetchall()] |
|
|
|
|
| def get_memories_by_user(user_id: int) -> list[dict]: |
| """Get memories for a specific user.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute("SELECT * FROM memories WHERE user_id = ?", (user_id,)) |
| return [dict(row) for row in cursor.fetchall()] |
|
|
|
|
| def delete_memory(memory_id: int) -> bool: |
| """Delete a memory.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute("DELETE FROM memories WHERE id = ?", (memory_id,)) |
| conn.commit() |
| return cursor.rowcount > 0 |
|
|
|
|
| |
|
|
| def add_context(weather: str = None, time_of_day: str = None, location_type: str = None) -> dict: |
| """Add a new context.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute( |
| "INSERT INTO contexts (weather, time_of_day, location_type) VALUES (?, ?, ?)", |
| (weather, time_of_day, location_type) |
| ) |
| conn.commit() |
| context_id = cursor.lastrowid |
| cursor = conn.execute("SELECT * FROM contexts WHERE id = ?", (context_id,)) |
| return dict(cursor.fetchone()) |
|
|
|
|
| def get_all_contexts() -> list[dict]: |
| """Get all contexts.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute("SELECT * FROM contexts") |
| return [dict(row) for row in cursor.fetchall()] |
|
|
|
|
| def delete_context(context_id: int) -> bool: |
| """Delete a context.""" |
| with get_db_connection() as conn: |
| cursor = conn.execute("DELETE FROM contexts WHERE id = ?", (context_id,)) |
| conn.commit() |
| return cursor.rowcount > 0 |
|
|
|
|
| |
|
|
| def add_play_history(user_id: int, song_id: int, context_id: int = None) -> dict: |
| """Add a play history entry.""" |
| played_at = datetime.utcnow().isoformat() |
| with get_db_connection() as conn: |
| conn.execute( |
| """INSERT INTO play_history (user_id, song_id, played_at, context_id) |
| VALUES (?, ?, ?, ?)""", |
| (user_id, song_id, played_at, context_id) |
| ) |
| conn.commit() |
| return {"user_id": user_id, "song_id": song_id, "played_at": played_at, "context_id": context_id} |
|
|
|
|
| def get_play_history(user_id: int = None, limit: int = 50) -> list[dict]: |
| """Get play history, optionally filtered by user.""" |
| with get_db_connection() as conn: |
| if user_id: |
| cursor = conn.execute( |
| """SELECT ph.*, s.title, s.artist |
| FROM play_history ph |
| JOIN songs s ON ph.song_id = s.id |
| WHERE ph.user_id = ? |
| ORDER BY ph.played_at DESC LIMIT ?""", |
| (user_id, limit) |
| ) |
| else: |
| cursor = conn.execute( |
| """SELECT ph.*, s.title, s.artist |
| FROM play_history ph |
| JOIN songs s ON ph.song_id = s.id |
| ORDER BY ph.played_at DESC LIMIT ?""", |
| (limit,) |
| ) |
| return [dict(row) for row in cursor.fetchall()] |
|
|