Skip to content

Commit

Permalink
Add very basic GUI
Browse files Browse the repository at this point in the history
Enables the user to select input file, output directory and perform
analysis.

- after successful file selection button color turns to green
- added error message if user tries to analyze data before setting input
  file
- added another button to load categories.json (in case user wants to use
  custom categories.json instead of default one)
- added antother button to exit application
  • Loading branch information
WojtekMs committed Jan 19, 2024
1 parent 743b222 commit 952979b
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 56 deletions.
57 changes: 3 additions & 54 deletions src/banker/__main__.py
Original file line number Diff line number Diff line change
@@ -1,58 +1,7 @@
import argparse
import os.path

from importlib_resources import files

from banker.analyzer.analyze import analyze_transactions, deduce_month_year
from banker.data.category import Category

from banker.data.transaction import Transaction
from banker.formatter.month_year_formatter import format_month_year
from banker.parser.html_transactions_parser import HtmlTransactionsParser
from banker.formatter.html_transactions_formatter import HtmlTransactionsFormatter
from banker.parser.interfaces.categories_parser import ICategoriesParser
from banker.parser.interfaces.transactions_parser import ITransactionsParser
from banker.parser.json_categories_parser import JsonCategoriesParser
from banker.writer.excel_categories_writer import ExcelCategoriesWriter


def get_supported_categories(categories_parser: ICategoriesParser, categories_filepath: str) -> list[Category]:
with open(categories_filepath, "r") as file:
return categories_parser.parse_categories(file.read())


def get_transactions(transactions_parser: ITransactionsParser, transactions_filepath: str) -> list[Transaction]:
with open(transactions_filepath, "r") as transactions_file:
return transactions_parser.parse_transactions(transactions_file.read())


def save_to_file(filepath: str, content: str):
with open(filepath, "w") as file:
file.write(content)
from banker.gui.manager import GuiManager


def main():
transactions_parser = HtmlTransactionsParser()
categories_parser = JsonCategoriesParser()
transactions_formatter = HtmlTransactionsFormatter()
categories_writer = ExcelCategoriesWriter()

parser = argparse.ArgumentParser()
parser.add_argument("html_file")
parser.add_argument("--categories_file", default=files('banker.resources').joinpath('categories.json'))
parser.add_argument("--output_directory", default=files('banker.resources').joinpath('output'))
args = parser.parse_args()

os.makedirs(args.output_directory, exist_ok=True)
output_unmatched_transactions_filepath = os.path.join(args.output_directory, "unmatched_transactions.html")
output_matched_categories_filepath = os.path.join(args.output_directory, "autogen_budget.xlsx")

all_transactions = get_transactions(transactions_parser, args.html_file)
month_year = deduce_month_year(all_transactions)
supported_categories = get_supported_categories(categories_parser, args.categories_file)
analyze_result = analyze_transactions(all_transactions, supported_categories)
formatted_transactions = transactions_formatter.format_transactions(analyze_result.unmatched_transactions)
gui_manager = GuiManager()

save_to_file(output_unmatched_transactions_filepath, formatted_transactions)
categories_writer.write_categories(analyze_result.matched_categories, output_matched_categories_filepath,
format_month_year(month_year))
gui_manager.run_mainloop()
19 changes: 19 additions & 0 deletions src/banker/common/filesystem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from banker.data.category import Category
from banker.data.transaction import Transaction
from banker.parser.interfaces.categories_parser import ICategoriesParser
from banker.parser.interfaces.transactions_parser import ITransactionsParser


def get_parsed_categories(categories_parser: ICategoriesParser, categories_filepath: str) -> list[Category]:
with open(categories_filepath, "r") as file:
return categories_parser.parse_categories(file.read())


def get_parsed_transactions(transactions_parser: ITransactionsParser, transactions_filepath: str) -> list[Transaction]:
with open(transactions_filepath, "r") as transactions_file:
return transactions_parser.parse_transactions(transactions_file.read())


def save_to_file(filepath: str, content: str):
with open(filepath, "w") as file:
file.write(content)
5 changes: 5 additions & 0 deletions src/banker/common/naming.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@
CATEGORIES_KEY_NAME_CATEGORY_NAME = "name"
CATEGORIES_KEY_NAME_CATEGORY_PAYMENT_TYPE = "payment_type"
CATEGORIES_KEY_NAME_CATEGORY_REGEXES = "matching_regexes"

