From 2b38fda25a10bb3d49c76f48ec1d4fd5738c1ea1 Mon Sep 17 00:00:00 2001 From: cottongin Date: Thu, 12 Mar 2026 01:21:33 -0400 Subject: [PATCH] feat: add database module with schema initialization Made-with: Cursor --- src/ntr_fetcher/db.py | 52 +++++++++++++++++++++++++++++++++++++++++++ tests/test_db.py | 30 +++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 src/ntr_fetcher/db.py create mode 100644 tests/test_db.py diff --git a/src/ntr_fetcher/db.py b/src/ntr_fetcher/db.py new file mode 100644 index 0000000..0a49a7b --- /dev/null +++ b/src/ntr_fetcher/db.py @@ -0,0 +1,52 @@ +import sqlite3 +from datetime import datetime, timezone + +from ntr_fetcher.models import Track, Show + +SCHEMA = """ +CREATE TABLE IF NOT EXISTS tracks ( + id INTEGER PRIMARY KEY, + title TEXT NOT NULL, + artist TEXT NOT NULL, + permalink_url TEXT NOT NULL, + artwork_url TEXT, + duration_ms INTEGER NOT NULL, + license TEXT NOT NULL DEFAULT '', + liked_at TEXT NOT NULL, + raw_json TEXT NOT NULL DEFAULT '{}' +); + +CREATE TABLE IF NOT EXISTS shows ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + week_start TEXT NOT NULL, + week_end TEXT NOT NULL, + created_at TEXT NOT NULL +); + +CREATE TABLE IF NOT EXISTS show_tracks ( + show_id INTEGER NOT NULL REFERENCES shows(id), + track_id INTEGER NOT NULL REFERENCES tracks(id), + position INTEGER NOT NULL, + UNIQUE(show_id, track_id) +); + +CREATE INDEX IF NOT EXISTS idx_show_tracks_show_id ON show_tracks(show_id); +CREATE INDEX IF NOT EXISTS idx_tracks_liked_at ON tracks(liked_at); +""" + + +class Database: + def __init__(self, path: str): + self.path = path + + def _connect(self) -> sqlite3.Connection: + conn = sqlite3.connect(self.path) + conn.execute("PRAGMA journal_mode=WAL") + conn.execute("PRAGMA foreign_keys=ON") + conn.row_factory = sqlite3.Row + return conn + + def initialize(self) -> None: + conn = self._connect() + conn.executescript(SCHEMA) + conn.close() diff --git a/tests/test_db.py b/tests/test_db.py new file mode 100644 index 0000000..b942940 --- /dev/null +++ b/tests/test_db.py @@ -0,0 +1,30 @@ +import sqlite3 + +import pytest + +from ntr_fetcher.db import Database + + +@pytest.fixture +def db(tmp_path): + db_path = str(tmp_path / "test.db") + database = Database(db_path) + database.initialize() + return database + + +def test_tables_created(db): + conn = sqlite3.connect(db.path) + cursor = conn.execute( + "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name" + ) + tables = [row[0] for row in cursor.fetchall()] + conn.close() + assert "tracks" in tables + assert "shows" in tables + assert "show_tracks" in tables + + +def test_initialize_idempotent(db): + """Calling initialize twice doesn't raise.""" + db.initialize()