Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added development db for Sanctuary #78

Merged
merged 3 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ web:
@docker compose up -d web

mig:
@echo "Creating database and applying migrations..."
@echo "Creating database and applying migrations for medical..."
@docker compose up migs
@docker compose up migssanctuary

automig:
@echo "Autogenerating migration in docker context"
@docker compose up -d --no-recreate api
@docker compose exec api bash -c "cd /app/api && alembic revision --autogenerate -m 'CHANGEME'"
@docker compose exec api bash -c "cd /app/api && alembic --name sanctuary revision --autogenerate -m 'CHANGEME'"
@docker compose exec api bash -c "cd /app/api && alembic --name medical revision --autogenerate -m 'CHANGEME'"

prune:
@echo "Pruning docker artifacts..."
Expand Down
17 changes: 10 additions & 7 deletions app/api/alembic.ini
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
# A generic, single database configuration.

[alembic]
[sanctuary]
# path to migration scripts
script_location = alembic/
script_location = alembic/sanctuary

# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
file_template = %%(year)d-%%(month).2d-%%(day).2d_%%(rev)s_%%(slug)s

# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory.
prepend_sys_path = ..
prepend_sys_path = .., ..

# timezone to use when rendering the date
# within the migration file as well as the filename.
Expand All @@ -32,16 +30,21 @@ prepend_sys_path = ..
# sourceless = false

# version location specification; this defaults
# to alembic//versions. When using multiple version
# to sanctuary/versions. When using multiple version
# directories, initial revisions must be specified with --version-path
# version_locations = %(here)s/bar %(here)s/bat alembic//versions
# version_locations = %(here)s/bar %(here)s/bat sanctuary/versions

# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8

sqlalchemy.url = driver://user:pass@localhost/dbname

[medical]
script_location = alembic/medical
sqlalchemy.url = driver://user:pass@localhost/dbname
prepend_sys_path = .., ..
file_template = %%(year)d-%%(month).2d-%%(day).2d_%%(rev)s_%%(slug)s

[post_write_hooks]
# post_write_hooks defines scripts or Python functions that are run
Expand Down
10 changes: 2 additions & 8 deletions app/api/alembic/env.py → app/api/alembic/medical/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,13 @@

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.

# Environment based sqlalchemy url
user = os.environ.get("POSTGRES_USER")
password = os.environ.get("POSTGRES_PASSWORD")
hostname = os.environ.get("POSTGRES_HOST")
db = os.environ.get("POSTGRES_DB")
SQLALCHEMY_DATABASE_URL = f"postgresql+psycopg2://{user}:{password}@{hostname}/{db}"
config.set_main_option('sqlalchemy.url', SQLALCHEMY_DATABASE_URL)
config.set_main_option("sqlalchemy.url", SQLALCHEMY_DATABASE_URL)


def run_migrations_offline():
Expand Down Expand Up @@ -74,8 +69,7 @@ def run_migrations_online():
)

with connectable.connect() as connection:
context.configure(connection=connection,
target_metadata=target_metadata)
context.configure(connection=connection, target_metadata=target_metadata)

with context.begin_transaction():
context.run_migrations()
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""CHANGEME

Revision ID: f2fff363a197
Revises: a98d7be28c23
Create Date: 2024-01-30 00:30:26.398900

