Skip to content

Commit

Permalink
Add debts category (#42)
Browse files Browse the repository at this point in the history
* added a new category to the init_tables script
* Created a release 4.2 database migration script
* New category is added to the base mapping
* Alalytics messages are updated
* Added a persent of costs in order to see its relation with the salary
  • Loading branch information
parfeniukink authored Jun 26, 2022
1 parent 339b77f commit 455784f
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 53 deletions.
1 change: 1 addition & 0 deletions scripts/db/init_tables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ INSERT INTO categories (name) VALUES
('🎁 Gifts'),
('📦 Other'),
('💼 Business'),
('💸 Debts'),
('🔄 Currency transactions');

INSERT INTO equity (currency, value) VALUES
Expand Down
4 changes: 4 additions & 0 deletions scripts/db/release_4_2_migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from db import database

with database.cursor() as cursor:
cursor.execute("INSERT INTO categories (name) VALUES ('💸 Debts')")
128 changes: 75 additions & 53 deletions src/analytics/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,74 +25,84 @@ def __costs_by_category(cls, costs: list[Cost]):
attr = "category_id"
return groupby(sorted(costs, key=attrgetter(attr)), key=attrgetter(attr))

@classmethod
def __get_costs_block(cls, name: str, total: Decimal, sign: str = "", percent: Decimal | None = None) -> str:
"""Returns specific costs block. Used for basic analytics."""

if not total:
return ""

return "".join(
[
"\n",
LINE_ITEM.format(
key=BOLD.format(text=name),
value=get_number_in_frames(total),
),
sign,
ITALIC.format(text=f" ({percent}%)") if percent else "",
]
)

@classmethod
def __get_formatted_costs_by_currency_basic(
cls, categories_by_id: dict[int, Category], costs: list[Cost] | None
cls, categories_by_id: dict[int, Category], costs: list[Cost] | None, incomes: list[Income]
) -> str:
text = ""

if not costs:
return text

total_salary: Decimal = sum(incomes) # type: ignore

currency_transaction_category: Category = CategoriesService.get_by_name(CategoriesMapping.CURRENCY_TRANSACTIONS)
debts_category: Category = CategoriesService.get_by_name(CategoriesMapping.DEBTS)

# NOTE: Get the real costs without currenty transaction and the second one separately
# NOTE: Get the real costs, currenty transaction costs and debts separately
currency_transaction_costs: list[Cost] = [
cost for cost in costs if cost.category_id == currency_transaction_category.id
]
real_costs: list[Cost] = [cost for cost in costs if cost.category_id != currency_transaction_category.id]
debts_costs: list[Cost] = [cost for cost in costs if cost.category_id == debts_category.id]
real_costs: list[Cost] = [
cost for cost in costs if cost.category_id not in [currency_transaction_category.id, debts_category.id]
]

total_costs_sum: Decimal = sum(real_costs) # type: ignore
real_costs_sum: Decimal = sum(real_costs) # type: ignore

# NOTE: Add USD dollar sign if needed
sign = "$" if costs[0].currency == Currencies.get_database_value("USD") else ""

for id, costs_group in cls.__costs_by_category(real_costs):
category: Category = categories_by_id[id]
total_costs: Decimal = sum(costs_group) # type: ignore
total_real_costs: Decimal = sum(costs_group) # type: ignore
text += "".join(
("\n", LINE_ITEM.format(key=category.name, value=get_number_in_frames((total_costs))), sign)
("\n", LINE_ITEM.format(key=category.name, value=get_number_in_frames((total_real_costs))), sign)
)

percent = (total_costs * Decimal("100") / total_costs_sum).quantize(Decimal("0.1"))
percent = (total_real_costs * Decimal("100") / real_costs_sum).quantize(Decimal("0.1"))
text += "".join((INDENTION, ITALIC.format(text=f"({percent}%)")))

# NOTE: Add total costs block
text += (
"".join(
[
"\n\n",
"-" * 10,
"\n",
LINE_ITEM.format(
key=BOLD.format(text="📉 Real costs"),
value=get_number_in_frames(total_costs_sum),
),
sign,
]
)
if total_costs_sum > Decimal("0")
else ""
# NOTE: Generate the result message
# #################################

# NOTE: Add a separator
text += ("\n" * 2) + ("-" * 10)

# NOTE: Add costs persentage in order to see it with salary relation
real_costs_percentage = (
((real_costs_sum / total_salary) * 100).quantize(Decimal("0.01")) if total_salary else Decimal("0")
)

# NOTE: Add currency_transaction costs block
text += (
"".join(
(
"\n",
LINE_ITEM.format(
key=BOLD.format(text="🔄 Currency transactions"),
value=get_number_in_frames(
# NOTE: sum() always return Decimal for costs
sum(currency_transaction_costs), # type: ignore
),
),
sign,
)
)
if currency_transaction_costs
else ""
# NOTE: Add real costs, currency transactions costs and debts costs blocks
text += cls.__get_costs_block(
name="📉 Real costs", total=real_costs_sum, sign=sign, percent=real_costs_percentage
)
text += cls.__get_costs_block(
name=CategoriesMapping.CURRENCY_TRANSACTIONS,
total=sum(currency_transaction_costs), # type: ignore
sign=sign,
)
text += cls.__get_costs_block(name=CategoriesMapping.DEBTS, total=sum(debts_costs), sign=sign) # type: ignore

return text

Expand Down Expand Up @@ -156,35 +166,47 @@ def __get_basic_report(
costs: dict[str, list[Cost]],
incomes: dict[str, list[Income]],
) -> str:
message = BOLD.format(text=date)
message: str = BOLD.format(text=date)
uah_title = CURRENCY_REPORT_TITLE_MESSAGE.format(currency=Currencies.UAH.value)
usd_title = CURRENCY_REPORT_TITLE_MESSAGE.format(currency=Currencies.USD.value)

# NOTE: Get all incomes by category (salary or other incomes)
uah_salary_incomes: list[Income] = [
i for i in incomes.get(Currencies.get_database_value("UAH"), []) if i.salary
]
uah_other_incomes: list[Income] = [
i for i in incomes.get(Currencies.get_database_value("UAH"), []) if i.salary is False
]
usd_salary_incomes: list[Income] = [
i for i in incomes.get(Currencies.get_database_value("USD"), []) if i.salary
]
usd_other_incomes: list[Income] = [
i for i in incomes.get(Currencies.get_database_value("USD"), []) if i.salary is False
]

# NOTE: Create costs messages
uah_costs_message = cls.__get_formatted_costs_by_currency_basic(
categories_by_id, costs.get(Currencies.get_database_value("UAH"))
categories_by_id, costs.get(Currencies.get_database_value("UAH")), uah_salary_incomes
)
usd_costs_message = cls.__get_formatted_costs_by_currency_basic(
categories_by_id, costs.get(Currencies.get_database_value("USD"))
categories_by_id, costs.get(Currencies.get_database_value("USD")), usd_salary_incomes
)

# NOTE: Create incomes messages
uah_salary_incomes_message = IncomesService.get_formatted_incomes(
[i for i in incomes.get(Currencies.get_database_value("UAH"), []) if i.salary], INCOME_SALARY_SOURCE_MESSAGE
uah_salary_incomes, INCOME_SALARY_SOURCE_MESSAGE
)
usd_salary_incomes_message = IncomesService.get_formatted_incomes(
[i for i in incomes.get(Currencies.get_database_value("USD"), []) if i.salary], INCOME_SALARY_SOURCE_MESSAGE
)
uah_other_incomes_message = IncomesService.get_formatted_incomes(
[i for i in incomes.get(Currencies.get_database_value("UAH"), []) if not i.salary],
INCOME_OTHER_SOURCE_MESSAGE,
)
usd_other_incomes_message = IncomesService.get_formatted_incomes(
[i for i in incomes.get(Currencies.get_database_value("USD"), []) if not i.salary],
INCOME_OTHER_SOURCE_MESSAGE,
usd_salary_incomes, INCOME_SALARY_SOURCE_MESSAGE
)
uah_other_incomes_message = IncomesService.get_formatted_incomes(uah_other_incomes, INCOME_OTHER_SOURCE_MESSAGE)
usd_other_incomes_message = IncomesService.get_formatted_incomes(usd_other_incomes, INCOME_OTHER_SOURCE_MESSAGE)

# NOTE: Concatenate UAH data together
if uah_costs_message or uah_salary_incomes_message:
message += "\n".join([uah_title, uah_costs_message, uah_salary_incomes_message, uah_other_incomes_message])

# NOTE: Concatenate USD data together
if usd_costs_message or usd_salary_incomes_message:
message += "\n".join([usd_title, usd_costs_message, usd_salary_incomes_message, usd_other_incomes_message])

Expand Down
1 change: 1 addition & 0 deletions src/categories/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ class CategoriesMapping(str, Enum):
GIFTS = "🎁 Gifts"
OTHER = "📦 Other"
BUSINESS = "💼 Business"
DEBTS = "💸 Debts"
CURRENCY_TRANSACTIONS = "🔄 Currency transactions"

0 comments on commit 455784f

Please sign in to comment.