[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:
parent
48fae5483d
commit
ec22b5e5bd
74
alembic/versions/8623e709e111_compact_category_selector.py
Normal file
74
alembic/versions/8623e709e111_compact_category_selector.py
Normal 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 ###
|
||||
46
alembic/versions/b599dafcf468_selector_type_name_change.py
Normal file
46
alembic/versions/b599dafcf468_selector_type_name_change.py
Normal 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")
|
||||
@ -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)])
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
@ -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),
|
||||
),
|
||||
]
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user