update scripts to use Postgres

This commit is contained in:
Thomas Peetz
2025-05-01 01:13:49 +02:00
parent 72c1a7d265
commit 7ff2bf912d
12 changed files with 110 additions and 45 deletions
+3
View File
@@ -15,3 +15,6 @@ kontor-gui/dist
fastapi/.coverage fastapi/.coverage
kontor-api/.coverage kontor-api/.coverage
db-password.txt db-password.txt
kontor-api/tests/test_main.py
kontor-api/tests/test_db.db
kontor-api/test_db.db
+2 -2
View File
@@ -43,7 +43,7 @@ services:
volumes: volumes:
- mariadb-storage:/var/lib/mysql:rw - mariadb-storage:/var/lib/mysql:rw
kontor: kontor:
image: kontor:0.1.0 image: kontor:0.2.0-SNAPSHOT
restart: unless-stopped restart: unless-stopped
networks: networks:
- database - database
@@ -54,7 +54,7 @@ services:
postgres: postgres:
condition: service_healthy condition: service_healthy
kontor-api: kontor-api:
image: kontor-api:0.1.0 image: kontor-api:0.2.0-SNAPSHOT
restart: unless-stopped restart: unless-stopped
networks: networks:
- database - database
+75
View File
@@ -0,0 +1,75 @@
import os
import sys
from typing import Any
from typing import Generator
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from src.apis.base import api_router
from src.db.models.base import Base
from src.db.session import get_db
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# this is to include backend dir in sys.path so that we can import from db,main.py
def start_application():
app = FastAPI()
app.include_router(api_router)
return app
SQLALCHEMY_DATABASE_URL = "sqlite:///./test_db.db"
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
# Use connect_args parameter only with sqlite
SessionTesting = sessionmaker(autocommit=False, autoflush=False, bind=engine)
@pytest.fixture(scope="module")
def app() -> Generator[FastAPI, Any, None]:
"""
Create a fresh database on each test case.
"""
Base.metadata.create_all(engine) # Create the tables.
_app = start_application()
yield _app
Base.metadata.drop_all(engine)
@pytest.fixture(scope="module")
def db_session(app: FastAPI) -> Generator[SessionTesting, Any, None]:
connection = engine.connect()
transaction = connection.begin()
session = SessionTesting(bind=connection)
yield session # use the session in tests.
session.close()
transaction.rollback()
connection.close()
@pytest.fixture(scope="module")
def client(
app: FastAPI, db_session: SessionTesting
) -> Generator[TestClient, Any, None]:
"""
Create a new FastAPI TestClient that uses the `db_session` fixture to override
the `get_db` dependency that is injected into routes.
"""
def _get_test_db():
try:
yield db_session
finally:
pass
app.dependency_overrides[get_db] = _get_test_db
with TestClient(app) as client:
yield client
+1 -9
View File
@@ -1,15 +1,7 @@
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
import pytest
from src.main import kontor
@pytest.fixture(name="client")
def client_fixture():
client = TestClient(kontor)
yield client
def test_get_artists(client: TestClient): def test_get_artists(client: TestClient):
response = client.get("/api/comic/artists") response = client.get("/api/comic/artists")
assert response.status_code == 200 assert response.status_code == 200
assert len(response.json()) == 5 assert len(response.json()) == 0
+1 -1
View File
@@ -25,7 +25,7 @@ def get_database_cursors(log, config: str):
password=db_config['mariadb']['password'], password=db_config['mariadb']['password'],
database=db_config['mariadb']['database'] database=db_config['mariadb']['database']
) )
postgres_conn = psycopg2.connect(f"host={db_config['postgres']['host']} port={db_config['postgres']['port']} user={db_config['postgres']['user']} password={db_config['postgres']['password']} dbname={db_config['postgres']['']}") postgres_conn = psycopg2.connect(f"host={db_config['postgres']['host']} port={db_config['postgres']['port']} user={db_config['postgres']['user']} password={db_config['postgres']['password']} dbname={db_config['postgres']['database']}")
return sqlite_conn, mariadb_conn, postgres_conn return sqlite_conn, mariadb_conn, postgres_conn
+6 -6
View File
@@ -28,12 +28,12 @@ if __name__ == '__main__':
database_config = Path(dirs.user_config_dir, 'database-config.yaml') database_config = Path(dirs.user_config_dir, 'database-config.yaml')
with open(database_config, 'rt') as f: with open(database_config, 'rt') as f:
db_config = yaml.safe_load(f.read()) db_config = yaml.safe_load(f.read())
connect_string = ('mariadb+mariadbconnector://{}:{}@{}:{}/{}'.format( connect_string = ('postgresql://{}:{}@{}:{}/{}'.format(
db_config['mariadb']['user'], db_config['postgres']['user'],
db_config['mariadb']['password'], db_config['postgres']['password'],
db_config['mariadb']['host'], db_config['postgres']['host'],
db_config['mariadb']['port'], db_config['postgres']['port'],
db_config['mariadb']['database'] db_config['postgres']['database']
)) ))
engine = create_engine(connect_string) engine = create_engine(connect_string)
Base.metadata.create_all(bind=engine, checkfirst=True) Base.metadata.create_all(bind=engine, checkfirst=True)
+3 -3
View File
@@ -23,12 +23,12 @@ def copy_data(postgres_conn, data_file: Path, log):
log.info("read json file") log.info("read json file")
with open(data_file, 'r') as json_file: with open(data_file, 'r') as json_file:
json_load = json.load(json_file) json_load = json.load(json_file)
postgres_cursor.execute("SET session_replication_role='replica'")
for table in json_load: for table in json_load:
log.info(f"{table}: {len(json_load[table])}") log.info(f"{table}: {len(json_load[table])}")
# result[table] = import_table(table, json_load[table]) # result[table] = import_table(table, json_load[table])
truncate_statement = 'TRUNCATE {}'.format(table) truncate_statement = 'TRUNCATE {} CASCADE'.format(table)
#log.info(f"truncate: {truncate_statement}") #log.info(f"truncate: {truncate_statement}")
postgres_cursor.execute("SET FOREIGN_KEY_CHECKS = 0")
postgres_cursor.execute(truncate_statement) postgres_cursor.execute(truncate_statement)
items = json_load[table] items = json_load[table]
for item in items: for item in items:
@@ -47,7 +47,7 @@ def copy_data(postgres_conn, data_file: Path, log):
postgres_conn.commit() postgres_conn.commit()
except psycopg2.Error as error: except psycopg2.Error as error:
log.info('insert failed with %s', error) log.info('insert failed with %s', error)
postgres_cursor.execute("SET session_replication_role='origin'")
if __name__ == '__main__': if __name__ == '__main__':
logger = get_logger(args.verbose, args.config) logger = get_logger(args.verbose, args.config)
+5 -6
View File
@@ -1,7 +1,6 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import Column, ForeignKey, Integer, String from sqlalchemy import Column, ForeignKey, Integer, String, Boolean
from sqlalchemy.dialects.mysql import BIT
from sqlalchemy.orm import relationship, mapped_column, Mapped from sqlalchemy.orm import relationship, mapped_column, Mapped
from .base import Base, BaseMixin from .base import Base, BaseMixin
@@ -14,7 +13,7 @@ class Profile(Base, BaseMixin):
user_name = Column(String(255), nullable=False) user_name = Column(String(255), nullable=False)
email = Column(String(255)) email = Column(String(255))
password = Column(String(255)) password = Column(String(255))
enabled = Column(BIT(1)) enabled = Column(Boolean)
assignments = relationship("Assignment") assignments = relationship("Assignment")
tokens = relationship("Token") tokens = relationship("Token")
@@ -34,7 +33,7 @@ class Token(Base, BaseMixin):
token = Column(String(255), nullable=False, unique=True) token = Column(String(255), nullable=False, unique=True)
name = Column(String(255)) name = Column(String(255))
last_used_date: Mapped[datetime] = mapped_column() last_used_date: Mapped[datetime] = mapped_column()
enabled = Column(BIT(1)) enabled = Column(Boolean)
profile_id = Column(String(255), ForeignKey("profile.id"), nullable=False) profile_id = Column(String(255), ForeignKey("profile.id"), nullable=False)
profile = relationship("Profile", back_populates="tokens") profile = relationship("Profile", back_populates="tokens")
@@ -56,7 +55,7 @@ class Assignment(Base, BaseMixin):
class ModuleData(Base, BaseMixin): class ModuleData(Base, BaseMixin):
__tablename__ = "module_data" __tablename__ = "module_data"
module_name = Column(String(255), nullable=False) module_name = Column(String(255), nullable=False)
import_data = Column(BIT(1)) import_data = Column(Boolean)
class MailAccount(Base, BaseMixin): class MailAccount(Base, BaseMixin):
@@ -66,7 +65,7 @@ class MailAccount(Base, BaseMixin):
protocol = Column(String(255)) protocol = Column(String(255))
user_name = Column(String(255)) user_name = Column(String(255))
password = Column(String(255)) password = Column(String(255))
start_tls = Column(BIT(1)) start_tls = Column(Boolean)
class Mail(Base, BaseMixin): class Mail(Base, BaseMixin):
+3 -4
View File
@@ -1,8 +1,7 @@
import uuid import uuid
from datetime import datetime from datetime import datetime
from sqlalchemy import func, Column, String from sqlalchemy import func, Column, String, Boolean
from sqlalchemy.dialects.mysql import BIT
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
@@ -25,7 +24,7 @@ class BaseVideoMixin:
cloud_link = Column(String(255)) cloud_link = Column(String(255))
file_name = Column(String(255)) file_name = Column(String(255))
path = Column(String(255)) path = Column(String(255))
review = Column(BIT(1)) review = Column(Boolean)
title = Column(String(255)) title = Column(String(255))
url = Column(String(255), unique=True) url = Column(String(255), unique=True)
should_download = Column(BIT(1)) should_download = Column(Boolean)
+5 -6
View File
@@ -1,5 +1,4 @@
from sqlalchemy import Column, ForeignKey, Integer, String from sqlalchemy import Column, ForeignKey, Integer, String, Boolean
from sqlalchemy.dialects.mysql import BIT
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from .base import Base, BaseMixin from .base import Base, BaseMixin
@@ -22,8 +21,8 @@ class Comic(Base, BaseMixin):
title = Column(String(length=255), unique=True) title = Column(String(length=255), unique=True)
publisher_id = Column(String, ForeignKey('publisher.id'), nullable=False) publisher_id = Column(String, ForeignKey('publisher.id'), nullable=False)
publisher = relationship("Publisher", back_populates="comics") publisher = relationship("Publisher", back_populates="comics")
current_order = Column(BIT(1)) current_order = Column(Boolean)
completed = Column(BIT(1)) completed = Column(Boolean)
issues = relationship("Issue") issues = relationship("Issue")
story_arcs = relationship("StoryArc") story_arcs = relationship("StoryArc")
trade_paperbacks = relationship("TradePaperback") trade_paperbacks = relationship("TradePaperback")
@@ -64,8 +63,8 @@ class StoryArc(Base, BaseMixin):
class Issue(Base, BaseMixin): class Issue(Base, BaseMixin):
__tablename__ = "issue" __tablename__ = "issue"
issue_number = Column(String(255)) issue_number = Column(String(255))
in_stock = Column(BIT(1)) in_stock = Column(Boolean)
is_read = Column(BIT(1)) is_read = Column(Boolean)
comic_id = Column(String, ForeignKey("comic.id"), nullable=False) comic_id = Column(String, ForeignKey("comic.id"), nullable=False)
comic = relationship("Comic", back_populates="issues") comic = relationship("Comic", back_populates="issues")
volume_id = Column(String, ForeignKey("volume.id"), nullable=True) volume_id = Column(String, ForeignKey("volume.id"), nullable=True)
+3 -4
View File
@@ -1,5 +1,4 @@
from sqlalchemy import Column, String, ForeignKey, Integer from sqlalchemy import Column, String, ForeignKey, Integer, Boolean
from sqlalchemy.dialects.mysql import BIT
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from .base import Base, BaseMixin from .base import Base, BaseMixin
@@ -28,8 +27,8 @@ class MetaDataColumn(Base, BaseMixin):
table = relationship("MetaDataTable", back_populates="table_columns") table = relationship("MetaDataTable", back_populates="table_columns")
column_label = Column(String(255)) column_label = Column(String(255))
filter_label = Column(String(255)) filter_label = Column(String(255))
is_shown = Column(BIT(1)) is_shown = Column(Boolean)
show_filter = Column(BIT(1)) show_filter = Column(Boolean)
ref_column = Column(String, nullable=True) ref_column = Column(String, nullable=True)
def __repr__(self): def __repr__(self):
+3 -4
View File
@@ -1,5 +1,4 @@
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Boolean
from sqlalchemy.dialects.mysql import BIT
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from .base import Base, BaseMixin from .base import Base, BaseMixin
@@ -78,8 +77,8 @@ class CardSet(Base, BaseMixin):
UniqueConstraint("name", "vendor_id"), UniqueConstraint("name", "vendor_id"),
) )
name = Column(String(255), index=True) name = Column(String(255), index=True)
parallel_set = Column(BIT(1)) parallel_set = Column(Boolean)
insert_set = Column(BIT(1)) insert_set = Column(Boolean)
vendor_id = Column(String, ForeignKey("vendor.id"), nullable=False, index=True) vendor_id = Column(String, ForeignKey("vendor.id"), nullable=False, index=True)
vendor = relationship("Vendor", back_populates="card_sets") vendor = relationship("Vendor", back_populates="card_sets")
cards = relationship("Card") cards = relationship("Card")