Bump version to 0.2.0 and add alembic as a dependency.
The commit also includes changes to add Alembic configuration file, add migration scripts for initial migration, and modify the database class to include functions for running migrations and creating new migrations.
This commit is contained in:
parent
eb87e38507
commit
23407f031e
6 changed files with 183 additions and 3 deletions
|
@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "trackbert"
|
name = "trackbert"
|
||||||
version = "0.1.3"
|
version = "0.2.0"
|
||||||
authors = [
|
authors = [
|
||||||
{ name="Kumi Mitterer", email="trackbert@kumi.email" },
|
{ name="Kumi Mitterer", email="trackbert@kumi.email" },
|
||||||
]
|
]
|
||||||
|
@ -23,6 +23,7 @@ dependencies = [
|
||||||
"glsapi",
|
"glsapi",
|
||||||
"fedextrack",
|
"fedextrack",
|
||||||
"sqlalchemy",
|
"sqlalchemy",
|
||||||
|
"alembic",
|
||||||
"python-dateutil",
|
"python-dateutil",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
2
src/trackbert/alembic.ini
Normal file
2
src/trackbert/alembic.ini
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[alembic]
|
||||||
|
version_path_separator = os
|
|
@ -3,7 +3,13 @@ from sqlalchemy import create_engine, ForeignKey
|
||||||
from sqlalchemy.orm import sessionmaker, relationship
|
from sqlalchemy.orm import sessionmaker, relationship
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
|
||||||
|
from alembic.config import Config
|
||||||
|
from alembic import command
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
|
|
||||||
|
@ -35,6 +41,8 @@ class Database:
|
||||||
Session = sessionmaker(bind=self.engine)
|
Session = sessionmaker(bind=self.engine)
|
||||||
self.session = Session()
|
self.session = Session()
|
||||||
|
|
||||||
|
self.run_migrations()
|
||||||
|
|
||||||
def create_shipment(self, tracking_number, carrier, description=""):
|
def create_shipment(self, tracking_number, carrier, description=""):
|
||||||
new_shipment = Shipment(
|
new_shipment = Shipment(
|
||||||
tracking_number=tracking_number, carrier=carrier, description=description
|
tracking_number=tracking_number, carrier=carrier, description=description
|
||||||
|
@ -102,5 +110,16 @@ class Database:
|
||||||
)
|
)
|
||||||
return event
|
return event
|
||||||
|
|
||||||
def initialize_db(self):
|
def make_migration(self, message):
|
||||||
Base.metadata.create_all(self.engine)
|
alembic_cfg = Config(Path(__file__).parent.parent / "alembic.ini")
|
||||||
|
alembic_cfg.set_main_option("sqlalchemy.url", self.engine.url.__to_string__(hide_password=False))
|
||||||
|
migrations_dir = Path(__file__).parent.parent / 'migrations'
|
||||||
|
alembic_cfg.set_main_option("script_location", str(migrations_dir))
|
||||||
|
command.revision(alembic_cfg, message=message, autogenerate=True)
|
||||||
|
|
||||||
|
def run_migrations(self):
|
||||||
|
alembic_cfg = Config(Path(__file__).parent.parent / "alembic.ini")
|
||||||
|
alembic_cfg.set_main_option("sqlalchemy.url", self.engine.url.__to_string__(hide_password=False))
|
||||||
|
migrations_dir = Path(__file__).parent.parent / 'migrations'
|
||||||
|
alembic_cfg.set_main_option("script_location", str(migrations_dir))
|
||||||
|
command.upgrade(alembic_cfg, "head")
|
72
src/trackbert/migrations/env.py
Normal file
72
src/trackbert/migrations/env.py
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
from sqlalchemy import engine_from_config
|
||||||
|
from sqlalchemy import pool
|
||||||
|
from alembic import context
|
||||||
|
|
||||||
|
from trackbert.classes.database import Base
|
||||||
|
|
||||||
|
# this is the Alembic Config object, which provides
|
||||||
|
# access to the values within the .ini file in use.
|
||||||
|
config = context.config
|
||||||
|
|
||||||
|
# add your model's MetaData object here
|
||||||
|
# for 'autogenerate' support
|
||||||
|
# from myapp import mymodel
|
||||||
|
# target_metadata = mymodel.Base.metadata
|
||||||
|
target_metadata = Base.metadata
|
||||||
|
|
||||||
|
# other values from the config, defined by the needs of env.py,
|
||||||
|
# can be acquired:
|
||||||
|
# my_important_option = config.get_main_option("my_important_option")
|
||||||
|
# ... etc.
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_offline() -> None:
|
||||||
|
"""Run migrations in 'offline' mode.
|
||||||
|
|
||||||
|
This configures the context with just a URL
|
||||||
|
and not an Engine, though an Engine is acceptable
|
||||||
|
here as well. By skipping the Engine creation
|
||||||
|
we don't even need a DBAPI to be available.
|
||||||
|
|
||||||
|
Calls to context.execute() here emit the given string to the
|
||||||
|
script output.
|
||||||
|
|
||||||
|
"""
|
||||||
|
url = config.get_main_option("sqlalchemy.url")
|
||||||
|
context.configure(
|
||||||
|
url=url,
|
||||||
|
target_metadata=target_metadata,
|
||||||
|
literal_binds=True,
|
||||||
|
dialect_opts={"paramstyle": "named"},
|
||||||
|
)
|
||||||
|
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_online() -> None:
|
||||||
|
"""Run migrations in 'online' mode.
|
||||||
|
|
||||||
|
In this scenario we need to create an Engine
|
||||||
|
and associate a connection with the context.
|
||||||
|
|
||||||
|
"""
|
||||||
|
connectable = engine_from_config(
|
||||||
|
config.get_section(config.config_ini_section, {}),
|
||||||
|
prefix="sqlalchemy.",
|
||||||
|
poolclass=pool.NullPool,
|
||||||
|
)
|
||||||
|
|
||||||
|
with connectable.connect() as connection:
|
||||||
|
context.configure(
|
||||||
|
connection=connection, target_metadata=target_metadata
|
||||||
|
)
|
||||||
|
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
|
if context.is_offline_mode():
|
||||||
|
run_migrations_offline()
|
||||||
|
else:
|
||||||
|
run_migrations_online()
|
26
src/trackbert/migrations/script.py.mako
Normal file
26
src/trackbert/migrations/script.py.mako
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
"""${message}
|
||||||
|
|
||||||
|
Revision ID: ${up_revision}
|
||||||
|
Revises: ${down_revision | comma,n}
|
||||||
|
Create Date: ${create_date}
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
${imports if imports else ""}
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = ${repr(up_revision)}
|
||||||
|
down_revision: Union[str, None] = ${repr(down_revision)}
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
|
||||||
|
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
${upgrades if upgrades else "pass"}
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
${downgrades if downgrades else "pass"}
|
|
@ -0,0 +1,60 @@
|
||||||
|
"""Initial migration
|
||||||
|
|
||||||
|
Revision ID: 770fdbef1f4e
|
||||||
|
Revises:
|
||||||
|
Create Date: 2023-08-29 10:01:10.100731
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
from sqlalchemy.exc import ProgrammingError, OperationalError
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "770fdbef1f4e"
|
||||||
|
down_revision: Union[str, None] = None
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
if not table_exists("shipments"):
|
||||||
|
op.create_table(
|
||||||
|
"shipments",
|
||||||
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("tracking_number", sa.String(), nullable=True),
|
||||||
|
sa.Column("carrier", sa.String(), nullable=True),
|
||||||
|
sa.Column("description", sa.String(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
|
)
|
||||||
|
|
||||||
|
if not table_exists("events"):
|
||||||
|
op.create_table(
|
||||||
|
"events",
|
||||||
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("event_time", sa.String(), nullable=True),
|
||||||
|
sa.Column("event_description", sa.String(), nullable=True),
|
||||||
|
sa.Column("raw_event", sa.String(), nullable=True),
|
||||||
|
sa.Column("shipment_id", sa.Integer(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["shipment_id"],
|
||||||
|
["shipments.id"],
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
op.drop_table("events")
|
||||||
|
op.drop_table("shipments")
|
||||||
|
|
||||||
|
|
||||||
|
def table_exists(table_name):
|
||||||
|
try:
|
||||||
|
op.execute(f'SELECT 1 FROM "{table_name}" LIMIT 1;')
|
||||||
|
return True
|
||||||
|
except (OperationalError, ProgrammingError):
|
||||||
|
return False
|
Loading…
Reference in a new issue