Compare commits

..

No commits in common. "fb3a54d4304f26c0e36bdafd9c78ab46de9c3bc0" and "90024635ebb97dcf8013e510e8d741827b2b1345" have entirely different histories.

7 changed files with 118 additions and 216 deletions

View File

@ -1,22 +1,3 @@
# banking-breakdown # banking-breakdown
Visualize bank statements. Visualize bank statements.
## Usage
1. Assign categories to bank statement entries:
```bash
$ python -m banking_breakdown categorize -i [bank_statement.csv] -f [regex_file]
```
2. Generate report from categorized data:
```bash
$ python -m banking_breakdown report -i [categorized.csv]
```
## Other
### Generate GUI from `*.ui` file
```bash
$ pyuic6 res/main_window.ui -o banking_breakdown/ui/main_window.py
```

View File

@ -12,7 +12,7 @@ def categorize_func(args):
if args.i is not None: if args.i is not None:
df = get_stripped_statement(args.i) df = get_stripped_statement(args.i)
ui.show_main_window(df=df) ui.show_main_window("res/main_window.ui", df=df)
def report_func(args): def report_func(args):

View File

@ -7,27 +7,17 @@ from PyQt6.QtWidgets import QApplication
from banking_breakdown.ui.generated_wrapper import GeneratedWindowWrapper from banking_breakdown.ui.generated_wrapper import GeneratedWindowWrapper
def show_main_window(categories: typing.Sequence[str] = None, def show_main_window(ui_file, categories: typing.Sequence[str] = None,
df: pd.DataFrame = None): df: pd.DataFrame = None):
app = QApplication(sys.argv) if categories is None:
window = GeneratedWindowWrapper() categories = []
if categories is not None: app = QApplication(sys.argv)
window.add_categories(categories) window = GeneratedWindowWrapper(ui_file, categories)
if df is not None: if df is not None:
window.set_statement_data(df) 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() window.show()
app.exec() app.exec()
@ -40,7 +30,7 @@ def main():
df = get_stripped_statement("../../res/banking_statement_2023.csv") df = get_stripped_statement("../../res/banking_statement_2023.csv")
show_main_window(categories, df) show_main_window("../../res/main_window.ui", categories, df)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -15,20 +15,7 @@ class Ui_MainWindow(object):
MainWindow.resize(800, 600) MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(parent=MainWindow) self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
self.centralwidget.setObjectName("centralwidget") self.centralwidget.setObjectName("centralwidget")
self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.centralwidget) self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.centralwidget)
self.verticalLayout_5.setSpacing(0)
self.verticalLayout_5.setObjectName("verticalLayout_5")
self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.warningWidget = QtWidgets.QWidget(parent=self.centralwidget)
self.warningWidget.setMaximumSize(QtCore.QSize(600, 16777215))
self.warningWidget.setObjectName("warningWidget")
self.warningLayout = QtWidgets.QVBoxLayout(self.warningWidget)
self.warningLayout.setContentsMargins(-1, 0, -1, 9)
self.warningLayout.setObjectName("warningLayout")
self.horizontalLayout_5.addWidget(self.warningWidget)
self.verticalLayout_5.addLayout(self.horizontalLayout_5)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.groupBox = QtWidgets.QGroupBox(parent=self.centralwidget) self.groupBox = QtWidgets.QGroupBox(parent=self.centralwidget)
self.groupBox.setObjectName("groupBox") self.groupBox.setObjectName("groupBox")
@ -37,7 +24,6 @@ class Ui_MainWindow(object):
self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout") self.horizontalLayout.setObjectName("horizontalLayout")
self.createCategoryButton = QtWidgets.QPushButton(parent=self.groupBox) self.createCategoryButton = QtWidgets.QPushButton(parent=self.groupBox)
self.createCategoryButton.setStatusTip("")
self.createCategoryButton.setObjectName("createCategoryButton") self.createCategoryButton.setObjectName("createCategoryButton")
self.horizontalLayout.addWidget(self.createCategoryButton) self.horizontalLayout.addWidget(self.createCategoryButton)
self.deleteCategoryButton = QtWidgets.QPushButton(parent=self.groupBox) self.deleteCategoryButton = QtWidgets.QPushButton(parent=self.groupBox)
@ -69,15 +55,20 @@ class Ui_MainWindow(object):
self.verticalLayout.addWidget(self.statementTableView) self.verticalLayout.addWidget(self.statementTableView)
self.horizontalLayout_3.addWidget(self.groupBox_2) self.horizontalLayout_3.addWidget(self.groupBox_2)
self.horizontalLayout_3.setStretch(1, 1) self.horizontalLayout_3.setStretch(1, 1)
self.verticalLayout_5.addLayout(self.horizontalLayout_3)
MainWindow.setCentralWidget(self.centralwidget) MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(parent=MainWindow) self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23)) self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23))
self.menubar.setObjectName("menubar") self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(parent=self.menubar)
self.menuFile.setObjectName("menuFile")
MainWindow.setMenuBar(self.menubar) MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(parent=MainWindow) self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
self.statusbar.setObjectName("statusbar") self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar) MainWindow.setStatusBar(self.statusbar)
self.actionSave = QtGui.QAction(parent=MainWindow)
self.actionSave.setObjectName("actionSave")
self.menuFile.addAction(self.actionSave)
self.menubar.addAction(self.menuFile.menuAction())
self.retranslateUi(MainWindow) self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow)
@ -86,10 +77,10 @@ class Ui_MainWindow(object):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.groupBox.setTitle(_translate("MainWindow", "Categories")) self.groupBox.setTitle(_translate("MainWindow", "Categories"))
self.createCategoryButton.setToolTip(_translate("MainWindow", "Create new category"))
self.createCategoryButton.setText(_translate("MainWindow", "Create")) self.createCategoryButton.setText(_translate("MainWindow", "Create"))
self.deleteCategoryButton.setToolTip(_translate("MainWindow", "Delete selected category"))
self.deleteCategoryButton.setText(_translate("MainWindow", "Delete")) self.deleteCategoryButton.setText(_translate("MainWindow", "Delete"))
self.applyCategoryButton.setToolTip(_translate("MainWindow", "Apply selected category to selected transactions"))
self.applyCategoryButton.setText(_translate("MainWindow", "Apply")) self.applyCategoryButton.setText(_translate("MainWindow", "Apply"))
self.groupBox_2.setTitle(_translate("MainWindow", "Bank statement")) self.groupBox_2.setTitle(_translate("MainWindow", "Bank statement"))
self.menuFile.setTitle(_translate("MainWindow", "File"))
self.actionSave.setText(_translate("MainWindow", "Save"))
self.actionSave.setShortcut(_translate("MainWindow", "Ctrl+S"))

