diff --git a/alembic.ini b/alembic.ini index 09a6086..4bf4069 100644 --- a/alembic.ini +++ b/alembic.ini @@ -55,7 +55,7 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne # are written from script.py.mako # output_encoding = utf-8 -sqlalchemy.url = postgresql://pf-budget:muster-neutron-omega@database.lan/pf-budget +sqlalchemy.url = postgresql://pf-budget:muster-neutron-omega@database.home.arpa/pf-budget [post_write_hooks] diff --git a/alembic/env.py b/alembic/env.py index 69ba638..3fc5432 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -27,7 +27,7 @@ target_metadata = Base.metadata def include_name(name, type_, parent_names): if type_ == "schema": - return name in ["bank", "category", "tag", "transactions"] + return name == "pfbudget" else: return True diff --git a/alembic/versions/d7f0401c1fd3_unified_schemas.py b/alembic/versions/d7f0401c1fd3_unified_schemas.py new file mode 100644 index 0000000..a913989 --- /dev/null +++ b/alembic/versions/d7f0401c1fd3_unified_schemas.py @@ -0,0 +1,594 @@ +"""unified schemas + + +Revision ID: d7f0401c1fd3 +Revises: 952de57a3c43 +Create Date: 2023-04-27 16:30:08.514985+00:00 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = "d7f0401c1fd3" +down_revision = "952de57a3c43" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "banks", + sa.Column("name", sa.String(), nullable=False), + sa.Column("BIC", sa.String(length=8), nullable=False), + sa.Column( + "type", + sa.Enum( + "checking", + "savings", + "investment", + "mealcard", + "VISA", + "MASTERCARD", + name="accounttype", + schema="pfbudget", + inherit_schema=True, + ), + nullable=False, + ), + sa.PrimaryKeyConstraint("BIC", "type", name=op.f("pk_banks")), + sa.UniqueConstraint("name", name=op.f("uq_banks_name")), + schema="pfbudget", + ) + op.create_table( + "category_groups", + sa.Column("name", sa.String(), nullable=False), + sa.PrimaryKeyConstraint("name", name=op.f("pk_category_groups")), + schema="pfbudget", + ) + op.create_table( + "rules", + sa.Column( + "id", sa.BigInteger().with_variant(sa.Integer(), "sqlite"), nullable=False + ), + sa.Column("start", sa.Date(), nullable=True), + sa.Column("end", sa.Date(), nullable=True), + sa.Column("description", sa.String(), nullable=True), + sa.Column("regex", sa.String(), nullable=True), + sa.Column("bank", sa.String(), nullable=True), + sa.Column("min", sa.Numeric(precision=16, scale=2), nullable=True), + sa.Column("max", sa.Numeric(precision=16, scale=2), nullable=True), + sa.Column("type", sa.String(), nullable=False), + sa.PrimaryKeyConstraint("id", name=op.f("pk_rules")), + schema="pfbudget", + ) + op.create_table( + "tags", + sa.Column("name", sa.String(), nullable=False), + sa.PrimaryKeyConstraint("name", name=op.f("pk_tags")), + schema="pfbudget", + ) + op.create_table( + "banks_nordigen", + sa.Column("name", sa.Text(), nullable=False), + sa.Column("bank_id", sa.String(), nullable=True), + sa.Column("requisition_id", sa.String(), nullable=True), + sa.Column("invert", sa.Boolean(), nullable=True), + sa.ForeignKeyConstraint( + ["name"], ["pfbudget.banks.name"], name=op.f("fk_banks_nordigen_name_banks") + ), + sa.PrimaryKeyConstraint("name", name=op.f("pk_banks_nordigen")), + schema="pfbudget", + ) + op.create_table( + "categories", + sa.Column("name", sa.String(), nullable=False), + sa.Column("group", sa.String(), nullable=True), + sa.ForeignKeyConstraint( + ["group"], + ["pfbudget.category_groups.name"], + name=op.f("fk_categories_group_category_groups"), + ), + sa.PrimaryKeyConstraint("name", name=op.f("pk_categories")), + schema="pfbudget", + ) + op.create_table( + "tag_rules", + sa.Column("id", sa.BigInteger(), nullable=False), + sa.Column("tag", sa.String(), nullable=False), + sa.ForeignKeyConstraint( + ["id"], + ["pfbudget.rules.id"], + name=op.f("fk_tag_rules_id_rules"), + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["tag"], + ["pfbudget.tags.name"], + name=op.f("fk_tag_rules_tag_tags"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_tag_rules")), + schema="pfbudget", + ) + op.create_table( + "transactions", + sa.Column( + "id", sa.BigInteger().with_variant(sa.Integer(), "sqlite"), nullable=False + ), + sa.Column("date", sa.Date(), nullable=False), + sa.Column("description", sa.String(), nullable=True), + sa.Column("amount", sa.Numeric(precision=16, scale=2), nullable=False), + sa.Column("split", sa.Boolean(), nullable=False), + sa.Column("type", sa.String(), nullable=False), + sa.Column("bank", sa.Text(), nullable=True), + sa.Column("original", sa.BigInteger(), nullable=True), + sa.ForeignKeyConstraint( + ["bank"], ["pfbudget.banks.name"], name=op.f("fk_transactions_bank_banks") + ), + sa.ForeignKeyConstraint( + ["original"], + ["pfbudget.transactions.id"], + name=op.f("fk_transactions_original_transactions"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_transactions")), + schema="pfbudget", + ) + op.create_table( + "category_rules", + sa.Column("id", sa.BigInteger(), nullable=False), + sa.Column("name", sa.String(), nullable=False), + sa.ForeignKeyConstraint( + ["id"], + ["pfbudget.rules.id"], + name=op.f("fk_category_rules_id_rules"), + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["name"], + ["pfbudget.categories.name"], + name=op.f("fk_category_rules_name_categories"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_category_rules")), + schema="pfbudget", + ) + op.create_table( + "category_schedules", + sa.Column("name", sa.String(), nullable=False), + sa.Column( + "period", + sa.Enum( + "daily", + "weekly", + "monthly", + "yearly", + name="period", + schema="pfbudget", + inherit_schema=True, + ), + nullable=True, + ), + sa.Column("period_multiplier", sa.Integer(), nullable=True), + sa.Column("amount", sa.Integer(), nullable=True), + sa.ForeignKeyConstraint( + ["name"], + ["pfbudget.categories.name"], + name=op.f("fk_category_schedules_name_categories"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("name", name=op.f("pk_category_schedules")), + schema="pfbudget", + ) + op.create_table( + "links", + sa.Column("original", sa.BigInteger(), nullable=False), + sa.Column("link", sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint( + ["link"], + ["pfbudget.transactions.id"], + name=op.f("fk_links_link_transactions"), + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["original"], + ["pfbudget.transactions.id"], + name=op.f("fk_links_original_transactions"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("original", "link", name=op.f("pk_links")), + schema="pfbudget", + ) + op.create_table( + "notes", + sa.Column("id", sa.BigInteger(), nullable=False), + sa.Column("note", sa.String(), nullable=False), + sa.ForeignKeyConstraint( + ["id"], + ["pfbudget.transactions.id"], + name=op.f("fk_notes_id_transactions"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_notes")), + schema="pfbudget", + ) + op.create_table( + "transactions_categorized", + sa.Column("id", sa.BigInteger(), nullable=False), + sa.Column("name", sa.String(), nullable=False), + sa.ForeignKeyConstraint( + ["id"], + ["pfbudget.transactions.id"], + name=op.f("fk_transactions_categorized_id_transactions"), + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["name"], + ["pfbudget.categories.name"], + name=op.f("fk_transactions_categorized_name_categories"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_transactions_categorized")), + schema="pfbudget", + ) + op.create_table( + "transactions_tagged", + sa.Column("id", sa.BigInteger(), nullable=False), + sa.Column("tag", sa.String(), nullable=False), + sa.ForeignKeyConstraint( + ["id"], + ["pfbudget.transactions.id"], + name=op.f("fk_transactions_tagged_id_transactions"), + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["tag"], + ["pfbudget.tags.name"], + name=op.f("fk_transactions_tagged_tag_tags"), + ), + sa.PrimaryKeyConstraint("id", "tag", name=op.f("pk_transactions_tagged")), + schema="pfbudget", + ) + op.create_table( + "category_selectors", + sa.Column("id", sa.BigInteger(), nullable=False), + sa.Column( + "selector", + sa.Enum( + "unknown", + "nullifier", + "vacations", + "rules", + "algorithm", + "manual", + name="selector_t", + schema="pfbudget", + inherit_schema=True, + ), + nullable=False, + ), + sa.ForeignKeyConstraint( + ["id"], + ["pfbudget.transactions_categorized.id"], + name=op.f("fk_category_selectors_id_transactions_categorized"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_category_selectors")), + schema="pfbudget", + ) + op.drop_table("notes", schema="transactions") + op.drop_table("rules", schema="tag") + op.drop_table("tagged", schema="transactions") + op.drop_table("available", schema="tag") + op.drop_table("nordigen", schema="bank") + op.drop_table("links", schema="transactions") + op.drop_table("selector", schema="transactions") + op.drop_table("categorized", schema="transactions") + op.drop_table("transactions", schema="transactions") + op.drop_table("banks", schema="bank") + op.drop_table("rules", schema="category") + op.drop_table("schedules", schema="category") + op.drop_table("rules", schema="transactions") + op.drop_table("available", schema="category") + op.drop_table("groups", schema="category") + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "transactions", + sa.Column( + "id", + sa.BIGINT(), + server_default=sa.text( + "nextval('transactions.originals_id_seq'::regclass)" + ), + autoincrement=True, + nullable=False, + ), + sa.Column("date", sa.DATE(), autoincrement=False, nullable=False), + sa.Column("description", sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column("bank", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column( + "amount", + sa.NUMERIC(precision=16, scale=2), + autoincrement=False, + nullable=False, + ), + sa.Column("type", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column("split", sa.BOOLEAN(), autoincrement=False, nullable=False), + sa.Column("original", sa.BIGINT(), autoincrement=False, nullable=True), + sa.ForeignKeyConstraint( + ["bank"], ["bank.banks.name"], name="fk_originals_bank_banks" + ), + sa.ForeignKeyConstraint( + ["original"], + ["transactions.transactions.id"], + name="fk_originals_original_originals", + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name="pk_originals"), + schema="transactions", + postgresql_ignore_search_path=False, + ) + op.create_table( + "nordigen", + sa.Column("name", sa.TEXT(), autoincrement=False, nullable=False), + sa.Column("bank_id", sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column("requisition_id", sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column("invert", sa.BOOLEAN(), autoincrement=False, nullable=True), + sa.ForeignKeyConstraint( + ["name"], ["bank.banks.name"], name="fk_nordigen_name_banks" + ), + sa.PrimaryKeyConstraint("name", name="pk_nordigen"), + schema="bank", + ) + op.create_table( + "tagged", + sa.Column("id", sa.BIGINT(), autoincrement=False, nullable=False), + sa.Column("tag", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint( + ["id"], + ["transactions.transactions.id"], + name="fk_tags_id_originals", + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["tag"], ["tag.available.name"], name="fk_tags_tag_available" + ), + sa.PrimaryKeyConstraint("id", "tag", name="pk_tags"), + schema="transactions", + ) + op.create_table( + "available", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column("group", sa.VARCHAR(), autoincrement=False, nullable=True), + sa.ForeignKeyConstraint( + ["group"], ["category.groups.name"], name="fk_available_group_groups" + ), + sa.PrimaryKeyConstraint("name", name="pk_available"), + schema="category", + postgresql_ignore_search_path=False, + ) + op.create_table( + "rules", + sa.Column( + "id", + sa.BIGINT(), + server_default=sa.text("nextval('transactions.rules_id_seq'::regclass)"), + autoincrement=True, + nullable=False, + ), + sa.Column("end", sa.DATE(), autoincrement=False, nullable=True), + sa.Column("description", sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column("regex", sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column("bank", sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column( + "min", sa.NUMERIC(precision=16, scale=2), autoincrement=False, nullable=True + ), + sa.Column( + "max", sa.NUMERIC(precision=16, scale=2), autoincrement=False, nullable=True + ), + sa.Column("type", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column("start", sa.DATE(), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint("id", name="pk_rules"), + schema="transactions", + postgresql_ignore_search_path=False, + ) + op.create_table( + "groups", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="pk_groups"), + schema="category", + postgresql_ignore_search_path=False, + ) + op.create_table( + "rules", + sa.Column("id", sa.BIGINT(), autoincrement=False, nullable=False), + sa.Column("tag", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint( + ["id"], + ["transactions.rules.id"], + name="fk_rules_id_rules", + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["tag"], + ["tag.available.name"], + name="fk_rules_tag_available", + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name="pk_rules"), + schema="tag", + ) + op.create_table( + "schedules", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column( + "period", + postgresql.ENUM( + "daily", "weekly", "monthly", "yearly", name="period", schema="category" + ), + autoincrement=False, + nullable=True, + ), + sa.Column( + "period_multiplier", sa.INTEGER(), autoincrement=False, nullable=True + ), + sa.Column("amount", sa.INTEGER(), autoincrement=False, nullable=True), + sa.ForeignKeyConstraint( + ["name"], + ["category.available.name"], + name="fk_schedules_name_available", + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("name", name="pk_schedules"), + schema="category", + ) + op.create_table( + "rules", + sa.Column("id", sa.BIGINT(), autoincrement=False, nullable=False), + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint( + ["id"], + ["transactions.rules.id"], + name="fk_rules_id_rules", + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["name"], + ["category.available.name"], + name="fk_rules_name_available", + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name="pk_rules"), + schema="category", + ) + op.create_table( + "links", + sa.Column("original", sa.BIGINT(), autoincrement=False, nullable=False), + sa.Column("link", sa.BIGINT(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint( + ["link"], + ["transactions.transactions.id"], + name="fk_links_link_originals", + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["original"], + ["transactions.transactions.id"], + name="fk_links_original_originals", + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("original", "link", name="pk_links"), + schema="transactions", + ) + op.create_table( + "selector", + sa.Column("id", sa.BIGINT(), autoincrement=False, nullable=False), + sa.Column( + "selector", + postgresql.ENUM( + "unknown", + "nullifier", + "vacations", + "rules", + "algorithm", + "manual", + name="selector_t", + schema="transactions", + ), + autoincrement=False, + nullable=False, + ), + sa.ForeignKeyConstraint( + ["id"], + ["transactions.categorized.id"], + name="fk_selector_id_categorized", + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name="pk_selector"), + schema="transactions", + ) + op.create_table( + "banks", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column("BIC", sa.VARCHAR(length=8), autoincrement=False, nullable=False), + sa.Column( + "type", + postgresql.ENUM( + "checking", + "savings", + "investment", + "mealcard", + "VISA", + "MASTERCARD", + name="accounttype", + schema="bank", + ), + autoincrement=False, + nullable=False, + ), + sa.PrimaryKeyConstraint("BIC", "type", name="pk_banks"), + sa.UniqueConstraint("name", name="uq_banks_name"), + schema="bank", + postgresql_ignore_search_path=False, + ) + op.create_table( + "available", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="pk_available"), + schema="tag", + ) + op.create_table( + "notes", + sa.Column("id", sa.BIGINT(), autoincrement=False, nullable=False), + sa.Column("note", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint( + ["id"], + ["transactions.transactions.id"], + name="fk_notes_id_originals", + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name="pk_notes"), + schema="transactions", + ) + op.create_table( + "categorized", + sa.Column("id", sa.BIGINT(), autoincrement=False, nullable=False), + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint( + ["id"], + ["transactions.transactions.id"], + name="fk_categorized_id_originals", + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["name"], + ["category.available.name"], + name="fk_categorized_name_available", + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name="pk_categorized"), + schema="transactions", + ) + op.drop_table("category_selectors", schema="pfbudget") + op.drop_table("transactions_tagged", schema="pfbudget") + op.drop_table("transactions_categorized", schema="pfbudget") + op.drop_table("notes", schema="pfbudget") + op.drop_table("links", schema="pfbudget") + op.drop_table("category_schedules", schema="pfbudget") + op.drop_table("category_rules", schema="pfbudget") + op.drop_table("transactions", schema="pfbudget") + op.drop_table("tag_rules", schema="pfbudget") + op.drop_table("categories", schema="pfbudget") + op.drop_table("banks_nordigen", schema="pfbudget") + op.drop_table("tags", schema="pfbudget") + op.drop_table("rules", schema="pfbudget") + op.drop_table("category_groups", schema="pfbudget") + op.drop_table("banks", schema="pfbudget") + # ### end Alembic commands ### diff --git a/pfbudget/db/model.py b/pfbudget/db/model.py index 68d2fa6..0f38567 100644 --- a/pfbudget/db/model.py +++ b/pfbudget/db/model.py @@ -25,7 +25,7 @@ from sqlalchemy.orm import ( class Base(MappedAsDataclass, DeclarativeBase): metadata = MetaData( - schema="transactions", + schema="pfbudget", naming_convention={ "ix": "ix_%(column_0_label)s", "uq": "uq_%(table_name)s_%(column_0_name)s", @@ -58,7 +58,6 @@ class Export: class Bank(Base, Export): - __table_args__ = {"schema": "bank"} __tablename__ = "banks" name: Mapped[str] = mapped_column(unique=True) @@ -151,8 +150,7 @@ class SplitTransaction(Transaction): class CategoryGroup(Base, Export): - __table_args__ = {"schema": "category"} - __tablename__ = "groups" + __tablename__ = "category_groups" name: Mapped[str] = mapped_column(primary_key=True) @@ -162,8 +160,7 @@ class CategoryGroup(Base, Export): class Category(Base, Export): - __table_args__ = {"schema": "category"} - __tablename__ = "available" + __tablename__ = "categories" name: Mapped[str] = mapped_column(primary_key=True) group: Mapped[Optional[str]] = mapped_column( @@ -200,7 +197,7 @@ catfk = Annotated[ class TransactionCategory(Base, Export): - __tablename__ = "categorized" + __tablename__ = "transactions_categorized" id: Mapped[idfk] = mapped_column(primary_key=True, init=False) name: Mapped[catfk] @@ -222,8 +219,7 @@ class Note(Base): class Nordigen(Base, Export): - __table_args__ = {"schema": "bank"} - __tablename__ = "nordigen" + __tablename__ = "banks_nordigen" name: Mapped[bankfk] = mapped_column(primary_key=True) bank_id: Mapped[Optional[str]] @@ -241,8 +237,7 @@ class Nordigen(Base, Export): class Tag(Base): - __table_args__ = {"schema": "tag"} - __tablename__ = "available" + __tablename__ = "tags" name: Mapped[str] = mapped_column(primary_key=True) @@ -252,7 +247,7 @@ class Tag(Base): class TransactionTag(Base, Export): - __tablename__ = "tagged" + __tablename__ = "transactions_tagged" id: Mapped[idfk] = mapped_column(primary_key=True, init=False) tag: Mapped[str] = mapped_column(ForeignKey(Tag.name), primary_key=True) @@ -281,7 +276,7 @@ categoryselector = Annotated[ class CategorySelector(Base, Export): - __tablename__ = "selector" + __tablename__ = "category_selectors" id: Mapped[int] = mapped_column( BigInteger, @@ -307,8 +302,7 @@ scheduleperiod = Annotated[Selector_T, mapped_column(Enum(Period, inherit_schema class CategorySchedule(Base, Export): - __table_args__ = {"schema": "category"} - __tablename__ = "schedules" + __tablename__ = "category_schedules" name: Mapped[catfk] = mapped_column(primary_key=True) period: Mapped[Optional[scheduleperiod]] @@ -393,8 +387,7 @@ class Rule(Base, Export): class CategoryRule(Rule): - __table_args__ = {"schema": "category"} - __tablename__ = "rules" + __tablename__ = "category_rules" id: Mapped[int] = mapped_column( BigInteger, @@ -417,8 +410,7 @@ class CategoryRule(Rule): class TagRule(Rule): - __table_args__ = {"schema": "tag"} - __tablename__ = "rules" + __tablename__ = "tag_rules" id: Mapped[int] = mapped_column( BigInteger,