Add start date rule

Rename date to end.
This commit is contained in:
Luís Murta 2023-02-06 22:10:53 +00:00
parent 23eb2c80bd
commit f7df033d58
Signed by: satprog
GPG Key ID: 169EF1BBD7049F94
4 changed files with 70 additions and 31 deletions

View File

@ -0,0 +1,32 @@
"""Start/End date rule
Revision ID: 952de57a3c43
Revises: 18572111d9ff
Create Date: 2023-02-06 21:57:57.545327+00:00
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "952de57a3c43"
down_revision = "18572111d9ff"
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"rules", sa.Column("start", sa.Date(), nullable=True), schema="transactions"
)
op.alter_column("rules", column_name="date", new_column_name="end", schema="transactions")
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column("rules", column_name="end", new_column_name="date", schema="transactions")
op.drop_column("rules", "start", schema="transactions")
# ### end Alembic commands ###

View File

@ -1,6 +1,3 @@
from decimal import Decimal
from typing import Sequence
from pfbudget.cli.argparser import argparser from pfbudget.cli.argparser import argparser
from pfbudget.cli.interactive import Interactive from pfbudget.cli.interactive import Interactive
from pfbudget.common.types import Operation from pfbudget.common.types import Operation
@ -146,12 +143,13 @@ if __name__ == "__main__":
] ]
case Operation.RuleAdd: case Operation.RuleAdd:
keys = {"category", "date", "description", "bank", "min", "max"} keys = {"category", "start", "end", "description", "regex", "bank", "min", "max"}
assert args.keys() >= keys, f"missing {args.keys() - keys}" assert args.keys() >= keys, f"missing {args.keys() - keys}"
params = [ params = [
type.CategoryRule( type.CategoryRule(
args["date"][0] if args["date"] else None, args["start"][0] if args["start"] else None,
args["end"][0] if args["end"] else None,
args["description"][0] if args["description"] else None, args["description"][0] if args["description"] else None,
args["regex"][0] if args["regex"] else None, args["regex"][0] if args["regex"] else None,
args["bank"][0] if args["bank"] else None, args["bank"][0] if args["bank"] else None,
@ -197,12 +195,13 @@ if __name__ == "__main__":
params = [type.Tag(tag) for tag in args["tag"]] params = [type.Tag(tag) for tag in args["tag"]]
case Operation.TagRuleAdd: case Operation.TagRuleAdd:
keys = {"tag", "date", "description", "bank", "min", "max"} keys = {"tag", "start", "end", "description", "regex", "bank", "min", "max"}
assert args.keys() >= keys, f"missing {args.keys() - keys}" assert args.keys() >= keys, f"missing {args.keys() - keys}"
params = [ params = [
type.TagRule( type.TagRule(
args["date"][0] if args["date"] else None, args["start"][0] if args["start"] else None,
args["end"][0] if args["end"] else None,
args["description"][0] if args["description"] else None, args["description"][0] if args["description"] else None,
args["regex"][0] if args["regex"] else None, args["regex"][0] if args["regex"] else None,
args["bank"][0] if args["bank"] else None, args["bank"][0] if args["bank"] else None,

View File

@ -374,7 +374,8 @@ def tag_rule(parser: argparse.ArgumentParser):
def rules(parser: argparse.ArgumentParser): def rules(parser: argparse.ArgumentParser):
parser.add_argument("--date", nargs=1, type=dt.date.fromisoformat) parser.add_argument("--start", nargs=1, type=dt.date.fromisoformat)
parser.add_argument("--end", nargs=1, type=dt.date.fromisoformat)
parser.add_argument("--description", nargs=1, type=str) parser.add_argument("--description", nargs=1, type=str)
parser.add_argument("--regex", nargs=1, type=str) parser.add_argument("--regex", nargs=1, type=str)
parser.add_argument("--bank", nargs=1, type=str) parser.add_argument("--bank", nargs=1, type=str)

View File

@ -335,7 +335,8 @@ class Rule(Base, Export):
__tablename__ = "rules" __tablename__ = "rules"
id: Mapped[idpk] = mapped_column(init=False) id: Mapped[idpk] = mapped_column(init=False)
date: Mapped[Optional[dt.date]] start: Mapped[Optional[dt.date]]
end: Mapped[Optional[dt.date]]
description: Mapped[Optional[str]] description: Mapped[Optional[str]]
regex: Mapped[Optional[str]] regex: Mapped[Optional[str]]
bank: Mapped[Optional[str]] bank: Mapped[Optional[str]]
@ -349,32 +350,34 @@ class Rule(Base, Export):
"polymorphic_on": "type", "polymorphic_on": "type",
} }
def matches(self, transaction: BankTransaction) -> bool: def matches(self, t: BankTransaction) -> bool:
if ( valid = None
(self.date and self.date < transaction.date) if self.regex:
or ( valid = re.compile(self.regex, re.IGNORECASE)
self.description
and transaction.description ops = (
and self.description not in transaction.description Rule.exists(self.start, lambda r: r < t.date),
) Rule.exists(self.end, lambda r: r > t.date),
or ( Rule.exists(self.description, lambda r: r == t.description),
self.regex Rule.exists(
and transaction.description valid,
and not re.compile(self.regex, re.IGNORECASE).search( lambda r: r.search(t.description) if t.description else False,
transaction.description ),
) Rule.exists(self.bank, lambda r: r == t.bank),
) Rule.exists(self.min, lambda r: r < t.amount),
or (self.bank and self.bank != transaction.bank) Rule.exists(self.max, lambda r: r > t.amount),
or (self.min and self.min > transaction.amount) )
or (self.max and self.max < transaction.amount)
): if all(ops):
return False return True
return True
return False
@property @property
def format(self) -> dict[str, Any]: def format(self) -> dict[str, Any]:
return dict( return dict(
date=self.date, start=self.start,
end=self.end,
description=self.description, description=self.description,
regex=self.regex, regex=self.regex,
bank=self.bank, bank=self.bank,
@ -383,6 +386,10 @@ class Rule(Base, Export):
type=self.type, type=self.type,
) )
@staticmethod
def exists(r, op) -> bool:
return op(r) if r is not None else True
class CategoryRule(Rule): class CategoryRule(Rule):
__table_args__ = {"schema": "category"} __table_args__ = {"schema": "category"}