GUI_APP_NAME = "Bankier"
GUI_SELECT_TRANSACTIONS_BUTTON_TITLE = "Wybierz plik z transakcjami"
GUI_SELECT_TRANSACTIONS_DIALOG_TITLE = "Wybierz plik z transakcjami"
GUI_SELECT_OUTPUT_DIR_DIALOG_TITLE = "Wybierz katalog na pliki wyjściowe"
Empty file added src/banker/executor/__init__.py
Empty file.
34 changes: 34 additions & 0 deletions src/banker/executor/executor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import os

from banker.analyzer.analyze import deduce_month_year, analyze_transactions
from banker.common.filesystem import get_parsed_categories, get_parsed_transactions, save_to_file
from banker.formatter.interfaces.transactions_formatter import ITransactionsFormatter
from banker.formatter.month_year_formatter import format_month_year
from banker.parser.interfaces.categories_parser import ICategoriesParser
from banker.parser.interfaces.transactions_parser import ITransactionsParser
from banker.writer.interfaces.categories_writer import ICategoriesWriter


class Executor:
def __init__(self, transactions_parser: ITransactionsParser, categories_parser: ICategoriesParser,
transactions_formatter: ITransactionsFormatter, categories_writer: ICategoriesWriter):
self.__transactions_parser = transactions_parser
self.__categories_parser = categories_parser
self.__transactions_formatter = transactions_formatter
self.__categories_writer = categories_writer

def execute(self, transactions_filepath: str, categories_filepath: str, output_directory: str):
os.makedirs(output_directory, exist_ok=True)
output_unmatched_transactions_filepath = os.path.join(output_directory, "unmatched_transactions.html")
output_matched_categories_filepath = os.path.join(output_directory, "autogen_budget.xlsx")

all_transactions = get_parsed_transactions(self.__transactions_parser, transactions_filepath)
month_year = deduce_month_year(all_transactions)
supported_categories = get_parsed_categories(self.__categories_parser, categories_filepath)
analyze_result = analyze_transactions(all_transactions, supported_categories)
formatted_transactions = self.__transactions_formatter.format_transactions(
analyze_result.unmatched_transactions)

save_to_file(output_unmatched_transactions_filepath, formatted_transactions)
self.__categories_writer.write_categories(analyze_result.matched_categories, output_matched_categories_filepath,
format_month_year(month_year))
Empty file added src/banker/gui/__init__.py
Empty file.
108 changes: 108 additions & 0 deletions src/banker/gui/manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
from importlib_resources import files

from banker.executor.executor import Executor
from banker.formatter.html_transactions_formatter import HtmlTransactionsFormatter
from banker.parser.html_transactions_parser import HtmlTransactionsParser
from banker.parser.json_categories_parser import JsonCategoriesParser
from banker.writer.excel_categories_writer import ExcelCategoriesWriter


class GuiManager:
def __init__(self):
self.__app_name = "Bankier"
self.__select_categories_button_title = "Wybierz plik konfiguracyjny"
self.__select_transactions_button_title = "Wybierz plik z transakcjami"
self.__select_transactions_dialog_title = "Wybierz plik z transakcjami"
self.__select_output_dir_dialog_title = "Wybierz katalog na pliki wyjściowe"
self.__analyze_button_title = "Analizuj"
self.__exit_button_title = "Wyjdź"
self.__correct_color = "#1ec83d"
self.__correct_active_color = "#23de45"

self.__transactions_filepath: str | None = None
self.__output_directory: str | None = None
self.__categories_filepath: str = str(files('banker.resources').joinpath('categories.json'))

self.__executor = Executor(transactions_parser=HtmlTransactionsParser(),
categories_parser=JsonCategoriesParser(),
transactions_formatter=HtmlTransactionsFormatter(),
categories_writer=ExcelCategoriesWriter())

self.__construct_gui()

def __construct_gui(self):
self.__window = tk.Tk()
self.__window.title(self.__app_name)
self.__window.rowconfigure(0, minsize=300, weight=1)
self.__window.columnconfigure(0, minsize=300, weight=1)
self.__frame_buttons = tk.Frame(self.__window)