"""

# revision identifiers, used by Alembic.
revision = 'f2fff363a197'
down_revision = 'a98d7be28c23'

from alembic import op
import sqlalchemy as sa

from alembic import context


def upgrade():
schema_upgrades()
if context.get_x_argument(as_dictionary=True).get("data", None):
data_upgrades()


def downgrade():
if context.get_x_argument(as_dictionary=True).get("data", None):
data_downgrades()
schema_downgrades()


def schema_upgrades():
"""schema upgrade migrations go here."""
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###


def schema_downgrades():
"""schema downgrade migrations go here."""
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###


def data_upgrades():
"""Add any optional data upgrade migrations here!"""
pass


def data_downgrades():
"""Add any optional data downgrade migrations here!"""
pass
Empty file.
82 changes: 82 additions & 0 deletions app/api/alembic/sanctuary/env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import os
from logging.config import fileConfig

from sqlalchemy import engine_from_config
from sqlalchemy import pool

from alembic import context

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config

# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)

# add your model's MetaData object here
# for 'autogenerate' support
from api.main.database import BaseSanctuary

# Populates Base.metadata by importing all models
import api.models

target_metadata = BaseSanctuary.metadata

# Environment based sqlalchemy url
user = os.environ.get("POSTGRES_USER")
password = os.environ.get("POSTGRES_PASSWORD")
hostname = os.environ.get("POSTGRES_HOST")
db = "sanctuary"
SQLALCHEMY_DATABASE_URL = f"postgresql+psycopg2://{user}:{password}@{hostname}/{db}"
config.set_main_option("sqlalchemy.url", SQLALCHEMY_DATABASE_URL)


def run_migrations_offline():
"""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():
"""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()
24 changes: 24 additions & 0 deletions app/api/alembic/sanctuary/script.py.mako
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""${message}

Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}

"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}

# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}


def upgrade():
${upgrades if upgrades else "pass"}


def downgrade():
${downgrades if downgrades else "pass"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""CHANGEME

Revision ID: e7a96dd328c5
Revises:
Create Date: 2024-01-30 00:30:20.784035

"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = 'e7a96dd328c5'
down_revision = None
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('intakes',
sa.Column('created_timestamp', sa.DateTime(), nullable=True),
sa.Column('last_updated_timestamp', sa.DateTime(), nullable=True),
sa.Column('deleted', sa.Boolean(), nullable=True),
sa.Column('intake_uuid', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('user_uuid', postgresql.UUID(as_uuid=True), nullable=True),
sa.Column('guest_rfid', sa.String(), nullable=True),
sa.Column('arrival_date', sa.DateTime(), nullable=True),
sa.Column('arrival_time', sa.DateTime(), nullable=True),
sa.Column('arrival_method', sa.String(), nullable=True),
sa.Column('identified_gender', sa.String(), nullable=True),
sa.Column('first_visit', sa.Boolean(), nullable=True),
sa.Column('presenting_complaint', sa.String(), nullable=True),
sa.Column('guest_consciousness_level', sa.String(), nullable=True),
sa.Column('guest_emotional_state', sa.String(), nullable=True),
sa.Column('substance_categories', sa.String(), nullable=True),
sa.Column('time_since_last_dose', sa.Integer(), nullable=True),
sa.Column('discharge_date', sa.DateTime(), nullable=True),
sa.Column('discharge_time', sa.DateTime(), nullable=True),
sa.Column('discharge_method', sa.String(), nullable=True),
sa.PrimaryKeyConstraint('intake_uuid'),
sa.UniqueConstraint('intake_uuid')
)
op.create_table('users',
sa.Column('created_timestamp', sa.DateTime(), nullable=True),
sa.Column('last_updated_timestamp', sa.DateTime(), nullable=True),
sa.Column('deleted', sa.Boolean(), nullable=True),
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('user_uuid', postgresql.UUID(as_uuid=True), nullable=True),
sa.Column('email', sa.String(), nullable=True),
sa.Column('hashed_password', sa.String(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('user_uuid')
)
op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_users_id'), table_name='users')
op.drop_table('users')
op.drop_table('intakes')
# ### end Alembic commands ###
24 changes: 18 additions & 6 deletions app/api/main/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,21 @@
f"postgresql+psycopg2://{user}:{password}@{hostname}:{port}/{db}"
)

engine = create_engine(SQLALCHEMY_DATABASE_URL, pool_pre_ping=True)
if not database_exists(engine.url): # Check if the db exists
create_database(engine.url) # Create new DB
# TODO: rename "example" database to medical
# Engine for the 'medical' database
engine_example = create_engine(
f"postgresql+psycopg2://{user}:{password}@{hostname}:{port}/example"
)
if not database_exists(engine_example.url): # Check if the db exists
create_database(engine_example.url) # Create new DB

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# TODO: Rename sessionLocal to sessionLocalMedical
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine_example)

# TODO: rename this to MedicalBase
Base = declarative_base()
BaseSanctuary = declarative_base()


def get_db() -> Generator:
db = SessionLocal()
Expand All @@ -35,9 +43,13 @@ def get_db() -> Generator:
finally:
db.close()


# TODO: implement engine and session for sanctuary
def create_all_tables() -> None:
Base.metadata.create_all(engine, checkfirst=True)
Base.metadata.create_all(engine_example, checkfirst=True)
# BaseSanctuary.metadata.create_all(engine_example, checkfirst=True)


def drop_all_tables(check_first: bool = False) -> None:
Base.metadata.drop_all(engine, checkfirst=check_first)
Base.metadata.drop_all(engine_example, checkfirst=check_first)
# BaseSanctuary.metadata.drop_all(engine_example, checkfirst=check_first)
7 changes: 2 additions & 5 deletions app/api/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
from api.models.user import User
from api.models.user import User, UserSanctuary, UserMedical
from api.models.patient_encounter import PatientEncounter

# TODO: uncomment this for sanctuary dev db
# Ticket: https://mediform.atlassian.net/browse/MEDI-30
# from api.models.intake import Intake
from api.models.intake import Intake
4 changes: 2 additions & 2 deletions app/api/models/intake.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Session

from api.main.database import Base
from api.main.database import BaseSanctuary
from api.models.mixins import BasicMetrics

logger = logging.getLogger(__name__)

class Intake(Base, BasicMetrics):
class Intake(BaseSanctuary, BasicMetrics):
__tablename__ = "intakes"

intake_uuid = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True)
Expand Down
Loading