feat: monitoring data model (WatchedArtist, MonitoredRelease, Request link)
This commit is contained in:
@@ -0,0 +1,51 @@
|
|||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "MonitoredReleaseState" AS ENUM ('wanted', 'grabbed', 'fulfilled');
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Request" ADD COLUMN "monitoredReleaseId" TEXT;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "WatchedArtist" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"mbid" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"autoMonitorFuture" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"monitorFrom" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"lastPolledAt" TIMESTAMP(3),
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "WatchedArtist_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "MonitoredRelease" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"watchedArtistId" TEXT,
|
||||||
|
"artistMbid" TEXT NOT NULL,
|
||||||
|
"artistName" TEXT NOT NULL,
|
||||||
|
"rgMbid" TEXT NOT NULL,
|
||||||
|
"album" TEXT NOT NULL,
|
||||||
|
"primaryType" TEXT,
|
||||||
|
"secondaryTypes" TEXT[],
|
||||||
|
"firstReleaseDate" TEXT,
|
||||||
|
"monitored" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"state" "MonitoredReleaseState" NOT NULL DEFAULT 'wanted',
|
||||||
|
"currentQualityClass" INTEGER,
|
||||||
|
"firstGrabbedAt" TIMESTAMP(3),
|
||||||
|
"lastSearchedAt" TIMESTAMP(3),
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "MonitoredRelease_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "WatchedArtist_mbid_key" ON "WatchedArtist"("mbid");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "MonitoredRelease_rgMbid_key" ON "MonitoredRelease"("rgMbid");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Request" ADD CONSTRAINT "Request_monitoredReleaseId_fkey" FOREIGN KEY ("monitoredReleaseId") REFERENCES "MonitoredRelease"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "MonitoredRelease" ADD CONSTRAINT "MonitoredRelease_watchedArtistId_fkey" FOREIGN KEY ("watchedArtistId") REFERENCES "WatchedArtist"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
@@ -23,6 +23,12 @@ enum JobState {
|
|||||||
needs_attention
|
needs_attention
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum MonitoredReleaseState {
|
||||||
|
wanted
|
||||||
|
grabbed
|
||||||
|
fulfilled
|
||||||
|
}
|
||||||
|
|
||||||
model Request {
|
model Request {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
artist String
|
artist String
|
||||||
@@ -31,6 +37,9 @@ model Request {
|
|||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
job Job?
|
job Job?
|
||||||
libraryItem LibraryItem?
|
libraryItem LibraryItem?
|
||||||
|
|
||||||
|
monitoredReleaseId String?
|
||||||
|
monitoredRelease MonitoredRelease? @relation(fields: [monitoredReleaseId], references: [id], onDelete: SetNull)
|
||||||
}
|
}
|
||||||
|
|
||||||
model Job {
|
model Job {
|
||||||
@@ -82,3 +91,34 @@ model Config {
|
|||||||
secret Boolean @default(false)
|
secret Boolean @default(false)
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model WatchedArtist {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
mbid String @unique
|
||||||
|
name String
|
||||||
|
autoMonitorFuture Boolean @default(false)
|
||||||
|
monitorFrom DateTime @default(now())
|
||||||
|
lastPolledAt DateTime?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
releases MonitoredRelease[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model MonitoredRelease {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
watchedArtist WatchedArtist? @relation(fields: [watchedArtistId], references: [id], onDelete: Cascade)
|
||||||
|
watchedArtistId String?
|
||||||
|
artistMbid String
|
||||||
|
artistName String
|
||||||
|
rgMbid String @unique
|
||||||
|
album String
|
||||||
|
primaryType String?
|
||||||
|
secondaryTypes String[]
|
||||||
|
firstReleaseDate String?
|
||||||
|
monitored Boolean @default(false)
|
||||||
|
state MonitoredReleaseState @default(wanted)
|
||||||
|
currentQualityClass Int?
|
||||||
|
firstGrabbedAt DateTime?
|
||||||
|
lastSearchedAt DateTime?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
requests Request[]
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ def conn():
|
|||||||
with connection.cursor() as cur:
|
with connection.cursor() as cur:
|
||||||
cur.execute('DELETE FROM "Job"')
|
cur.execute('DELETE FROM "Job"')
|
||||||
cur.execute('DELETE FROM "Request"')
|
cur.execute('DELETE FROM "Request"')
|
||||||
|
cur.execute('DELETE FROM "MonitoredRelease"')
|
||||||
|
cur.execute('DELETE FROM "WatchedArtist"')
|
||||||
cur.execute('DELETE FROM "Config"')
|
cur.execute('DELETE FROM "Config"')
|
||||||
connection.commit()
|
connection.commit()
|
||||||
yield connection
|
yield connection
|
||||||
@@ -19,6 +21,8 @@ def conn():
|
|||||||
with connection.cursor() as cur:
|
with connection.cursor() as cur:
|
||||||
cur.execute('DELETE FROM "Job"')
|
cur.execute('DELETE FROM "Job"')
|
||||||
cur.execute('DELETE FROM "Request"')
|
cur.execute('DELETE FROM "Request"')
|
||||||
|
cur.execute('DELETE FROM "MonitoredRelease"')
|
||||||
|
cur.execute('DELETE FROM "WatchedArtist"')
|
||||||
cur.execute('DELETE FROM "Config"')
|
cur.execute('DELETE FROM "Config"')
|
||||||
connection.commit()
|
connection.commit()
|
||||||
connection.close()
|
connection.close()
|
||||||
@@ -41,3 +45,41 @@ def insert_request(conn, artist="Artist", album="Album"):
|
|||||||
job_id = cur.fetchone()[0]
|
job_id = cur.fetchone()[0]
|
||||||
conn.commit()
|
conn.commit()
|
||||||
return job_id
|
return job_id
|
||||||
|
|
||||||
|
|
||||||
|
def insert_watched_artist(conn, mbid="mbid", name="Artist", auto_monitor_future=False,
|
||||||
|
monitor_from_sql="now()", last_polled_sql="NULL"):
|
||||||
|
"""Insert a WatchedArtist; monitor_from_sql / last_polled_sql are raw SQL time expressions."""
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(
|
||||||
|
'INSERT INTO "WatchedArtist" (id, mbid, name, "autoMonitorFuture", '
|
||||||
|
f'"monitorFrom", "lastPolledAt", "createdAt") '
|
||||||
|
f"VALUES (gen_random_uuid()::text, %s, %s, %s, {monitor_from_sql}, {last_polled_sql}, now()) "
|
||||||
|
"RETURNING id",
|
||||||
|
(mbid, name, auto_monitor_future),
|
||||||
|
)
|
||||||
|
artist_id = cur.fetchone()[0]
|
||||||
|
conn.commit()
|
||||||
|
return artist_id
|
||||||
|
|
||||||
|
|
||||||
|
def insert_monitored_release(conn, watched_artist_id=None, artist_mbid="mbid", artist_name="Artist",
|
||||||
|
rg_mbid="rg", album="Album", primary_type=None, secondary_types=None,
|
||||||
|
first_release_date=None, monitored=False, state="wanted",
|
||||||
|
current_quality_class=None, first_grabbed_sql="NULL",
|
||||||
|
last_searched_sql="NULL"):
|
||||||
|
"""Insert a MonitoredRelease; *_sql args are raw SQL time expressions."""
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(
|
||||||
|
'INSERT INTO "MonitoredRelease" (id, "watchedArtistId", "artistMbid", "artistName", '
|
||||||
|
'"rgMbid", album, "primaryType", "secondaryTypes", "firstReleaseDate", monitored, '
|
||||||
|
f'state, "currentQualityClass", "firstGrabbedAt", "lastSearchedAt", "createdAt") '
|
||||||
|
f"VALUES (gen_random_uuid()::text, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, "
|
||||||
|
f"{first_grabbed_sql}, {last_searched_sql}, now()) RETURNING id",
|
||||||
|
(watched_artist_id, artist_mbid, artist_name, rg_mbid, album, primary_type,
|
||||||
|
secondary_types if secondary_types is not None else [], first_release_date,
|
||||||
|
monitored, state, current_quality_class),
|
||||||
|
)
|
||||||
|
rel_id = cur.fetchone()[0]
|
||||||
|
conn.commit()
|
||||||
|
return rel_id
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
from tests.conftest import insert_monitored_release, insert_watched_artist
|
||||||
|
|
||||||
|
|
||||||
|
def test_watched_artist_and_release_round_trip(conn):
|
||||||
|
artist_id = insert_watched_artist(conn, mbid="mbid-1", name="John Mayer", auto_monitor_future=True)
|
||||||
|
rel_id = insert_monitored_release(
|
||||||
|
conn, watched_artist_id=artist_id, artist_mbid="mbid-1", artist_name="John Mayer",
|
||||||
|
rg_mbid="rg-1", album="Continuum", primary_type="Album",
|
||||||
|
secondary_types=["Live"], first_release_date="2006-09-12", monitored=True,
|
||||||
|
)
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(
|
||||||
|
'SELECT "watchedArtistId", album, "primaryType", "secondaryTypes", '
|
||||||
|
'monitored, state FROM "MonitoredRelease" WHERE id = %s',
|
||||||
|
(rel_id,),
|
||||||
|
)
|
||||||
|
row = cur.fetchone()
|
||||||
|
assert row == (artist_id, "Continuum", "Album", ["Live"], True, "wanted")
|
||||||
|
|
||||||
|
|
||||||
|
def test_request_has_nullable_monitored_release_link(conn):
|
||||||
|
rel_id = insert_monitored_release(
|
||||||
|
conn, watched_artist_id=None, artist_mbid="mbid-2", artist_name="X",
|
||||||
|
rg_mbid="rg-2", album="Y", monitored=True,
|
||||||
|
)
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(
|
||||||
|
'INSERT INTO "Request" (id, artist, album, status, "monitoredReleaseId", "createdAt") '
|
||||||
|
"VALUES (gen_random_uuid()::text, 'X', 'Y', 'pending', %s, now()) RETURNING id",
|
||||||
|
(rel_id,),
|
||||||
|
)
|
||||||
|
req_id = cur.fetchone()[0]
|
||||||
|
conn.commit()
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute('SELECT "monitoredReleaseId" FROM "Request" WHERE id = %s', (req_id,))
|
||||||
|
assert cur.fetchone()[0] == rel_id
|
||||||
Reference in New Issue
Block a user