# categories button
self.__select_categories_file_button = tk.Button(self.__frame_buttons,
text=self.__select_categories_button_title,
command=self.__set_categories_file)
self.__select_categories_file_button.grid(row=0, column=0, padx=10, pady=10)
self.__select_categories_file_button.configure(bg=self.__correct_color)
self.__select_categories_file_button.configure(activebackground=self.__correct_active_color)

# transactions button
self.__select_file_button = tk.Button(self.__frame_buttons, text=self.__select_transactions_button_title,
command=self.__set_transactions_file)
self.__select_file_button.grid(row=1, column=0, padx=10, pady=10)
self.__select_file_button_default_color = self.__select_file_button.cget("background")
self.__select_file_button_default_active_color = self.__select_file_button.cget("activebackground")

# analyze button
self.__analyze_button = tk.Button(self.__frame_buttons, text=self.__analyze_button_title,
command=self.__analyze)
self.__analyze_button.grid(row=2, column=0, padx=10, pady=10)

# exit button
self.__exit_button = tk.Button(self.__frame_buttons, text=self.__exit_button_title,
command=lambda: self.__window.destroy())
self.__exit_button.grid(row=3, column=0, padx=10, pady=10)

self.__frame_buttons.grid(row=0, column=0)

def __set_categories_file(self):
categories_filepath = filedialog.askopenfilename(title=self.__select_categories_button_title,
defaultextension=".json",
filetypes=[("Pliki JSON", "*.json")])
if not categories_filepath:
return
self.__categories_filepath = categories_filepath

def __set_transactions_file(self):
self.__transactions_filepath = filedialog.askopenfilename(title=self.__select_transactions_dialog_title,
defaultextension=".html",
filetypes=[("Pliki HTML", "*.html")])
self.__select_file_button.configure(bg=self.__correct_color)
self.__select_file_button.configure(activebackground=self.__correct_active_color)
if not self.__transactions_filepath:
self.__transactions_filepath = None
self.__select_file_button.configure(bg=self.__select_file_button_default_color)
self.__select_file_button.configure(activebackground=self.__select_file_button_default_active_color)

def __analyze(self):
if not self.__transactions_filepath:
tk.messagebox.showerror(title="Błąd",
message=f"Musisz wybrać plik z transakcjami")
return
self.__output_directory = filedialog.askdirectory(title=self.__select_output_dir_dialog_title)
if not self.__output_directory:
self.__output_directory = None
return

try:
self.__executor.execute(self.__transactions_filepath, self.__categories_filepath, self.__output_directory)
tk.messagebox.showinfo(title="Sukces",
message=f"Pliki wyjściowe zapisano w katalogu: {self.__output_directory}")
except Exception as e:
tk.messagebox.showerror(title="Błąd",
message=f"Wystąpił błąd: {e}")

def run_mainloop(self):
self.__window.mainloop()
5 changes: 5 additions & 0 deletions src/banker/gui/open_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from tkinter import filedialog


def get_file() -> str | None:
return filedialog.askopenfilename()
8 changes: 6 additions & 2 deletions src/banker/writer/excel_categories_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ def __init__(self):
self.__bold_font = Font(bold=True)
self.__title_cell_location = Location(row=2, col=2)
self.__table_headers_locations = self.__generate_table_headers_locations()
self.__next_category_location = {payment_type: Location(location.row + 1, location.col) for
payment_type, location in self.__table_headers_locations.items()}
self.__next_category_location = self.__make_next_category_location()

def __make_next_category_location(self):
return {payment_type: Location(location.row + 1, location.col) for
payment_type, location in self.__table_headers_locations.items()}

def __generate_table_headers_locations(self):
result = {}
Expand Down Expand Up @@ -55,6 +58,7 @@ def __set_table_headers(self, sheet: Worksheet):
subheader_category_cell.font = self.__bold_font

def __set_categories(self, sheet: Worksheet, categories: list[Category]):
self.__next_category_location = self.__make_next_category_location()
for category in categories:
location = self.__next_category_location[category.get_payment_type()]
sheet.cell(row=location.row, column=location.col, value=abs(category.value.amount))
Expand Down

0 comments on commit 952979b

Please sign in to comment.