View File

@ -1,13 +1,11 @@
import typing import typing
from functools import partial
import pandas as pd import pandas as pd
from PyQt6.QtGui import QPixmap from PyQt6.QtWidgets import QMainWindow, QTableView
from PyQt6.QtWidgets import QMainWindow, QTableView, QHBoxLayout, QLabel
from PyQt6 import uic, QtGui, QtCore from PyQt6 import uic, QtGui, QtCore
from PyQt6.QtCore import Qt from PyQt6.QtCore import Qt
from banking_breakdown.ui.main_window import Ui_MainWindow from banking_breakdown.ui.generated import Ui_MainWindow
class PandasModel(QtCore.QAbstractTableModel): class PandasModel(QtCore.QAbstractTableModel):
@ -63,59 +61,27 @@ class PandasModel(QtCore.QAbstractTableModel):
class GeneratedWindowWrapper(QMainWindow): class GeneratedWindowWrapper(QMainWindow):
def __init__(self): def __init__(self, ui_file: str, categories: typing.Sequence):
super(GeneratedWindowWrapper, self).__init__() super(GeneratedWindowWrapper, self).__init__()
self._window = Ui_MainWindow() self._window = Ui_MainWindow()
self._window.setupUi(self) self._window.setupUi(self)
# Set window behavior # Set up window
self._window.statementTableView.setSelectionBehavior( self._window.statementTableView.setSelectionBehavior(
QTableView.SelectionBehavior.SelectRows) QTableView.SelectionBehavior.SelectRows)
# Populate categories self._set_categories(categories)
self._category_model = QtGui.QStandardItemModel() def _set_categories(self, categories: typing.Sequence[str]):
self._window.categoryListView.setModel(self._category_model) model = QtGui.QStandardItemModel()
self._window.categoryListView.setModel(model)
# Onclick handlers
self._window.createCategoryButton.clicked.connect(
self._handle_create_click)
def add_categories(self, categories: typing.Sequence[str]):
for category in categories: for category in categories:
item = QtGui.QStandardItem(category) item = QtGui.QStandardItem(category)
self._category_model.appendRow(item) model.appendRow(item)
def set_statement_data(self, df: pd.DataFrame): def set_statement_data(self, df: pd.DataFrame):
model = PandasModel(df) model = PandasModel(df)
self._window.statementTableView.setModel(model) self._window.statementTableView.setModel(model)
def _handle_create_click(self):
self.add_categories(['asdf'])
def _handle_delete_click(self):
pass
def _handle_apply_click(self):
pass
def add_warning_text(self, text: str):
layout = QHBoxLayout()
warningIcon = QLabel()
pixmap = QPixmap('res/warning.png')
warningIcon.setPixmap(pixmap)
label = QLabel(text)
label.setWordWrap(True)
layout.addWidget(warningIcon)
layout.addWidget(label)
layout.setStretch(0, 0)
layout.setStretch(1, 1)
self._window.warningLayout.addLayout(layout)

