Add database migration script

This commit is contained in:
Zoe Roux 2025-05-14 01:28:43 +02:00
parent 75bc5a7d70
commit 7a76faa637
No known key found for this signature in database
4 changed files with 59 additions and 11 deletions

View File

@ -5,4 +5,3 @@ type-case=1
no-space-function=1 no-space-function=1
keep-newline=1 keep-newline=1
nogrouping=1 nogrouping=1
placeholder=%(\(\w+\))?s

View File

@ -1,5 +1,3 @@
begin;
create type scanner.request_kind as enum( create type scanner.request_kind as enum(
'episode', 'episode',
'movie' 'movie'
@ -17,12 +15,9 @@ create table scanner.requests(
kind scanner.request_kind not null, kind scanner.request_kind not null,
title text not null, title text not null,
year integer, year integer,
external_id jsonb not null default '{}' ::jsonb, external_id jsonb not null default '{}'::jsonb,
status scanner.request_status not null, status scanner.request_status not null,
started_at created_at timestamptz, started_at timestamptz,
created_at created_at timestamptz not null default now() ::timestamptz, created_at timestamptz not null default now()::timestamptz,
constraint unique_kty(kind, title, year) constraint unique_kty unique(kind, title, year)
); );
commit;

View File

@ -10,7 +10,7 @@ from scanner.providers.composite import CompositeProvider
from scanner.providers.themoviedatabase import TheMovieDatabase from scanner.providers.themoviedatabase import TheMovieDatabase
from scanner.requests import RequestCreator, RequestProcessor from scanner.requests import RequestCreator, RequestProcessor
from .database import get_db, init_pool from .database import get_db, init_pool, migrate
from .routers.routes import router from .routers.routes import router
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
@ -26,6 +26,7 @@ async def lifespan(_):
KyooClient() as client, KyooClient() as client,
TheMovieDatabase() as tmdb, TheMovieDatabase() as tmdb,
): ):
await migrate();
# creating the processor makes it listen to requests event in pg # creating the processor makes it listen to requests event in pg
async with ( async with (
RequestProcessor(db, client, CompositeProvider(tmdb)) as processor, RequestProcessor(db, client, CompositeProvider(tmdb)) as processor,

View File

@ -1,9 +1,12 @@
import os import os
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from logging import getLogger
from typing import Any, cast from typing import Any, cast
from asyncpg import Connection, Pool, create_pool from asyncpg import Connection, Pool, create_pool
logger = getLogger(__name__)
pool: Pool pool: Pool
@ -30,3 +33,53 @@ async def init_pool():
async def get_db(): async def get_db():
async with pool.acquire() as db: async with pool.acquire() as db:
yield cast(Connection, db) yield cast(Connection, db)
async def migrate(migrations_dir="./migrations"):
async with get_db() as db:
_ = await db.execute(
"""
create schema if not exists scanner;
create table if not exists scanner._migrations(
pk serial primary key,
name text not null,
applied_at timestamptz not null default now() ::timestamptz
);
""",
)
applied = await db.fetchval(
"""
select
count(*)
from
scanner._migrations
"""
)
if not os.path.exists(migrations_dir):
logger.warning(f"Migrations directory '{migrations_dir}' not found")
return
migrations = sorted(
f for f in os.listdir(migrations_dir) if f.endswith("up.sql")
)
for migration in migrations[applied:]:
file_path = os.path.join(migrations_dir, migration)
logger.info(f"Applying migration: {migration}")
try:
with open(file_path, "r") as f:
sql = f.read()
async with db.transaction():
_ = await db.execute(sql)
_ = await db.execute(
"""
insert into scanner._migrations(name)
values ($1)
""",
migration,
)
except Exception as e:
logger.error(f"Failed to apply migration {migration}", exc_info=e)
raise