Compare commits

..

3 Commits

Author SHA1 Message Date
fb3a54d430 Add warning labels 2024-01-03 18:47:32 +01:00
37bc515712 Change the way categories are added 2024-01-03 16:09:57 +01:00
a31f754cfd Update README.md 2024-01-03 15:53:26 +01:00
7 changed files with 215 additions and 117 deletions

View File

@ -1,3 +1,22 @@
# 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:
df = get_stripped_statement(args.i)
ui.show_main_window("res/main_window.ui", df=df)
ui.show_main_window(df=df)
def report_func(args):

View File

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

View File

@ -1,11 +1,13 @@
import typing
from functools import partial
import pandas as pd
from PyQt6.QtWidgets import QMainWindow, QTableView
from PyQt6.QtGui import QPixmap
from PyQt6.QtWidgets import QMainWindow, QTableView, QHBoxLayout, QLabel
from PyQt6 import uic, QtGui, QtCore
from PyQt6.QtCore import Qt
from banking_breakdown.ui.generated import Ui_MainWindow
from banking_breakdown.ui.main_window import Ui_MainWindow
class PandasModel(QtCore.QAbstractTableModel):
@ -61,27 +63,59 @@ class PandasModel(QtCore.QAbstractTableModel):
class GeneratedWindowWrapper(QMainWindow):
def __init__(self, ui_file: str, categories: typing.Sequence):
def __init__(self):
super(GeneratedWindowWrapper, self).__init__()
self._window = Ui_MainWindow()
self._window.setupUi(self)
# Set up window
# Set window behavior
self._window.statementTableView.setSelectionBehavior(
QTableView.SelectionBehavior.SelectRows)
self._set_categories(categories)
# Populate categories
def _set_categories(self, categories: typing.Sequence[str]):
model = QtGui.QStandardItemModel()
self._window.categoryListView.setModel(model)
self._category_model = QtGui.QStandardItemModel()
self._window.categoryListView.setModel(self._category_model)
# Onclick handlers
self._window.createCategoryButton.clicked.connect(
self._handle_create_click)
def add_categories(self, categories: typing.Sequence[str]):
for category in categories:
item = QtGui.QStandardItem(category)
model.appendRow(item)
self._category_model.appendRow(item)
def set_statement_data(self, df: pd.DataFrame):
model = PandasModel(df)
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

@ -15,7 +15,20 @@ class Ui_MainWindow(object):
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.centralwidget)
self.verticalLayout_5 = QtWidgets.QVBoxLayout(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.groupBox = QtWidgets.QGroupBox(parent=self.centralwidget)
self.groupBox.setObjectName("groupBox")
@ -24,6 +37,7 @@ class Ui_MainWindow(object):
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.createCategoryButton = QtWidgets.QPushButton(parent=self.groupBox)
self.createCategoryButton.setStatusTip("")
self.createCategoryButton.setObjectName("createCategoryButton")
self.horizontalLayout.addWidget(self.createCategoryButton)
self.deleteCategoryButton = QtWidgets.QPushButton(parent=self.groupBox)
@ -55,20 +69,15 @@ class Ui_MainWindow(object):
self.verticalLayout.addWidget(self.statementTableView)
self.horizontalLayout_3.addWidget(self.groupBox_2)
self.horizontalLayout_3.setStretch(1, 1)
self.verticalLayout_5.addLayout(self.horizontalLayout_3)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23))
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(parent=self.menubar)
self.menuFile.setObjectName("menuFile")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
self.statusbar.setObjectName("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)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
@ -77,10 +86,10 @@ class Ui_MainWindow(object):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.groupBox.setTitle(_translate("MainWindow", "Categories"))
self.createCategoryButton.setToolTip(_translate("MainWindow", "Create new category"))
self.createCategoryButton.setText(_translate("MainWindow", "Create"))
self.deleteCategoryButton.setToolTip(_translate("MainWindow", "Delete selected category"))
self.deleteCategoryButton.setText(_translate("MainWindow", "Delete"))
self.applyCategoryButton.setToolTip(_translate("MainWindow", "Apply selected category to selected transactions"))
self.applyCategoryButton.setText(_translate("MainWindow", "Apply"))
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

@ -14,86 +14,127 @@
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,1">
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Categories</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="createCategoryButton">
<property name="text">
<string>Create</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="deleteCategoryButton">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QListView" name="categoryListView">
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QWidget" name="warningWidget" native="true">
<property name="maximumSize">
<size>
<width>600</width>
<height>16777215</height>
</size>
</property>
<layout class="QVBoxLayout" name="warningLayout">
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>9</number>
</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>
</widget>
</item>
</layout>
</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>
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,1">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Categories</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="createCategoryButton">
<property name="toolTip">
<string>Create new category</string>
</property>
<property name="statusTip">
<string/>
</property>
<property name="text">
<string>Create</string>
</property>
</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>
</layout>
</widget>
@ -106,23 +147,8 @@
<height>23</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionSave"/>
</widget>
<addaction name="menuFile"/>
</widget>
<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>
<resources/>
<connections/>

BIN
res/warning.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B