Add sqlite + 0002 migration

This commit is contained in:
t0xa 2025-10-12 16:27:47 +03:00
parent 4ccdc39529
commit 5c8a4566bd
8 changed files with 74 additions and 13 deletions

View file

@ -9,6 +9,8 @@ class Settings(BaseSettings):
version: str = "0.0.1" version: str = "0.0.1"
debug: bool = False debug: bool = False
# TODO: Add sqlite 3 settings
# Postgres database settings # Postgres database settings
POSTGRES_DATABASE_URL: Optional[str] = None POSTGRES_DATABASE_URL: Optional[str] = None
POSTGRES_DATABASE_HOST: str = "localhost" POSTGRES_DATABASE_HOST: str = "localhost"

View file

@ -10,6 +10,11 @@ from app.api.v1.web import web_router
from app.config import settings from app.config import settings
from migrations.runner import MigrationRunner from migrations.runner import MigrationRunner
# TODO: Replace level with settings value
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
@asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI): async def lifespan(app: FastAPI):
@ -24,7 +29,7 @@ app = FastAPI(
title="Fitness Parser API", title="Fitness Parser API",
description="API for parsing fitness training data from various sources", description="API for parsing fitness training data from various sources",
version="0.1.0", version="0.1.0",
lifespan=lifespan lifespan=lifespan,
) )
app.mount("/static", StaticFiles(directory="app/static"), name="static") app.mount("/static", StaticFiles(directory="app/static"), name="static")

View file

@ -13,7 +13,8 @@
<!-- Navigation --> <!-- Navigation -->
<nav class="navbar navbar-dark bg-primary"> <nav class="navbar navbar-dark bg-primary">
<div class="container"> <div class="container">
<a class="navbar-brand" href="/"> <!-- TODO: find solution to not harcode links in hrefs -->
<a class="navbar-brand" href="/app/obsidian/list">
<strong>{{ app_name }}</strong> <strong>{{ app_name }}</strong>
</a> </a>
</div> </div>

View file

@ -10,7 +10,9 @@ class MigrationRunner:
def __init__(self, database_url: str) -> None: def __init__(self, database_url: str) -> None:
self.engine = create_async_engine(database_url) self.engine = create_async_engine(database_url)
self.migrations_dir = Path("migrations/sql") self.migrations_dir = Path("migrations/sql")
logging.error(f"{database_url}") logging.info(
f"Migrator initializer: engine = {self.engine}, migrations_dir = {self.migrations_dir}"
)
async def get_applied_migrations(self) -> set: async def get_applied_migrations(self) -> set:
"""Get list of applied migrations""" """Get list of applied migrations"""
@ -29,7 +31,9 @@ class MigrationRunner:
result = await conn.execute( result = await conn.execute(
text(f"SELECT version FROM {self.MIGRATIONS_SCHEMA_NAME}") text(f"SELECT version FROM {self.MIGRATIONS_SCHEMA_NAME}")
) )
return {row[0] for row in result.fetchall()} result_data = {row[0] for row in result.fetchall()}
logging.info(f"Received migrator data: {result_data}")
return result_data
async def run_migrations(self): async def run_migrations(self):
"""Run all unapplied migrations""" """Run all unapplied migrations"""
@ -38,7 +42,9 @@ class MigrationRunner:
# Getting all sql files from migrations_dir # Getting all sql files from migrations_dir
# TODO: (#ToLearn) Read about Path.glob function # TODO: (#ToLearn) Read about Path.glob function
migration_files = sorted([file for file in self.migrations_dir.glob(".*sql")]) migration_files = self.migrations_dir.glob('.*sql')
migration_files = sorted([file for file in self.migrations_dir.glob("*.sql")])
logging.info(f"Migration files: {migration_files}")
for migration_file in migration_files: for migration_file in migration_files:
# TODO: (#ToLearn) Read about stem property # TODO: (#ToLearn) Read about stem property
@ -52,12 +58,12 @@ class MigrationRunner:
async def _apply_migration(self, migration_file: Path, migration_name: str): async def _apply_migration(self, migration_file: Path, migration_name: str):
"""Apply migrations from migrations/sql folder""" """Apply migrations from migrations/sql folder"""
with open(migration_file, 'r', encoding='utf-8') as f: with open(migration_file, "r", encoding="utf-8") as f:
sql_content = f.read() sql_content = f.read()
async with self.engine.begin() as conn: async with self.engine.begin() as conn:
try: try:
statements = [s.strip() for s in sql_content.split(';') if s.strip()] statements = [s.strip() for s in sql_content.split(";") if s.strip()]
for statement in statements: for statement in statements:
if statement: if statement:

