diff --git a/gui/dialogs/platform_rename.ui b/gui/dialogs/platform_rename.ui new file mode 100644 index 0000000..b18d19a --- /dev/null +++ b/gui/dialogs/platform_rename.ui @@ -0,0 +1,55 @@ + + + PlatformRename + + + + 0 + 0 + 300 + 90 + + + + Rename Platform + + + + + 221 + 62 + 75 + 24 + + + + OK + + + + + + 60 + 10 + 191 + 20 + + + + Enter a new name for the platform + + + + + + 8 + 34 + 287 + 22 + + + + + + + diff --git a/gui/main_gui.ui b/gui/main_gui.ui index 8b5f2de..f618c7f 100644 --- a/gui/main_gui.ui +++ b/gui/main_gui.ui @@ -220,13 +220,13 @@ File - + - + - Edit Platforms + Platform List diff --git a/gui/platform_list.ui b/gui/platform_list.ui new file mode 100644 index 0000000..fe6679a --- /dev/null +++ b/gui/platform_list.ui @@ -0,0 +1,124 @@ + + + PlatformList + + + + 0 + 0 + 264 + 473 + + + + Platform List + + + + + 10 + 34 + 243 + 371 + + + + + 11 + + + + + + + 11 + 413 + 121 + 24 + + + + + 10 + + + + Add platform + + + + + + 135 + 413 + 121 + 24 + + + + + 10 + + + + Remove platform + + + + + + 136 + 442 + 121 + 24 + + + + + 10 + + + + Save + + + + + + 11 + 442 + 121 + 24 + + + + + 10 + + + + Edit platform + + + + + + 12 + 8 + 231 + 20 + + + + + 11 + + + + Platform enabled? + + + + + + diff --git a/src/db_handler.py b/src/db_handler.py new file mode 100644 index 0000000..e8cd137 --- /dev/null +++ b/src/db_handler.py @@ -0,0 +1,106 @@ +import os +import sqlite3 + + +class DBHandler: + def __init__(self): + def create_tables(): + self.cur.execute(""" + CREATE TABLE "tblPlatforms" ( + "PlatformID" INTEGER NOT NULL UNIQUE, + "PlatformName" TEXT NOT NULL, + PRIMARY KEY("PlatformID") + ) + """) + + self.cur.execute(""" + CREATE TABLE "tblFlatPlatFees" ( + "PlatformID" INTEGER NOT NULL, + "SharePlatFee" REAL NOT NULL, + "SharePlatMaxFee" REAL, + PRIMARY KEY("PlatformID"), + FOREIGN KEY("PlatformID") REFERENCES "tblPlatforms"("PlatformID") + ) + """) + + self.cur.execute(""" + CREATE TABLE "tblFlatDealFees" ( + "PlatformID" INTEGER NOT NULL, + "FundDealFee" REAL, + "ShareDealFee" REAL NOT NULL, + "ShareDealReduceTrades" REAL, + "ShareDealReduceAmount" REAL, + PRIMARY KEY("PlatformID"), + FOREIGN KEY("PlatformID") REFERENCES "tblPlatforms"("PlatformID") + ) + """) + + self.cur.execute(""" + CREATE TABLE "tblFundPlatFee" ( + "PlatformID" INTEGER NOT NULL, + "Band" REAL NOT NULL, + "Fee" REAL NOT NULL, + PRIMARY KEY("PlatformID","Band","Fee"), + FOREIGN KEY("PlatformID") REFERENCES "tblPlatforms"("PlatformID") + ) + """) + + self.cur.execute(""" + CREATE TABLE "tblUserDetails" ( + "PensionValue" REAL, + "SliderValue" INTEGER, + "ShareTrades" INTEGER, + "FundTrades" INTEGER + ) + """) + + if not os.path.exists("SIPPCompare.db"): + db_exists = False + else: + db_exists = True + + self.conn = sqlite3.connect("SIPPCompare.db") + self.cur = self.conn.cursor() + if not db_exists: + create_tables() + + def retrieve_plat_list(self) -> list: + res = self.cur.execute("SELECT PlatformName FROM tblPlatforms") + res_list = res.fetchall() + plat_name_list = [] + for platform in res_list: + plat_name_list.append(platform[0]) + + return plat_name_list + + def write_user_details(self, pension_val: float, slider_val: int, share_trades: int, fund_trades: int): + user_details_data = (pension_val, slider_val, share_trades, fund_trades) + + res = self.cur.execute("SELECT EXISTS(SELECT 1 FROM tblUserDetails)").fetchone() + if res[0] == 0: + self.cur.execute("INSERT INTO tblUserDetails VALUES (?, ?, ?, ?)", user_details_data) + else: + self.cur.execute(""" + UPDATE tblUserDetails SET + PensionValue = ?, + SliderValue = ?, + ShareTrades = ?, + FundTrades = ? + """, user_details_data) + self.conn.commit() + + def retrieve_user_details(self) -> dict: + res = self.cur.execute("SELECT EXISTS(SELECT 1 FROM tblUserDetails)").fetchone() + if res[0] == 0: + return {"NO_RECORD": None} + + res = self.cur.execute("SELECT * FROM tblUserDetails") + res_tuple = res.fetchone() + user_details_dict = { + "pension_val": res_tuple[0], + "slider_val": res_tuple[1], + "share_trades": res_tuple[2], + "fund_trades": res_tuple[3] + } + + return user_details_dict diff --git a/src/main_window.py b/src/main_window.py index d56484a..dc12513 100644 --- a/src/main_window.py +++ b/src/main_window.py @@ -3,7 +3,9 @@ from PyQt6.QtWidgets import QMainWindow, QWidget from PyQt6 import uic import output_window +import platform_list import resource_finder +import db_handler class SIPPCompare(QMainWindow): @@ -33,13 +35,16 @@ class SIPPCompare(QMainWindow): self.share_deal_fees = 0.0 # Create window objects + self.db = db_handler.DBHandler() self.platform_win = plat_edit_win + self.platform_list_win = platform_list.PlatformList(self.db) self.output_win = output_window.OutputWindow() # Handle events self.calc_but.clicked.connect(self.calculate_fees) # Menu bar entry (File -> Edit Platforms) - self.actionEdit_Platforms.triggered.connect(self.show_platform_edit) + #self.actionEdit_Platforms.triggered.connect(self.show_platform_edit) + self.actionList_Platforms.triggered.connect(self.show_platform_list) # Update percentage mix label when slider moved self.mix_slider.valueChanged.connect(self.update_slider_lab) self.value_input.valueChanged.connect(self.check_valid) @@ -50,6 +55,15 @@ class SIPPCompare(QMainWindow): self.share_trades_combo.setValidator(QIntValidator(0, 999)) self.fund_trades_combo.setValidator(QIntValidator(0, 99)) + # Restore last session + prev_session_data = self.db.retrieve_user_details() + if "NO_RECORD" not in prev_session_data: + self.value_input.setValue(prev_session_data["pension_val"]) + self.mix_slider.setValue(prev_session_data["slider_val"]) + self.share_trades_combo.setCurrentText(str(prev_session_data["share_trades"])) + self.fund_trades_combo.setCurrentText(str(prev_session_data["fund_trades"])) + self.calc_but.setFocus() + # Display slider position as mix between two nums (funds/shares) def update_slider_lab(self): slider_val = self.mix_slider.value() @@ -100,13 +114,18 @@ class SIPPCompare(QMainWindow): # Calculate fees def calculate_fees(self): self.init_variables() + # Set to zero each time to avoid persistence self.fund_plat_fees = 0 + + # Get user input value_num = float(self.value_input.value()) - # Funds/shares mix slider_val: int = self.mix_slider.value() - funds_value = (slider_val / 100) * value_num fund_trades_num = int(self.fund_trades_combo.currentText()) + share_trades_num = int(self.share_trades_combo.currentText()) + + # Funds/shares mix + funds_value = (slider_val / 100) * value_num if self.fund_deal_fee is not None: self.fund_deal_fees = fund_trades_num * self.fund_deal_fee @@ -130,13 +149,13 @@ class SIPPCompare(QMainWindow): else: self.share_plat_fees = self.share_plat_fee * shares_value - share_trades_num = int(self.share_trades_combo.currentText()) if self.share_deal_reduce_trades is not None: if (share_trades_num / 12) >= self.share_deal_reduce_trades: self.share_deal_fees = self.share_deal_reduce_amount * share_trades_num else: self.share_deal_fees = self.share_deal_fee * share_trades_num + self.db.write_user_details(value_num, slider_val, share_trades_num, fund_trades_num) self.show_output_win() # Show the output window - this func is called from calculate_fee() @@ -151,3 +170,6 @@ class SIPPCompare(QMainWindow): # Show the platform editor window (currently run-time only) def show_platform_edit(self): self.platform_win.show() + + def show_platform_list(self): + self.platform_list_win.show() diff --git a/src/platform_edit.py b/src/platform_edit.py index 663d8f8..492a9fb 100644 --- a/src/platform_edit.py +++ b/src/platform_edit.py @@ -93,7 +93,7 @@ class PlatformEdit(QWidget): QRegularExpressionValidator(QRegularExpression("\\w*")) ) - def create_plat_fee_struct(self): + def create_plat_fee_struct(self) -> list: plat_fee_struct = [[0], [0]] plat_fee_struct[0].append(self.first_tier_box.value()) plat_fee_struct[1].append(self.first_tier_fee_box.value()) diff --git a/src/platform_list.py b/src/platform_list.py new file mode 100644 index 0000000..91cccc9 --- /dev/null +++ b/src/platform_list.py @@ -0,0 +1,44 @@ +from PyQt6.QtWidgets import QWidget, QListWidgetItem +from PyQt6.QtGui import QIcon, QRegularExpressionValidator +from PyQt6.QtCore import QRegularExpression +from PyQt6 import uic + +import resource_finder + + +class PlatformRename(QWidget): + def __init__(self): + super().__init__() + # Import Qt Designer UI XML file + uic.loadUi(resource_finder.get_res_path("gui/dialogs/platform_rename.ui"), self) + self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico"))) + + # Set validators + # Regex accepts any characters that match [a-Z], [0-9] or _ + self.rename_plat_box.setValidator( + QRegularExpressionValidator(QRegularExpression("\\w*")) + ) + + +class PlatformList(QWidget): + def __init__(self, db): + super().__init__() + # Import Qt Designer UI XML file + uic.loadUi(resource_finder.get_res_path("gui/platform_list.ui"), self) + self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico"))) + + self.plat_list_dialog = PlatformRename() + self.db = db + self.plat_name_list = self.db.retrieve_plat_list() + print(self.plat_name_list) + + for platform in self.plat_name_list: + item = QListWidgetItem() + item.setText(platform) + self.platListWidget.addItem(item) + + # Handle events + self.add_plat_but.clicked.connect(self.add_platform) + + def add_platform(self): + self.plat_list_dialog.show() diff --git a/src/resource_finder.py b/src/resource_finder.py index d0e006a..f2b33e2 100644 --- a/src/resource_finder.py +++ b/src/resource_finder.py @@ -10,4 +10,4 @@ def get_res_path(relative_path): except AttributeError: base_path = os.path.abspath(".") - return os.path.join(base_path, relative_path) \ No newline at end of file + return os.path.join(base_path, relative_path)