[DB][Refactor] Compact the category selector

The `CategorySelector` was possibly added to be incremented with other
attributes. However, since none other that the selector enum is used at
the moment, it is only adding unnecessary cluter.
The category selector value is moved to the parent
`TransactionCategory`.
This commit is contained in:
Luís Murta 2023-05-08 20:09:37 +01:00
parent 48fae5483d
commit ec22b5e5bd
Signed by: satprog
GPG Key ID: 169EF1BBD7049F94
11 changed files with 147 additions and 70 deletions

View File

@ -0,0 +1,74 @@
"""Compact category selector
Revision ID: 8623e709e111
Revises: ce68ee15e5d2
Create Date: 2023-05-08 19:00:51.063240+00:00
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = "8623e709e111"
down_revision = "ce68ee15e5d2"
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("category_selectors", schema="pfbudget")
op.add_column(
"transactions_categorized",
sa.Column(
"selector",
sa.Enum(
"unknown",
"nullifier",
"vacations",
"rules",
"algorithm",
"manual",
name="selector_t",
schema="pfbudget",
inherit_schema=True,
),
nullable=False,
),
schema="pfbudget",
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("transactions_categorized", "selector", schema="pfbudget")
op.create_table(
"category_selectors",
sa.Column("id", sa.BIGINT(), autoincrement=False, nullable=False),
sa.Column(
"selector",
postgresql.ENUM(
"unknown",
"nullifier",
"vacations",
"rules",
"algorithm",
"manual",
name="selector_t",
schema="pfbudget",
),
autoincrement=False,
nullable=False,
),
sa.ForeignKeyConstraint(
["id"],
["pfbudget.transactions_categorized.id"],
name="fk_category_selectors_id_transactions_categorized",
ondelete="CASCADE",
),
sa.PrimaryKeyConstraint("id", name="pk_category_selectors"),
schema="pfbudget",
)
# ### end Alembic commands ###

View File

@ -0,0 +1,46 @@
"""Selector type name change
Revision ID: b599dafcf468
Revises: 8623e709e111
Create Date: 2023-05-08 19:46:20.661214+00:00
"""
from alembic import op
# revision identifiers, used by Alembic.
revision = "b599dafcf468"
down_revision = "8623e709e111"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.execute(
"""
CREATE TYPE pfbudget.categoryselector
AS ENUM ('unknown', 'nullifier', 'vacations', 'rules', 'algorithm', 'manual')
"""
)
op.execute(
"""ALTER TABLE pfbudget.transactions_categorized
ALTER COLUMN selector TYPE pfbudget.categoryselector
USING selector::text::pfbudget.categoryselector
"""
)
op.execute("DROP TYPE pfbudget.selector_t")
def downgrade() -> None:
op.execute(
"""
CREATE TYPE pfbudget.selector_t
AS ENUM ('unknown', 'nullifier', 'vacations', 'rules', 'algorithm', 'manual')
"""
)
op.execute(
"""ALTER TABLE pfbudget.transactions_categorized
ALTER COLUMN selector TYPE pfbudget.selector_t
USING selector::text::pfbudget.selector_t
"""
)
op.execute("DROP TYPE pfbudget.categoryselector")

View File

@ -3,9 +3,8 @@ import decimal
from ..core.manager import Manager
from ..db.model import (
Category,
CategorySelector,
Note,
Selector_T,
CategorySelector,
SplitTransaction,
Tag,
Transaction,
@ -16,7 +15,7 @@ from ..db.model import (
class Interactive:
help = "category(:tag)/split/note:/skip/quit"
selector = Selector_T.manual
selector = CategorySelector.manual
def __init__(self, manager: Manager) -> None:
self.manager = manager
@ -79,9 +78,7 @@ class Interactive:
if len(ct) > 1:
tags = ct[1:]
next.category = TransactionCategory(
category, CategorySelector(self.selector)
)
next.category = TransactionCategory(category, self.selector)
for tag in tags:
if tag not in [t.name for t in self.tags]:
session.insert([Tag(tag)])

View File

@ -13,12 +13,11 @@ from pfbudget.db.model import (
CategoryGroup,
CategoryRule,
CategorySchedule,
CategorySelector,
Link,
MoneyTransaction,
Nordigen,
Rule,
Selector_T,
CategorySelector,
SplitTransaction,
Tag,
TagRule,
@ -270,8 +269,7 @@ class Manager:
if category := row.pop("category", None):
transaction.category = TransactionCategory(
category["name"],
CategorySelector(category["selector"]["selector"]),
category["name"], category["selector"]["selector"]
)
transactions.append(transaction)
@ -348,7 +346,7 @@ class Manager:
return parse_data(filename, args)
def askcategory(self, transaction: Transaction):
selector = CategorySelector(Selector_T.manual)
selector = CategorySelector.manual
categories = self.database.select(Category)

View File

@ -36,6 +36,10 @@ class Base(MappedAsDataclass, DeclarativeBase):
},
)
type_annotation_map = {
enum.Enum: Enum(enum.Enum, create_constraint=True, inherit_schema=True),
}
class AccountType(enum.Enum):
checking = enum.auto()
@ -205,7 +209,7 @@ catfk = Annotated[
]
class Selector_T(enum.Enum):
class CategorySelector(enum.Enum):
unknown = enum.auto()
nullifier = enum.auto()
vacations = enum.auto()
@ -220,9 +224,7 @@ class TransactionCategory(Base, Export):
id: Mapped[idfk] = mapped_column(primary_key=True, init=False)
name: Mapped[catfk]
selector: Mapped[CategorySelector] = relationship(
cascade="all, delete-orphan", default=Selector_T.unknown, lazy="joined"
)
selector: Mapped[CategorySelector] = mapped_column(default=CategorySelector.unknown)
transaction: Mapped[Transaction] = relationship(
back_populates="category", init=False, compare=False
@ -231,7 +233,7 @@ class TransactionCategory(Base, Export):
@property
def format(self):
return dict(
name=self.name, selector=self.selector.format if self.selector else None
name=self.name, selector=self.selector.name
)
@ -281,28 +283,6 @@ class TransactionTag(Base, Export, unsafe_hash=True):
return dict(tag=self.tag)
categoryselector = Annotated[
Selector_T,
mapped_column(Enum(Selector_T, inherit_schema=True)),
]
class CategorySelector(Base, Export):
__tablename__ = "category_selectors"
id: Mapped[int] = mapped_column(
BigInteger,
ForeignKey(TransactionCategory.id, ondelete="CASCADE"),
primary_key=True,
init=False,
)
selector: Mapped[categoryselector] = mapped_column(default=Selector_T.unknown)
@property
def format(self):
return dict(selector=self.selector)
class Period(enum.Enum):
daily = "daily"
weekly = "weekly"
@ -310,7 +290,9 @@ class Period(enum.Enum):
yearly = "yearly"
scheduleperiod = Annotated[Selector_T, mapped_column(Enum(Period, inherit_schema=True))]
scheduleperiod = Annotated[
CategorySelector, mapped_column(Enum(Period, inherit_schema=True))
]
class CategorySchedule(Base, Export):

View File

@ -4,7 +4,6 @@ from typing import Iterable, Sequence
from pfbudget.db.model import (
CategoryRule,
CategorySelector,
Selector_T,
Transaction,
TransactionCategory,
)
@ -32,5 +31,5 @@ class Categorizer(Transformer):
continue
transaction.category = TransactionCategory(
rule.name, CategorySelector(Selector_T.rules)
rule.name, CategorySelector.rules
)

View File

@ -6,7 +6,6 @@ from .exceptions import MoreThanOneMatchError
from .transform import Transformer
from pfbudget.db.model import (
CategorySelector,
Selector_T,
Transaction,
TransactionCategory,
)
@ -89,6 +88,6 @@ class Nullifier(Transformer):
def _nullify(self, transaction: Transaction) -> Transaction:
transaction.category = TransactionCategory(
"null", selector=CategorySelector(Selector_T.nullifier)
"null", selector=CategorySelector.nullifier
)
return transaction

View File

@ -21,7 +21,7 @@ def _(obj: Transaction) -> Mapping[str, Any]:
if obj.category:
category = {
"name": obj.category.name,
"selector": str(obj.category.selector.selector)
"selector": str(obj.category.selector)
if obj.category.selector
else None,
}

View File

@ -3,7 +3,6 @@ from decimal import Decimal
from pfbudget.db.model import (
CategorySelector,
Selector_T,
Transaction,
TransactionCategory,
)
@ -18,16 +17,12 @@ simple_transformed = [
date(2023, 1, 1),
"",
Decimal("-10"),
category=TransactionCategory(
"category#1", CategorySelector(Selector_T.algorithm)
),
category=TransactionCategory("category#1", CategorySelector.algorithm),
),
Transaction(
date(2023, 1, 2),
"",
Decimal("-50"),
category=TransactionCategory(
"category#2", CategorySelector(Selector_T.algorithm)
),
category=TransactionCategory("category#2", CategorySelector.algorithm),
),
]

View File

@ -7,9 +7,8 @@ from pfbudget.db.model import (
AccountType,
Bank,
Base,
CategorySelector,
Nordigen,
Selector_T,
CategorySelector,
Transaction,
TransactionCategory,
)
@ -43,9 +42,7 @@ def transactions(client: Client) -> list[Transaction]:
date(2023, 1, 1),
"",
Decimal("-10"),
category=TransactionCategory(
"category", CategorySelector(Selector_T.algorithm)
),
category=TransactionCategory("category", CategorySelector.algorithm),
),
Transaction(date(2023, 1, 2), "", Decimal("-50")),
]
@ -55,7 +52,6 @@ def transactions(client: Client) -> list[Transaction]:
transaction.id = i + 1
if transaction.category:
transaction.category.id = 1
transaction.category.selector.id = 1
return transactions

View File

@ -7,7 +7,6 @@ from pfbudget.db.model import (
BankTransaction,
CategoryRule,
CategorySelector,
Selector_T,
TransactionCategory,
TransactionTag,
)
@ -31,9 +30,7 @@ class TestTransform:
transactions = categorizer.transform(transactions)
for t in transactions:
assert t.category == TransactionCategory(
"null", CategorySelector(Selector_T.nullifier)
)
assert t.category == TransactionCategory("null", CategorySelector.nullifier)
def test_nullifier_inplace(self):
transactions = [
@ -48,9 +45,7 @@ class TestTransform:
categorizer.transform_inplace(transactions)
for t in transactions:
assert t.category == TransactionCategory(
"null", CategorySelector(Selector_T.nullifier)
)
assert t.category == TransactionCategory("null", CategorySelector.nullifier)
def test_nullifier_with_rules(self):
transactions = [
@ -74,9 +69,7 @@ class TestTransform:
transactions = categorizer.transform(transactions)
for t in transactions:
assert t.category == TransactionCategory(
"null", CategorySelector(Selector_T.nullifier)
)
assert t.category == TransactionCategory("null", CategorySelector.nullifier)
def test_tagger(self):
transactions = [
@ -104,6 +97,4 @@ class TestTransform:
transactions = categorizer.transform(transactions)
for t in transactions:
assert t.category == TransactionCategory(
"cat#1", CategorySelector(Selector_T.rules)
)
assert t.category == TransactionCategory("cat#1", CategorySelector.rules)