View file

@ -0,0 +1,28 @@
-- Migration: 0002_trainings
-- Description: Basic migration to initialize and create basic tarinings table
-- Schemas for trainings
-- Trainings table
CREATE TABLE IF NOT EXISTS trainings (
id SERIAL PRIMARY KEY,
date DATE NOT NULL,
trainer VARCHAR(100)
);
-- Exercies table
CREATE TABLE IF NOT EXISTS exercises (
id SERIAL PRIMARY KEY,
training_id INTEGER REFERENCES trainings(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
splitted_weigh BOOLEAN DEFAULT FALSE
);
-- Approaches table
CREATE TABLE IF NOT EXISTS approaches (
id SERIAL PRIMARY KEY,
approach_id INTEGER REFERENCES approaches(id) ON DELETE CASCADE,
weight FLOAT NOT NULL,
reps INTEGER NOT NULL
);
INSERT INTO schema_migrations (version) VALUES ('0002_trainings');

View file

@ -15,6 +15,7 @@ dependencies = [
"python-multipart>=0.0.6", "python-multipart>=0.0.6",
"sqlalchemy>=2.0.43", "sqlalchemy>=2.0.43",
"asyncpg>=0.30.0", "asyncpg>=0.30.0",
"aiosqlite>=0.21.0",
] ]
[dependency-groups] [dependency-groups]
@ -23,3 +24,7 @@ dev = [
"types-grpcio>=1.0.0.20250703", "types-grpcio>=1.0.0.20250703",
"types-protobuf>=6.30.2.20250822", "types-protobuf>=6.30.2.20250822",
] ]
[tools.pyright]
venvPath = "."
venv = ".venv"

16
uv.lock
View file

@ -1,7 +1,19 @@
version = 1 version = 1
revision = 2 revision = 3
requires-python = ">=3.13" requires-python = ">=3.13"
[[package]]
name = "aiosqlite"
version = "0.21.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/13/7d/8bca2bf9a247c2c5dfeec1d7a5f40db6518f88d314b8bca9da29670d2671/aiosqlite-0.21.0.tar.gz", hash = "sha256:131bb8056daa3bc875608c631c678cda73922a2d4ba8aec373b19f18c17e7aa3", size = 13454, upload-time = "2025-02-03T07:30:16.235Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f5/10/6c25ed6de94c49f88a91fa5018cb4c0f3625f31d5be9f771ebe5cc7cd506/aiosqlite-0.21.0-py3-none-any.whl", hash = "sha256:2549cf4057f95f53dcba16f2b64e8e2791d7e1adedb13197dd8ed77bb226d7d0", size = 15792, upload-time = "2025-02-03T07:30:13.6Z" },
]
[[package]] [[package]]
name = "annotated-types" name = "annotated-types"
version = "0.7.0" version = "0.7.0"
@ -66,6 +78,7 @@ name = "f1tness-parser"
version = "0.1.0" version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "aiosqlite" },
{ name = "asyncpg" }, { name = "asyncpg" },
{ name = "fastapi" }, { name = "fastapi" },
{ name = "jinja2" }, { name = "jinja2" },
@ -87,6 +100,7 @@ dev = [
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "aiosqlite", specifier = ">=0.21.0" },
{ name = "asyncpg", specifier = ">=0.30.0" }, { name = "asyncpg", specifier = ">=0.30.0" },
{ name = "fastapi", specifier = ">=0.115.0" }, { name = "fastapi", specifier = ">=0.115.0" },
{ name = "jinja2", specifier = ">=3.1.0" }, { name = "jinja2", specifier = ">=3.1.0" },