diff --git a/src/banker/__main__.py b/src/banker/__main__.py index 85d900c..e9198d0 100644 --- a/src/banker/__main__.py +++ b/src/banker/__main__.py @@ -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() diff --git a/src/banker/common/filesystem.py b/src/banker/common/filesystem.py new file mode 100644 index 0000000..3fba831 --- /dev/null +++ b/src/banker/common/filesystem.py @@ -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) diff --git a/src/banker/common/naming.py b/src/banker/common/naming.py index 82e955b..35bbff3 100644 --- a/src/banker/common/naming.py +++ b/src/banker/common/naming.py @@ -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" diff --git a/src/banker/executor/__init__.py b/src/banker/executor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/banker/executor/executor.py b/src/banker/executor/executor.py new file mode 100644 index 0000000..e041ff7 --- /dev/null +++ b/src/banker/executor/executor.py @@ -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)) diff --git a/src/banker/gui/__init__.py b/src/banker/gui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/banker/gui/manager.py b/src/banker/gui/manager.py new file mode 100644 index 0000000..da8bc4d --- /dev/null +++ b/src/banker/gui/manager.py @@ -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() diff --git a/src/banker/gui/open_file.py b/src/banker/gui/open_file.py new file mode 100644 index 0000000..3e8a4ca --- /dev/null +++ b/src/banker/gui/open_file.py @@ -0,0 +1,5 @@ +from tkinter import filedialog + + +def get_file() -> str | None: + return filedialog.askopenfilename() diff --git a/src/banker/writer/excel_categories_writer.py b/src/banker/writer/excel_categories_writer.py index 8ab2920..06f3a72 100644 --- a/src/banker/writer/excel_categories_writer.py +++ b/src/banker/writer/excel_categories_writer.py @@ -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 = {} @@ -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))