diff --git a/.gitignore b/.gitignore index ca30bc9..5ad7fbf 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ .idea .venv alembic.ini -alembic/ db/ !src/db __pycache__ diff --git a/alembic/README b/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/alembic/env.py b/alembic/env.py new file mode 100644 index 0000000..6e4827b --- /dev/null +++ b/alembic/env.py @@ -0,0 +1,79 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context +from src.db import models + +# 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. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = models.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() diff --git a/alembic/script.py.mako b/alembic/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${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: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/alembic/versions/59f05a12ab61_new_datetime_for_sent_at.py b/alembic/versions/59f05a12ab61_new_datetime_for_sent_at.py new file mode 100644 index 0000000..522db40 --- /dev/null +++ b/alembic/versions/59f05a12ab61_new_datetime_for_sent_at.py @@ -0,0 +1,38 @@ +"""new datetime for sent_at + +Revision ID: 59f05a12ab61 +Revises: a4ee74812db9 +Create Date: 2025-11-09 02:00:29.654082 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision: str = '59f05a12ab61' +down_revision: Union[str, None] = 'a4ee74812db9' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('sessions', 'sent_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('sessions', 'sent_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False) + # ### end Alembic commands ### diff --git a/alembic/versions/84f73c5eb1f2_change_ids.py b/alembic/versions/84f73c5eb1f2_change_ids.py new file mode 100644 index 0000000..d3a0bd1 --- /dev/null +++ b/alembic/versions/84f73c5eb1f2_change_ids.py @@ -0,0 +1,32 @@ +"""change ids + +Revision ID: 84f73c5eb1f2 +Revises: c5239bb8dbab +Create Date: 2025-11-09 01:42:52.765402 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '84f73c5eb1f2' +down_revision: Union[str, None] = 'c5239bb8dbab' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/alembic/versions/a4ee74812db9_new_datetime.py b/alembic/versions/a4ee74812db9_new_datetime.py new file mode 100644 index 0000000..6a151f6 --- /dev/null +++ b/alembic/versions/a4ee74812db9_new_datetime.py @@ -0,0 +1,166 @@ +"""new datetime + +Revision ID: a4ee74812db9 +Revises: d009e91d1863 +Create Date: 2025-11-09 01:58:33.901426 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision: str = 'a4ee74812db9' +down_revision: Union[str, None] = 'd009e91d1863' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('admins', 'created_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=True) + op.alter_column('doctors', 'created_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False) + op.alter_column('form_links', 'created_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False) + op.alter_column('patients', 'created_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False) + op.alter_column('patients', 'updated_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=True) + op.alter_column('payment_methods', 'created_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False) + op.alter_column('referral_codes', 'created_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False) + op.alter_column('referral_codes', 'used_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=True) + op.alter_column('session_date_time_history', 'updated_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False) + op.alter_column('session_date_time_history', 'consultation_date_time', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=True) + op.alter_column('session_notifications', 'created_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False) + op.alter_column('session_notifications', 'sent_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=True) + op.alter_column('session_status_history', 'updated_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False) + op.alter_column('sessions', 'consulted_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=True) + op.alter_column('sessions', 'paid_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=True) + op.alter_column('verification_requests', 'sent_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False) + op.alter_column('verification_requests', 'reviewed_at', + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=True) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('verification_requests', 'reviewed_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=True) + op.alter_column('verification_requests', 'sent_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False) + op.alter_column('sessions', 'paid_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=True) + op.alter_column('sessions', 'consulted_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=True) + op.alter_column('session_status_history', 'updated_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False) + op.alter_column('session_notifications', 'sent_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=True) + op.alter_column('session_notifications', 'created_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False) + op.alter_column('session_date_time_history', 'consultation_date_time', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=True) + op.alter_column('session_date_time_history', 'updated_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False) + op.alter_column('referral_codes', 'used_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=True) + op.alter_column('referral_codes', 'created_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False) + op.alter_column('payment_methods', 'created_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False) + op.alter_column('patients', 'updated_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=True) + op.alter_column('patients', 'created_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False) + op.alter_column('form_links', 'created_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False) + op.alter_column('doctors', 'created_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False) + op.alter_column('admins', 'created_at', + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=True) + # ### end Alembic commands ### diff --git a/alembic/versions/c5239bb8dbab_.py b/alembic/versions/c5239bb8dbab_.py new file mode 100644 index 0000000..cd4b619 --- /dev/null +++ b/alembic/versions/c5239bb8dbab_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: c5239bb8dbab +Revises: +Create Date: 2025-11-09 01:39:11.865102 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'c5239bb8dbab' +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: + """Upgrade schema.""" + pass + + +def downgrade() -> None: + """Downgrade schema.""" + pass diff --git a/alembic/versions/d009e91d1863_tables.py b/alembic/versions/d009e91d1863_tables.py new file mode 100644 index 0000000..a6129e3 --- /dev/null +++ b/alembic/versions/d009e91d1863_tables.py @@ -0,0 +1,78 @@ +"""tables + +Revision ID: d009e91d1863 +Revises: 84f73c5eb1f2 +Create Date: 2025-11-09 01:50:32.046162 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'd009e91d1863' +down_revision: Union[str, None] = '84f73c5eb1f2' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('sessions', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('code', sa.String(length=8), nullable=False), + sa.Column('patient_id', sa.Integer(), nullable=False), + sa.Column('sent_at', sa.DateTime(), nullable=False), + sa.Column('consulted_at', sa.DateTime(), nullable=True), + sa.Column('doctor_id', sa.Integer(), nullable=False), + sa.Column('paid_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['doctor_id'], ['doctors.id'], ), + sa.ForeignKeyConstraint(['patient_id'], ['patients.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code') + ) + op.create_table('session_date_time_history', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('sessions_id', sa.Integer(), nullable=False), + sa.Column('updated_at', sa.DateTime(), nullable=False), + sa.Column('consultation_date_time', sa.DateTime(), nullable=True), + sa.Column('who_updated', sa.String(length=50), nullable=True), + sa.ForeignKeyConstraint(['sessions_id'], ['sessions.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('session_notifications', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('session_id', sa.Integer(), nullable=False), + sa.Column('patient_id', sa.Integer(), nullable=False), + sa.Column('type', sa.Enum('PAYMENT_RECEIVED', name='notificationtype'), nullable=False), + sa.Column('created_at', sa.DateTime(), nullable=False), + sa.Column('sent_at', sa.DateTime(), nullable=True), + sa.Column('last_error', sa.Text(), nullable=True), + sa.ForeignKeyConstraint(['patient_id'], ['patients.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['session_id'], ['sessions.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('session_id', 'type', name='uq_session_notification_once') + ) + op.create_table('session_status_history', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('sessions_id', sa.Integer(), nullable=False), + sa.Column('updated_at', sa.DateTime(), nullable=False), + sa.Column('status', sa.String(length=50), nullable=False), + sa.Column('who_updated', sa.String(length=50), nullable=True), + sa.ForeignKeyConstraint(['sessions_id'], ['sessions.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('session_status_history') + op.drop_table('session_notifications') + op.drop_table('session_date_time_history') + op.drop_table('sessions') + # ### end Alembic commands ### diff --git a/alembic/versions/d252e6046fda_add_consultation_price.py b/alembic/versions/d252e6046fda_add_consultation_price.py new file mode 100644 index 0000000..823ca8e --- /dev/null +++ b/alembic/versions/d252e6046fda_add_consultation_price.py @@ -0,0 +1,32 @@ +"""add consultation price + +Revision ID: d252e6046fda +Revises: 59f05a12ab61 +Create Date: 2025-12-20 22:44:51.998668 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'd252e6046fda' +down_revision: Union[str, None] = '59f05a12ab61' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('doctors', sa.Column('consultation_price', sa.Numeric(precision=12, scale=2), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('doctors', 'consultation_price') + # ### end Alembic commands ###