View File

@ -14,127 +14,86 @@
<string>MainWindow</string> <string>MainWindow</string>
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_5"> <layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,1">
<property name="spacing">
<number>0</number>
</property>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_5"> <widget class="QGroupBox" name="groupBox">
<item> <property name="title">
<widget class="QWidget" name="warningWidget" native="true"> <string>Categories</string>
<property name="maximumSize"> </property>
<size> <layout class="QVBoxLayout" name="verticalLayout_2">
<width>600</width> <item>
<height>16777215</height> <layout class="QHBoxLayout" name="horizontalLayout">
</size> <item>
</property> <widget class="QPushButton" name="createCategoryButton">
<layout class="QVBoxLayout" name="warningLayout"> <property name="text">
<property name="topMargin"> <string>Create</string>
<number>0</number> </property>
</property> </widget>
<property name="bottomMargin"> </item>
<number>9</number> <item>
</property> <widget class="QPushButton" name="deleteCategoryButton">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </item>
</item> <item>
</layout> <widget class="QListView" name="categoryListView">
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="applyCategoryButton">
<property name="text">
<string>Apply</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,1"> <widget class="QGroupBox" name="groupBox_2">
<item> <property name="title">
<widget class="QGroupBox" name="groupBox"> <string>Bank statement</string>
<property name="title"> </property>
<string>Categories</string> <layout class="QVBoxLayout" name="verticalLayout">
</property> <item>
<layout class="QVBoxLayout" name="verticalLayout_2"> <widget class="QTableView" name="statementTableView">
<item> <property name="sortingEnabled">
<layout class="QHBoxLayout" name="horizontalLayout"> <bool>false</bool>
<item> </property>
<widget class="QPushButton" name="createCategoryButton"> <property name="wordWrap">
<property name="toolTip"> <bool>true</bool>
<string>Create new category</string> </property>
</property> <attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<property name="statusTip"> <bool>false</bool>
<string/> </attribute>
</property> </widget>
<property name="text"> </item>
<string>Create</string> </layout>
</property> </widget>
</widget>
</item>
<item>
<widget class="QPushButton" name="deleteCategoryButton">
<property name="toolTip">
<string>Delete selected category</string>
</property>
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QListView" name="categoryListView">
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="applyCategoryButton">
<property name="toolTip">
<string>Apply selected category to selected transactions</string>
</property>
<property name="text">
<string>Apply</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Bank statement</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTableView" name="statementTableView">
<property name="sortingEnabled">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>
@ -147,8 +106,23 @@
<height>23</height> <height>23</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionSave"/>
</widget>
<addaction name="menuFile"/>
</widget> </widget>
<widget class="QStatusBar" name="statusbar"/> <widget class="QStatusBar" name="statusbar"/>
<action name="actionSave">
<property name="text">
<string>Save</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
</action>
</widget> </widget>
<resources/> <resources/>
<connections/> <connections/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 634 B