Skip to content

Commit

Permalink
Merge pull request #2 from carlosfelgarcia:Fixing-bugs-and-added-tkinter
Browse files Browse the repository at this point in the history
Add GUI for file selection and update .gitignore
  • Loading branch information
carlosfelgarcia authored Apr 9, 2024
2 parents 0d87764 + 8faebc9 commit ac9e25a
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 177 deletions.
Binary file added .DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,6 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# Visual Studio Code
.vscode/
160 changes: 0 additions & 160 deletions .gitignore copy

This file was deleted.

Binary file added Transactify.icns
Binary file not shown.
47 changes: 37 additions & 10 deletions converter.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
# MIT License 2024, Carlos Garcia
# MIT License (c) 2024. Carlos Garcia, www.carlosgarciadev.com
import json
import pathlib
import tkinter as tk
from hashlib import md5
from typing import Union

import ofxparse
import pandas as pd


class Converter:
def __init__(self, files_path: Union[pathlib.Path, None]) -> None:
def __init__(self, files_path: pathlib.Path | None, info_label: tk.Label | None = None) -> None:
self.ofx_parser = ofxparse.OfxParser()
self._info_label = info_label
if not files_path:
self.files_path = pathlib.Path.home() / "Downloads"
else:
Expand All @@ -25,7 +26,10 @@ def create_transactions_files_excel(self) -> None:
excel_files = list(self.files_path.glob("*.xlsx"))

if not excel_files:
print("No Excel files found.")
if self._info_label:
self._info_label["text"] = "No Excel files found."
else:
print("No Excel files found.")
return

if not excel_trans_path.exists():
Expand All @@ -34,7 +38,10 @@ def create_transactions_files_excel(self) -> None:
all_transactions = []

for excel_file in excel_files:
print(f"Processing {excel_file}...")
if self._info_label:
self._info_label["text"] = f"Processing {excel_file}..."
else:
print(f"Processing {excel_file}...")
df = pd.read_excel(excel_file)

df["Settlement Date"] = pd.to_datetime(df["Settlement Date"], format="%Y-%m-%d %I:%M:%S %p").dt.strftime(
Expand All @@ -45,7 +52,9 @@ def create_transactions_files_excel(self) -> None:
)

df["id"] = df.apply(
lambda row: self.consistent_hash(str(row["Net Amount"]) + str(row["Price"]) + row["Transaction Date"]),
lambda row: self.consistent_hash(
str(row["Net Amount"]) + str(row["Price"]) + row["Transaction Date"] + str(row["Account #"])
),
axis=1,
)
df["date"] = df["Settlement Date"]
Expand Down Expand Up @@ -76,20 +85,30 @@ def create_transactions_files_excel(self) -> None:
with open(json_file_path, "w") as file:
json.dump(all_transactions, file, indent=4)

if self._info_label:
self._info_label["text"] = "Excel files converted successfully."
else:
print("Excel files converted successfully.")

def create_transaction_files_qfx(self) -> None:
all_transactions = []
qfx_transaction_files = list(self.files_path.rglob("*.QFX")) + list(self.files_path.rglob("*.qfx"))
print(f"qfx_transaction_files: {qfx_transaction_files}")
if not qfx_transaction_files:
print("No QFX files found.")
if self._info_label:
self._info_label["text"] = "No QFX files found."
else:
print("No QFX files found.")
return

qfx_trans_path = self.files_path / "qfx_transactions"
if not qfx_trans_path.exists():
qfx_trans_path.mkdir()

for qfx_file in qfx_transaction_files:
print(f"Processing {qfx_file}...")
if self._info_label:
self._info_label["text"] = f"Processing {qfx_file}..."
else:
print(f"Processing {qfx_file}...")
with open(qfx_file) as file:
ofx_data = self.ofx_parser.parse(file)

Expand All @@ -100,7 +119,10 @@ def create_transaction_files_qfx(self) -> None:
accounts = [ofx_data.account]

for account in accounts:
print(f"Processing account {account.account_id}...")
if self._info_label:
self._info_label["text"] = f"Processing account {account.account_id}..."
else:
print(f"Processing account {account.account_id}...")
transactions = []
for transaction in account.statement.transactions:
name = transaction.payee.strip()
Expand All @@ -123,3 +145,8 @@ def create_transaction_files_qfx(self) -> None:
json_file_path = f"{qfx_trans_path}/all_transactions.json"
with open(json_file_path, "w") as file:
json.dump(all_transactions, file, indent=4)

if self._info_label:
self._info_label["text"] = "QFX files converted successfully."
else:
print("QFX files converted successfully.")
42 changes: 35 additions & 7 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,56 @@
#!.venv/bin/python3
import pathlib
from typing import Union
import sys

import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import typer

from converter import Converter


def main(
files_path=typer.Option(
pathlib.Path.home() / "Downloads",
None,
help="Path to the files to convert, usually the Downloads folder.",
),
):
if isinstance(files_path, str):
files_path = pathlib.Path(files_path)

if files_path is None:
files_path = pathlib.Path.home() / "Downloads"
convert_transactions(files_path)


def convert_transactions(files_path: Union[pathlib.Path, None]) -> None:
converter = Converter(files_path)
def mainUI():
root = tk.Tk()
root.withdraw()
files_path = filedialog.askdirectory(
title="Select the folder with the files to convert", initialdir=str(pathlib.Path.home() / "Downloads")
)

progress_window = tk.Tk()
progress_window.title("Converting files")
progress_window.geometry("300x200")
if not files_path:
messagebox.showerror("Error", "No folder selected.")
sys.exit(1)

info_label = tk.Label(progress_window, text="Converting files...")
info_label.pack()
convert_transactions(pathlib.Path(files_path), info_label=info_label)
messagebox.showinfo("Success", "Files converted successfully!")
progress_window.destroy()


def convert_transactions(files_path: pathlib.Path | None, info_label: tk.Label | None = None) -> None:
converter = Converter(files_path, info_label)
converter.create_transactions_files_excel()
converter.create_transaction_files_qfx()


if __name__ == "__main__":
typer.run(main)
if hasattr(sys, "frozen") and getattr(sys, "frozen") == "macosx_app":
mainUI()
else:
typer.run(main)
18 changes: 18 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# MIT License (c) 2024. Carlos Garcia, www.carlosgarciadev.com

from setuptools import setup

APP = ["main.py"]
DATA_FILES = []
OPTIONS = {
"argv_emulation": True,
"packages": ["pandas", "ofxparse"],
}

setup(
app=APP,
name="Transactify",
data_files=DATA_FILES,
options={"py2app": OPTIONS},
setup_requires=["py2app"],
)

0 comments on commit ac9e25a

Please sign in to comment.