diff --git a/alembic/env.py b/alembic/env.py index 4b46b18..69ba638 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 ["transactions"] + return name in ["bank", "category", "tag", "transactions"] else: return True diff --git a/alembic/versions/e455c78df789_divide_by_schemas.py b/alembic/versions/e455c78df789_divide_by_schemas.py new file mode 100644 index 0000000..fb9e26d --- /dev/null +++ b/alembic/versions/e455c78df789_divide_by_schemas.py @@ -0,0 +1,452 @@ +"""Divide by schemas + +Revision ID: e455c78df789 +Revises: 6b293f78cc97 +Create Date: 2023-01-22 23:38:23.266906+00:00 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = "e455c78df789" +down_revision = "6b293f78cc97" +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="bank", + inherit_schema=True, + ), + nullable=False, + ), + sa.PrimaryKeyConstraint("BIC", "type", name=op.f("pk_banks")), + sa.UniqueConstraint("name", name=op.f("uq_banks_name")), + schema="bank", + ) + op.create_table( + "groups", + sa.Column("name", sa.String(), nullable=False), + sa.PrimaryKeyConstraint("name", name=op.f("pk_groups")), + schema="category", + ) + op.create_table( + "available", + sa.Column("name", sa.String(), nullable=False), + sa.PrimaryKeyConstraint("name", name=op.f("pk_available")), + schema="tag", + ) + op.create_table( + "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"], ["bank.banks.name"], name=op.f("fk_nordigen_name_banks") + ), + sa.PrimaryKeyConstraint("name", name=op.f("pk_nordigen")), + schema="bank", + ) + op.create_table( + "available", + sa.Column("name", sa.String(), nullable=False), + sa.Column("group", sa.String(), nullable=True), + sa.ForeignKeyConstraint( + ["group"], ["category.groups.name"], name=op.f("fk_available_group_groups") + ), + sa.PrimaryKeyConstraint("name", name=op.f("pk_available")), + schema="category", + ) + op.create_table( + "rules", + sa.Column("id", sa.BigInteger(), nullable=False), + sa.Column("tag", sa.String(), nullable=False), + sa.ForeignKeyConstraint( + ["id"], + ["transactions.rules.id"], + name=op.f("fk_rules_id_rules"), + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["tag"], + ["tag.available.name"], + name=op.f("fk_rules_tag_available"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_rules")), + schema="tag", + ) + op.create_table( + "rules", + sa.Column("id", sa.BigInteger(), nullable=False), + sa.Column("name", sa.String(), nullable=False), + sa.ForeignKeyConstraint( + ["id"], + ["transactions.rules.id"], + name=op.f("fk_rules_id_rules"), + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["name"], + ["category.available.name"], + name=op.f("fk_rules_name_available"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_rules")), + schema="category", + ) + op.create_table( + "schedules", + sa.Column("name", sa.String(), nullable=False), + sa.Column( + "period", + sa.Enum( + "daily", + "weekly", + "monthly", + "yearly", + name="period", + schema="category", + inherit_schema=True, + ), + nullable=True, + ), + sa.Column("period_multiplier", sa.Integer(), nullable=True), + sa.Column("amount", sa.Integer(), nullable=True), + sa.ForeignKeyConstraint( + ["name"], + ["category.available.name"], + name=op.f("fk_schedules_name_available"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("name", name=op.f("pk_schedules")), + schema="category", + ) + op.create_table( + "selector", + sa.Column("id", sa.BigInteger(), nullable=False), + sa.Column( + "selector", + sa.Enum( + "unknown", + "nullifier", + "vacations", + "rules", + "algorithm", + "manual", + name="selector_t", + schema="category", + inherit_schema=True, + ), + nullable=False, + ), + sa.ForeignKeyConstraint( + ["id"], + ["transactions.categorized.id"], + name=op.f("fk_selector_id_categorized"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_selector")), + schema="category", + ) + op.drop_constraint( + "fk_categorized_name_categories_available", + "categorized", + schema="transactions", + type_="foreignkey", + ) + op.create_foreign_key( + op.f("fk_categorized_name_available"), + "categorized", + "available", + ["name"], + ["name"], + source_schema="transactions", + referent_schema="category", + ondelete="CASCADE", + ) + op.drop_constraint( + "fk_originals_bank_banks", + "originals", + schema="transactions", + type_="foreignkey", + ) + op.create_foreign_key( + op.f("fk_originals_bank_banks"), + "originals", + "banks", + ["bank"], + ["name"], + source_schema="transactions", + referent_schema="bank", + ) + op.drop_constraint( + "fk_tags_tag_tags_available", "tags", schema="transactions", type_="foreignkey" + ) + op.create_foreign_key( + op.f("fk_tags_tag_available"), + "tags", + "available", + ["tag"], + ["name"], + source_schema="transactions", + referent_schema="tag", + ) + op.drop_table("categories_schedules", schema="transactions") + op.drop_table("categories_rules", schema="transactions") + op.drop_table("categories_available", schema="transactions") + op.drop_table("tag_rules", schema="transactions") + op.drop_table("nordigen", schema="transactions") + op.drop_table("tags_available", schema="transactions") + op.drop_table("banks", schema="transactions") + op.drop_table("categories_selector", schema="transactions") + op.drop_table("categories_groups", schema="transactions") + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint( + op.f("fk_tags_tag_available"), "tags", schema="transactions", type_="foreignkey" + ) + op.create_foreign_key( + "fk_tags_tag_tags_available", + "tags", + "tags_available", + ["tag"], + ["name"], + source_schema="transactions", + referent_schema="transactions", + ) + op.drop_constraint( + op.f("fk_originals_bank_banks"), + "originals", + schema="transactions", + type_="foreignkey", + ) + op.create_foreign_key( + "fk_originals_bank_banks", + "originals", + "banks", + ["bank"], + ["name"], + source_schema="transactions", + referent_schema="transactions", + ) + op.drop_constraint( + op.f("fk_categorized_name_available"), + "categorized", + schema="transactions", + type_="foreignkey", + ) + op.create_foreign_key( + "fk_categorized_name_categories_available", + "categorized", + "categories_available", + ["name"], + ["name"], + source_schema="transactions", + referent_schema="transactions", + ondelete="CASCADE", + ) + op.create_table( + "categories_groups", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="pk_categories_groups"), + schema="transactions", + postgresql_ignore_search_path=False, + ) + op.create_table( + "categories_selector", + sa.Column("id", sa.BIGINT(), autoincrement=False, nullable=False), + sa.Column( + "selector", + postgresql.ENUM( + "unknown", + "nullifier", + "vacations", + "rules", + "algorithm", + "manual", + name="selector", + schema="transactions", + ), + autoincrement=False, + nullable=False, + ), + sa.ForeignKeyConstraint( + ["id"], + ["transactions.categorized.id"], + name="fk_categories_selector_id_categorized", + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name="pk_categories_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="transactions", + ), + autoincrement=False, + nullable=False, + ), + sa.PrimaryKeyConstraint("BIC", "type", name="pk_banks"), + sa.UniqueConstraint("name", name="uq_banks_name"), + schema="transactions", + postgresql_ignore_search_path=False, + ) + op.create_table( + "tags_available", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="pk_tags_available"), + 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"], ["transactions.banks.name"], name="fk_nordigen_name_banks" + ), + sa.PrimaryKeyConstraint("name", name="pk_nordigen"), + schema="transactions", + ) + op.create_table( + "tag_rules", + sa.Column( + "id", + sa.BIGINT(), + server_default=sa.text( + "nextval('transactions.tag_rules_id_seq'::regclass)" + ), + autoincrement=True, + nullable=False, + ), + sa.Column("tag", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint( + ["id"], + ["transactions.rules.id"], + name="fk_tag_rules_id_rules", + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["tag"], + ["transactions.tags_available.name"], + name="fk_tag_rules_tag_tags_available", + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name="pk_tag_rules"), + schema="transactions", + ) + op.create_table( + "categories_rules", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column( + "id", + sa.BIGINT(), + server_default=sa.text( + "nextval('transactions.categories_rules_id_seq'::regclass)" + ), + autoincrement=True, + nullable=False, + ), + sa.ForeignKeyConstraint( + ["id"], + ["transactions.rules.id"], + name="fk_categories_rules_id_rules", + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["name"], + ["transactions.categories_available.name"], + name="fk_categories_rules_name_categories_available", + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name="pk_categories_rules"), + schema="transactions", + ) + op.create_table( + "categories_available", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column("group", sa.VARCHAR(), autoincrement=False, nullable=True), + sa.ForeignKeyConstraint( + ["group"], + ["transactions.categories_groups.name"], + name="fk_categories_available_group_categories_groups", + ), + sa.PrimaryKeyConstraint("name", name="pk_categories_available"), + schema="transactions", + postgresql_ignore_search_path=False, + ) + op.create_table( + "categories_schedules", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column( + "period", + postgresql.ENUM( + "daily", + "weekly", + "monthly", + "yearly", + name="period", + schema="transactions", + ), + 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"], + ["transactions.categories_available.name"], + name="fk_categories_schedules_name_categories_available", + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("name", name="pk_categories_schedules"), + schema="transactions", + ) + op.drop_table("selector", schema="category") + op.drop_table("schedules", schema="category") + op.drop_table("rules", schema="category") + op.drop_table("rules", schema="tag") + op.drop_table("available", schema="category") + op.drop_table("nordigen", schema="bank") + op.drop_table("available", schema="tag") + op.drop_table("groups", schema="category") + op.drop_table("banks", schema="bank") + # ### end Alembic commands ### diff --git a/pfbudget/db/model.py b/pfbudget/db/model.py index a00dec4..8c1d6bc 100644 --- a/pfbudget/db/model.py +++ b/pfbudget/db/model.py @@ -24,15 +24,15 @@ from sqlalchemy.orm import ( class Base(MappedAsDataclass, DeclarativeBase): - __table_args__ = {"schema": "transactions"} metadata = MetaData( + schema="transactions", naming_convention={ "ix": "ix_%(column_0_label)s", "uq": "uq_%(table_name)s_%(column_0_name)s", "ck": "ck_%(table_name)s_`%(constraint_name)s`", "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", "pk": "pk_%(table_name)s", - } + }, ) @@ -58,6 +58,7 @@ class Export: class Bank(Base, Export): + __table_args__ = {"schema": "bank"} __tablename__ = "banks" name: Mapped[str] = mapped_column(unique=True) @@ -147,7 +148,8 @@ class SplitTransaction(Transaction): class CategoryGroup(Base, Export): - __tablename__ = "categories_groups" + __table_args__ = {"schema": "category"} + __tablename__ = "groups" name: Mapped[str] = mapped_column(primary_key=True) @@ -157,7 +159,8 @@ class CategoryGroup(Base, Export): class Category(Base, Export): - __tablename__ = "categories_available" + __table_args__ = {"schema": "category"} + __tablename__ = "available" name: Mapped[str] = mapped_column(primary_key=True) group: Mapped[Optional[str]] = mapped_column( @@ -178,7 +181,7 @@ class Category(Base, Export): def format(self) -> dict[str, Any]: return dict( name=self.name, - group=self.group.format if self.group else None, + group=self.group if self.group else None, rules=[rule.format for rule in self.rules], schedule=self.schedule.format if self.schedule else None, ) @@ -213,6 +216,7 @@ class Note(Base): class Nordigen(Base, Export): + __table_args__ = {"schema": "bank"} __tablename__ = "nordigen" name: Mapped[bankfk] = mapped_column(primary_key=True) @@ -231,7 +235,8 @@ class Nordigen(Base, Export): class Tag(Base): - __tablename__ = "tags_available" + __table_args__ = {"schema": "tag"} + __tablename__ = "available" name: Mapped[str] = mapped_column(primary_key=True) @@ -254,7 +259,7 @@ class TransactionTag(Base, Export): return hash(self.id) -class Selector(enum.Enum): +class Selector_T(enum.Enum): unknown = enum.auto() nullifier = enum.auto() vacations = enum.auto() @@ -264,13 +269,14 @@ class Selector(enum.Enum): categoryselector = Annotated[ - Selector, - mapped_column(Enum(Selector, inherit_schema=True), default=Selector.unknown), + Selector_T, + mapped_column(Enum(Selector_T, inherit_schema=True), default=Selector_T.unknown), ] class CategorySelector(Base, Export): - __tablename__ = "categories_selector" + __table_args__ = {"schema": "category"} + __tablename__ = "selector" id: Mapped[int] = mapped_column( BigInteger, @@ -292,11 +298,12 @@ class Period(enum.Enum): yearly = "yearly" -scheduleperiod = Annotated[Selector, mapped_column(Enum(Period, inherit_schema=True))] +scheduleperiod = Annotated[Selector_T, mapped_column(Enum(Period, inherit_schema=True))] class CategorySchedule(Base, Export): - __tablename__ = "categories_schedules" + __table_args__ = {"schema": "category"} + __tablename__ = "schedules" name: Mapped[catfk] = mapped_column(primary_key=True) period: Mapped[Optional[scheduleperiod]] @@ -374,7 +381,8 @@ class Rule(Base, Export): class CategoryRule(Rule): - __tablename__ = "categories_rules" + __table_args__ = {"schema": "category"} + __tablename__ = "rules" id: Mapped[int] = mapped_column( BigInteger, @@ -397,7 +405,8 @@ class CategoryRule(Rule): class TagRule(Rule): - __tablename__ = "tag_rules" + __table_args__ = {"schema": "tag"} + __tablename__ = "rules" id: Mapped[int] = mapped_column( BigInteger,