budget/pfbudget/categories.py
Luís Murta fe26bf8a6a
Adds two new plot methods and health expenses
Two new graphs created, discrete, where all discretionary expenses are
plotted, and average, for the yearly average. This later one will be
moved to report form.
Health expenses separated from required. Commute moved from fixed to
required expenses.
New by_month_and_category functions added to transactions.py.
2021-02-02 21:57:07 +00:00

225 lines
5.2 KiB
Python

from datetime import date, timedelta
from re import compile as c
class Categories:
name = ""
regex = []
banks = []
values = []
range = ()
def search(self, t):
if not self.regex:
return False
if self.banks:
return any(
pattern.search(t.description.lower())
for pattern in self.regex
if t.bank in self.banks
)
elif self.range:
return any(
pattern.search(t.description.lower())
for pattern in self.regex
if self.range[0] < t.value < self.range[1]
)
elif self.values:
return any(
pattern.search(t.description.lower())
for pattern in self.regex
if t.value in self.values
)
else:
return any(pattern.search(t.description.lower()) for pattern in self.regex)
@classmethod
def get_categories(cls):
return cls.__subclasses__()
def get_categories():
return [cat.name for cat in Categories.get_categories()]
def get_income_categories():
return [cat for cat in get_categories() if "Income" in cat]
def get_fixed_expenses():
return [Utilities.name]
def get_required_expenses():
return [Groceries.name, Commute.name]
def get_health_expenses():
return [Medical.name]
def get_discretionary_expenses():
return [
cat
for cat in get_categories()
if cat
not in [
*get_income_categories(),
*get_fixed_expenses(),
*get_required_expenses(),
*get_health_expenses(),
Investment.name,
Null.name,
]
]
class Income1(Categories):
name = "Income1"
regex = [c("company A")]
class Income2(Categories):
name = "Income2"
regex = [c("transfer")]
banks = ["BankA"]
class Income3(Categories):
name = "Income3"
regex = [c("company B")]
class Null(Categories):
name = "Null"
regex = [
c("transfer A to B"),
c("1"),
c("2"),
]
def search(self, transaction):
pass
def search_all(self, transactions):
matches = []
for transaction in transactions:
for cancel in [
cancel
for cancel in transactions
if (
transaction.date - timedelta(days=4)
<= cancel.date
<= transaction.date + timedelta(days=4)
and any(
pattern.search(transaction.description.lower())
for pattern in self.regex
)
and transaction.bank != cancel.bank
and transaction
and cancel not in matches
and cancel != transaction
)
]:
if transaction.value == -cancel.value:
matches.extend([transaction, cancel])
# if transaction.value > 0:
# transaction, cancel = cancel, transaction
# print('{} -> {}'.format(transaction, cancel))
break
return matches
class Commute(Categories):
name = "Commute"
regex = [c("uber"), c("train")]
values = [-50]
def search(self, t):
if any(pattern.search(t.description.lower()) for pattern in self.regex[:1]):
return True
elif t.value in self.values:
return any(
pattern.search(t.description.lower()) for pattern in self.regex[1:]
)
else:
return False
class Utilities(Categories):
name = "Utilities"
regex = [c("electricity", "water", "internet")]
values = [-35]
def search(self, t):
if any(pattern.search(t.description.lower()) for pattern in self.regex[:2]):
return True
elif t.value in self.values:
return any(
pattern.search(t.description.lower()) for pattern in self.regex[2:]
)
else:
return False
class Groceries(Categories):
name = "Groceries"
regex = [
c("lidl"),
c("e.leclerc"),
c("aldi"),
]
class EatingOut(Categories):
name = "Eating Out"
regex = [
c("restaurant 1"),
c("restaurant 2"),
]
class Entertainment(Categories):
name = "Entertainment"
regex = [c("cinema"), c("steam")]
class Pets(Categories):
name = "Pets"
class Travel(Categories):
name = "Travel"
regex = [c("ryanair"), c("easyjet"), c("airbnb")]
not_in_travel = [
*get_income_categories(),
Utilities.name,
]
@staticmethod
def search_all(transactions, start, end):
matches = []
for transaction in transactions:
if start <= transaction.date < end:
matches.append(transaction)
return matches
class Miscellaneous(Categories):
name = "Miscellaneous"
class Investment(Categories):
name = "Investment"
regex = [c("subscrition")]
banks = ["BankC"]
class Medical(Categories):
name = "Medical"
regex = [c("hospital", "pharmacy")]