Implement show/hide warnings depending on columns
This commit is contained in:
parent
14e830ead0
commit
8a0d7f748f
@ -8,9 +8,7 @@ import argparse
|
||||
def categorize_func(args):
|
||||
import pandas as pd
|
||||
|
||||
df = None
|
||||
if args.i is not None:
|
||||
df = pd.read_csv(args.i, delimiter=';')
|
||||
df = pd.read_csv(args.i, delimiter=args.d)
|
||||
|
||||
import signal
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
@ -35,11 +33,14 @@ def main():
|
||||
|
||||
categorize_parser = subparsers.add_parser("categorize")
|
||||
categorize_parser.set_defaults(func=categorize_func)
|
||||
categorize_parser.add_argument('-i', required=False,
|
||||
categorize_parser.add_argument('-i', required=True,
|
||||
help="Bank statement CSV")
|
||||
categorize_parser.add_argument('-f', required=False,
|
||||
help="JSON file containing regexes to"
|
||||
" pre-categorize statement entries")
|
||||
categorize_parser.add_argument('-d', required=False,
|
||||
help="Delimiter to use when reading the"
|
||||
" bank statement", default=',')
|
||||
|
||||
report_parser = subparsers.add_parser("report")
|
||||
report_parser.set_defaults(func=report_func)
|
||||
|
||||
@ -8,7 +8,7 @@ from PyQt6.QtCore import Qt, QSortFilterProxyModel
|
||||
from PyQt6.QtGui import QPixmap, QAction
|
||||
from PyQt6.QtWidgets import QMainWindow, QPushButton, QHBoxLayout, QLabel, \
|
||||
QVBoxLayout, QMenu, QApplication, QTableView, QListView, QInputDialog, \
|
||||
QMessageBox
|
||||
QMessageBox, QFileDialog
|
||||
|
||||
|
||||
class PandasModel(QtCore.QAbstractTableModel):
|
||||
@ -74,31 +74,34 @@ class MainWindow(QMainWindow):
|
||||
|
||||
self._warning_layout \
|
||||
= self.findChild(QVBoxLayout, "warningLayout")
|
||||
|
||||
self._create_button \
|
||||
= self.findChild(QPushButton, "createCategoryButton")
|
||||
self._delete_button \
|
||||
= self.findChild(QPushButton, "deleteCategoryButton")
|
||||
self._apply_button \
|
||||
= self.findChild(QPushButton, "applyCategoryButton")
|
||||
|
||||
self._list_view \
|
||||
= self.findChild(QListView, "categoryListView")
|
||||
|
||||
self._table_view \
|
||||
= self.findChild(QTableView, "transactionTableView")
|
||||
self._action_save \
|
||||
= self.findChild(QAction, "actionSave")
|
||||
|
||||
self._warnings = []
|
||||
|
||||
self._category_model = QtGui.QStandardItemModel()
|
||||
self._list_view.setModel(self._category_model)
|
||||
|
||||
self._create_button.clicked.connect(
|
||||
self._handle_create_click)
|
||||
self._create_button.clicked.connect(self._handle_create_click)
|
||||
self._delete_button.clicked.connect(self._handle_create_click)
|
||||
self._apply_button.clicked.connect(self._handle_create_click)
|
||||
self._action_save.triggered.connect(self._handle_save)
|
||||
|
||||
header = self._table_view.horizontalHeader()
|
||||
header.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
||||
header.customContextMenuRequested.connect(self._header_right_clicked)
|
||||
|
||||
def add_warning_text(self, text: str):
|
||||
def _add_warning_text(self, text: str):
|
||||
layout = QHBoxLayout()
|
||||
|
||||
warningIcon = QLabel()
|
||||
@ -115,6 +118,7 @@ class MainWindow(QMainWindow):
|
||||
layout.setStretch(1, 1)
|
||||
|
||||
self._warning_layout.addLayout(layout)
|
||||
self._warnings.append((layout, warningIcon, label))
|
||||
|
||||
def set_statement_data(self, df: pd.DataFrame):
|
||||
model = PandasModel(df)
|
||||
@ -122,14 +126,42 @@ class MainWindow(QMainWindow):
|
||||
proxyModel.setSourceModel(model)
|
||||
self._table_view.setModel(proxyModel)
|
||||
|
||||
if len(df.columns) < 7:
|
||||
if len(df.columns) < 10: # Experimentally determined threshold
|
||||
# Properly resize columns (takes longer)
|
||||
self._table_view.resizeColumnsToContents()
|
||||
else:
|
||||
# Quickly approximate sizes
|
||||
for i, col in enumerate(df.columns):
|
||||
max_char = max(max([len(str(x)) for x in df[col].values]),
|
||||
len(col))
|
||||
self._table_view.setColumnWidth(i, max_char * 10)
|
||||
|
||||
self._show_warnings(df)
|
||||
|
||||
def _show_warnings(self, df: pd.DataFrame):
|
||||
for (layout, icon, text) in self._warnings:
|
||||
icon.hide()
|
||||
text.hide()
|
||||
self._warning_layout.removeItem(layout)
|
||||
|
||||
if 't' not in df.columns:
|
||||
self._add_warning_text(
|
||||
"The column 't' does not exist. Please rename the"
|
||||
" column containing the dates of the transactions"
|
||||
" to 't'.")
|
||||
|
||||
if 'value' not in df.columns:
|
||||
self._add_warning_text(
|
||||
"The column 'value' does not exist. Please rename"
|
||||
" the column containing the values of the"
|
||||
" transactions to 'value'.")
|
||||
|
||||
if 'balance' not in df.columns:
|
||||
self._add_warning_text(
|
||||
"The column 'balance' does not exist. Please"
|
||||
" rename the column containing the balance"
|
||||
" after each transaction to 'balance'")
|
||||
|
||||
def get_statement_data(self) -> pd.DataFrame:
|
||||
return self._table_view.model().sourceModel().get_dataframe()
|
||||
|
||||
@ -143,15 +175,19 @@ class MainWindow(QMainWindow):
|
||||
|
||||
rename_action = QAction("Rename", self)
|
||||
delete_action = QAction("Delete", self)
|
||||
switch_action = QAction("Switch position", self)
|
||||
|
||||
column = self._table_view.horizontalHeader().logicalIndexAt(pos)
|
||||
rename_action.triggered.connect(
|
||||
partial(self._header_rename_handler, column))
|
||||
delete_action.triggered.connect(
|
||||
partial(self._header_delete_handler, column))
|
||||
switch_action.triggered.connect(
|
||||
partial(self._header_switch_handler, column))
|
||||
|
||||
context.addAction(rename_action)
|
||||
context.addAction(delete_action)
|
||||
context.addAction(switch_action)
|
||||
|
||||
context.exec(self.sender().mapToGlobal(pos))
|
||||
|
||||
@ -181,6 +217,26 @@ class MainWindow(QMainWindow):
|
||||
in enumerate(df.columns) if j != column]]
|
||||
self.set_statement_data(df)
|
||||
|
||||
def _header_switch_handler(self, column):
|
||||
model = self._table_view.horizontalHeader().model()
|
||||
column_text = model.headerData(column, Qt.Orientation.Horizontal)
|
||||
|
||||
df = self.get_statement_data()
|
||||
|
||||
other_name, _ = QInputDialog.getItem(self, "Switch column position",
|
||||
f"Switch position of colum"
|
||||
f" '{column_text}' with:",
|
||||
df.columns, editable=False)
|
||||
|
||||
column_titles = list(df.columns)
|
||||
index1, index2 = column_titles.index(column_text), column_titles.index(
|
||||
other_name)
|
||||
column_titles[index1], column_titles[index2] \
|
||||
= column_titles[index2], column_titles[index1]
|
||||
|
||||
df = df.reindex(columns=column_titles)
|
||||
self.set_statement_data(df)
|
||||
|
||||
def _handle_create_click(self):
|
||||
self.add_categories(['asdf'])
|
||||
|
||||
@ -190,6 +246,15 @@ class MainWindow(QMainWindow):
|
||||
def _handle_apply_click(self):
|
||||
pass
|
||||
|
||||
def _handle_save(self):
|
||||
filename, _ = QFileDialog.getSaveFileName(self, 'Save File')
|
||||
|
||||
if filename == '':
|
||||
return
|
||||
|
||||
df = self.get_statement_data()
|
||||
df.to_csv(filename, index=False)
|
||||
|
||||
|
||||
def show_main_window(categories: typing.Sequence[str] = None,
|
||||
df: pd.DataFrame = None):
|
||||
@ -202,29 +267,18 @@ def show_main_window(categories: typing.Sequence[str] = None,
|
||||
if df is not None:
|
||||
window.set_statement_data(df)
|
||||
|
||||
window.add_warning_text("The column 't' does not exist. Please rename the"
|
||||
" column containing the dates of the transactions"
|
||||
" to 't'.")
|
||||
window.add_warning_text("The column 'balance' does not exist. Please"
|
||||
" rename the column containing the balance after"
|
||||
" each transaction to 'balance'")
|
||||
window.add_warning_text("The column 'value' does not exist. Please rename"
|
||||
" the column containing the values of the"
|
||||
" transactions to 'value'.")
|
||||
|
||||
window.show()
|
||||
app.exec()
|
||||
|
||||
|
||||
def main():
|
||||
from banking_breakdown.statement_parser import get_stripped_statement
|
||||
import os
|
||||
os.chdir("../")
|
||||
|
||||
categories = ["Food", "Rent & Utilities", "Hobbies", "Education",
|
||||
"Transportation", "Social Life", "Other"]
|
||||
|
||||
df = pd.read_csv("res/bank_statement_2023.csv", delimiter=';')
|
||||
df = pd.read_csv("res/bank_statement_2023_categorized_renamed.csv")
|
||||
|
||||
show_main_window(categories, df)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user