Compare commits

65 Commits

Author SHA1 Message Date
005f189c76 removed database binary from source tree 2025-04-29 14:22:06 +01:00
c8d7594af6 update nuitka product version 2025-04-29 14:17:30 +01:00
756efdf364 only update val_above_lab when input is valid 2025-04-28 16:41:52 +01:00
2af0ecfd75 fix logic for platform list 2025-04-28 15:26:28 +01:00
8ddc40baf6 refresh graph results properly 2025-04-28 14:59:22 +01:00
8f6f3dab00 add flowcharts 2025-04-28 14:56:44 +01:00
4b8bdfbeea update PyCharm indexing exclusions 2025-04-28 12:55:14 +01:00
09f8b7bba8 remove unused code 2025-04-28 12:54:07 +01:00
abbcbe41d0 final small fixes to UI files 2025-04-27 20:23:48 +01:00
75e26e229a finalise nuitka build options (for windows) 2025-04-27 16:22:06 +01:00
5eb0c3c4a3 fix mistakenly disabled checkbox + fix win size 2025-04-27 15:31:54 +01:00
78323c2ad8 switch from un-used trade QComboBox to FastEditQSpinBox 2025-04-27 15:29:04 +01:00
2422395759 properly implement resource finding for nuitka 2025-04-27 15:28:10 +01:00
7cb41652b4 switch from pyinstaller to nuitka for distribution 2025-04-27 11:56:19 +01:00
ae01d912e1 additional comments 2025-04-27 11:55:49 +01:00
9a8b0045fa defer import of matplotlib to spped up launch 2025-04-27 11:55:27 +01:00
9e79d986e4 add additional input validation 2025-04-27 11:54:09 +01:00
71d9590205 fix weird spacing bug 2025-04-27 11:51:05 +01:00
3b6b75ee48 updated .gitignore 2025-04-26 20:47:52 +01:00
ca8f4409c1 Merge pull request #3 from RolandWH/db_testing
implement sqlite database and graphed results
2025-04-26 16:45:44 +01:00
ba204becc9 code formatting, clean up & comments 2025-04-26 16:40:36 +01:00
42afd128e5 add error handling for file saving 2025-04-26 16:39:56 +01:00
629ea6833d set min and max sizes for all windows 2025-04-26 16:39:05 +01:00
e2ca298919 make results window active and raise it when calculating 2025-04-26 16:38:37 +01:00
593dec96d1 add enable/disable platform functionality 2025-04-26 16:37:23 +01:00
fa74e9e8da fix critical calculation bug 2025-04-26 14:17:17 +01:00
16802648bc implement deleting platforms and csv saving 2025-04-26 12:57:49 +01:00
c151b19a3c update pyinstaller spec 2025-04-25 17:36:13 +01:00
f2a0735972 add time slider and saving functionality to output 2025-04-25 17:35:54 +01:00
348b027ede remove redundant text-based output 2025-04-25 11:09:55 +01:00
54b7bdd700 clean up files 2025-04-25 11:05:00 +01:00
929c3719c0 complete db & graph implementation (except for delete) 2025-04-24 22:55:26 +01:00
d8c4b5d64d add data_struct.py file 2025-04-22 17:12:07 +01:00
0fa6db3bc0 read platform details from database (WIP) 2025-04-22 13:37:43 +01:00
df436fa2b2 begin implementation of sqlite database 2025-04-14 16:25:48 +01:00
1234008813 expand size of output window for bigger font 2025-04-13 12:59:14 +01:00
662a3a5e9c fix bug with debug autofill mode 2025-04-13 12:58:38 +01:00
e379b4a7e2 properly add icon - and fix main_gui size 2025-04-10 14:56:16 +01:00
5f9017b625 add application icon 2025-04-10 14:09:24 +01:00
ace96dfc3a make code compatible with PyInstaller 2025-03-18 19:25:11 +00:00
cc5686d3d7 Merge PR #2 from RolandWH/testing (user defined fund_plat_fee)
Implement user defined fund_plat_fee using a table interface where each row is a band. There is full input validation using a combination of the FastEditQDoubleSpinBox widget and custom code that checks the numbers in each row are sane (each band less than previous band, fee is a non-zero number).
2025-03-18 13:39:02 +00:00
e0f49a1c14 add dynamic 'on the value above' label 2025-03-18 13:35:55 +00:00
255fed7ce7 fix inout validation on first row 2025-03-18 13:06:45 +00:00
a99a6263a8 ensure the fee for a band is non-zero 2025-03-18 13:00:02 +00:00
0338bc0bb7 fix input validation when removing rows 2025-03-18 12:47:31 +00:00
93f69cc0da add input validation to fund_plat_fee tiers 2025-03-18 12:17:19 +00:00
ead59079b3 make font sizes consistent 2025-03-18 10:44:41 +00:00
df7baa2df2 increase font size for better readability 2025-03-17 21:57:30 +00:00
d1cf30422c correct indentation in fee calculation func 2025-03-17 21:56:39 +00:00
f395f155d0 make fund deal fee checkbox optional 2025-03-17 21:55:13 +00:00
32bc3add9b make fund_plat_fee user defined (no validation) 2025-03-17 15:16:02 +00:00
fca1b384bd make selection logic more readable 2025-02-17 10:18:14 +00:00
6bfeaa6e0d Merge pull request #1 from RolandWH/testing
Implement input validation with optional fields support
2025-02-17 10:08:28 +00:00
679a509101 fix input validation to work with custom widgets 2025-02-17 10:05:38 +00:00
68ac992483 implement custom Q(Double)SpinBox widget for onFocus selection 2025-02-11 21:19:05 +00:00
9ef6048601 do input validation on pension value field 2025-02-11 19:31:13 +00:00
107c388afb add label hint for checkboxes, fix tab order 2025-02-11 19:04:21 +00:00
36a36f5437 implemented optional fields - needs further work 2025-02-11 18:11:54 +00:00
fcbc78b05b opt field input validation complete - functionality still TODO 2025-02-11 15:52:36 +00:00
ecc2ce6d56 continue implementing optional fields - still broken 2025-02-11 13:42:35 +00:00
9143afc176 begin adding optional fields 2025-02-11 10:57:19 +00:00
b62518875f fix autofill debug flag 2025-02-10 23:28:11 +00:00
142906b39c open results file with utf-8 encoding 2025-02-10 23:27:42 +00:00
1f132da9b6 add input validation on combo boxes 2025-02-10 23:27:22 +00:00
59f05a3f38 edit tab order, window sizes 2025-02-10 23:26:27 +00:00
28 changed files with 2760 additions and 388 deletions

8
.gitignore vendored
View File

@ -1,2 +1,10 @@
/designer.exe - Shortcut.lnk /designer.exe - Shortcut.lnk
.idea/workspace.xml .idea/workspace.xml
.idea/discord.xml
.idea/encodings.xml
*build/
*dist/
output/
src/*/__pycache__/
src/__pycache__/
*.db

2
.idea/SIPPCompare.iml generated
View File

@ -3,6 +3,8 @@
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" /> <excludeFolder url="file://$MODULE_DIR$/.venv" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/output" />
</content> </content>
<orderEntry type="jdk" jdkName="Python 3.13 (SIPPCompare)" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="Python 3.13 (SIPPCompare)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />

138
flowcharts/main_win.drawio Normal file
View File

@ -0,0 +1,138 @@
<mxfile host="Electron" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/26.2.2 Chrome/134.0.6998.178 Electron/35.1.2 Safari/537.36" version="26.2.2">
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
<mxGraphModel dx="2068" dy="1219" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="GkLdqIe8JMVAQNq9l6uQ-0" target="GkLdqIe8JMVAQNq9l6uQ-4" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="144" y="120" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-0" value="Import user details from database" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.start_1;whiteSpace=wrap;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="94" y="10" width="100" height="60" as="geometry" />
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-7" value="Yes&lt;div&gt;&lt;br&gt;&lt;/div&gt;" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="GkLdqIe8JMVAQNq9l6uQ-4" target="GkLdqIe8JMVAQNq9l6uQ-14" edge="1">
<mxGeometry x="0.0098" y="15" relative="1" as="geometry">
<mxPoint x="139" y="230" as="sourcePoint" />
<mxPoint x="144" y="270" as="targetPoint" />
<mxPoint x="1" y="-1" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-4" value="User input?" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="94" y="120" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="GkLdqIe8JMVAQNq9l6uQ-8" target="GkLdqIe8JMVAQNq9l6uQ-4" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="330" y="320" as="targetPoint" />
<Array as="points">
<mxPoint x="250" y="480" />
<mxPoint x="250" y="170" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-10" value="No" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="GkLdqIe8JMVAQNq9l6uQ-9" vertex="1" connectable="0">
<mxGeometry x="0.0214" y="1" relative="1" as="geometry">
<mxPoint x="1" y="120" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-12" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="GkLdqIe8JMVAQNq9l6uQ-8" target="GkLdqIe8JMVAQNq9l6uQ-11" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-8" value="User input&amp;nbsp;&lt;div&gt;valid?&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="94" y="430" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-11" value="Enable calculate button" style="whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="84" y="584" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-15" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="GkLdqIe8JMVAQNq9l6uQ-14" target="GkLdqIe8JMVAQNq9l6uQ-20" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="340" y="320" as="targetPoint" />
<Array as="points">
<mxPoint x="300" y="320" />
<mxPoint x="300" y="40" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-14" value="Calculate&amp;nbsp;&lt;div&gt;button&amp;nbsp;&lt;/div&gt;&lt;div&gt;clicked?&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="94" y="270" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-17" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="GkLdqIe8JMVAQNq9l6uQ-14" target="GkLdqIe8JMVAQNq9l6uQ-8" edge="1">
<mxGeometry x="0.2" y="16" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="GkLdqIe8JMVAQNq9l6uQ-11" target="GkLdqIe8JMVAQNq9l6uQ-4" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="30" y="614" />
<mxPoint x="30" y="170" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-23" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="GkLdqIe8JMVAQNq9l6uQ-20" target="GkLdqIe8JMVAQNq9l6uQ-21" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-20" value="Calculate fund platform fees" style="whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="340" y="10" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="GkLdqIe8JMVAQNq9l6uQ-21" target="GkLdqIe8JMVAQNq9l6uQ-24" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-21" value="Calculate fund dealing fees" style="whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="340" y="120" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-26" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="GkLdqIe8JMVAQNq9l6uQ-24" target="GkLdqIe8JMVAQNq9l6uQ-27" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="400" y="330" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-24" value="Calculate share platform fees" style="whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="340" y="230" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-29" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="GkLdqIe8JMVAQNq9l6uQ-27" target="CeQ2sipdlTVvld8hq7BN-4" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-27" value="Calculate share dealing fees" style="whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="340" y="340" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="CeQ2sipdlTVvld8hq7BN-1" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="GkLdqIe8JMVAQNq9l6uQ-28" target="CeQ2sipdlTVvld8hq7BN-0">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="GkLdqIe8JMVAQNq9l6uQ-28" value="Send results list to output window (display results)" style="whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="520" y="450" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="CeQ2sipdlTVvld8hq7BN-0" value="Save user details to database" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="520" y="584" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="CeQ2sipdlTVvld8hq7BN-7" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="CeQ2sipdlTVvld8hq7BN-4" target="CeQ2sipdlTVvld8hq7BN-8">
<mxGeometry relative="1" as="geometry">
<mxPoint x="400" y="556" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="CeQ2sipdlTVvld8hq7BN-4" value="Add results for this platform to list" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="340" y="450" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="CeQ2sipdlTVvld8hq7BN-9" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="CeQ2sipdlTVvld8hq7BN-8" target="GkLdqIe8JMVAQNq9l6uQ-28">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="CeQ2sipdlTVvld8hq7BN-8" value="Last platform processed?" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="350" y="564" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="CeQ2sipdlTVvld8hq7BN-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.89;entryY=0.616;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="CeQ2sipdlTVvld8hq7BN-0" target="GkLdqIe8JMVAQNq9l6uQ-4">
<mxGeometry relative="1" as="geometry">
<mxPoint x="154" y="130" as="targetPoint" />
<mxPoint x="154" y="80" as="sourcePoint" />
<Array as="points">
<mxPoint x="580" y="680" />
<mxPoint x="220" y="680" />
<mxPoint x="220" y="220" />
<mxPoint x="183" y="220" />
</Array>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

@ -0,0 +1,79 @@
<mxfile host="Electron" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/26.2.2 Chrome/134.0.6998.178 Electron/35.1.2 Safari/537.36" version="26.2.2">
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
<mxGraphModel dx="1426" dy="841" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-5" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="D6qvsPv5HUMZqZDP2d3Z-0" target="D6qvsPv5HUMZqZDP2d3Z-22">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-0" value="Print results to screen" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.start_1;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="210" y="10" width="100" height="60" as="geometry" />
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-7" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="D6qvsPv5HUMZqZDP2d3Z-4" target="D6qvsPv5HUMZqZDP2d3Z-9">
<mxGeometry relative="1" as="geometry">
<mxPoint x="310" y="170.0000000000001" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-16" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="D6qvsPv5HUMZqZDP2d3Z-4" target="D6qvsPv5HUMZqZDP2d3Z-17">
<mxGeometry relative="1" as="geometry">
<mxPoint x="360" y="175.0000000000001" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-4" value="Save&lt;div&gt;button&amp;nbsp;&lt;/div&gt;&lt;div&gt;clicked?&lt;/div&gt;" style="rhombus;whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="210" y="230" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="D6qvsPv5HUMZqZDP2d3Z-13" target="D6qvsPv5HUMZqZDP2d3Z-22">
<mxGeometry relative="1" as="geometry">
<mxPoint x="190" y="90" as="sourcePoint" />
<mxPoint x="150" y="150" as="targetPoint" />
<Array as="points">
<mxPoint x="90" y="150" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-12" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="D6qvsPv5HUMZqZDP2d3Z-9" target="D6qvsPv5HUMZqZDP2d3Z-11">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-21" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="D6qvsPv5HUMZqZDP2d3Z-9" target="D6qvsPv5HUMZqZDP2d3Z-13">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-9" value="Output&amp;nbsp;&lt;div&gt;directory&amp;nbsp;&lt;/div&gt;&lt;div&gt;exists?&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="210" y="380" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-14" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="D6qvsPv5HUMZqZDP2d3Z-11" target="D6qvsPv5HUMZqZDP2d3Z-13">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-11" value="Create output directory" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="200" y="530" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-13" value="Save results to text file" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="30" y="250" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-19" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="D6qvsPv5HUMZqZDP2d3Z-17" target="D6qvsPv5HUMZqZDP2d3Z-18">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-17" value="OK button clicked?" style="rhombus;whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="363" y="230" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-18" value="Close results window" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="353" y="390" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-20" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="D6qvsPv5HUMZqZDP2d3Z-17" target="D6qvsPv5HUMZqZDP2d3Z-22">
<mxGeometry x="-0.6728" relative="1" as="geometry">
<Array as="points">
<mxPoint x="414" y="150" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-22" value="Input loop start" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="200" y="120" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="D6qvsPv5HUMZqZDP2d3Z-23" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.502;entryY=0.045;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="D6qvsPv5HUMZqZDP2d3Z-22" target="D6qvsPv5HUMZqZDP2d3Z-4">
<mxGeometry relative="1" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1,242 @@
<mxfile host="Electron" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/26.2.15 Chrome/134.0.6998.205 Electron/35.2.1 Safari/537.36" version="26.2.15">
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
<mxGraphModel dx="2066" dy="1219" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="1654" math="0" shadow="0">
<root>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="RMgAkj6IqEivbKdGbQ9N-0" value="Retrieve details of platform from DB if applicable" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.start_1;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="532" y="10" width="116" height="80" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-2" value="Any field edited?" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="540" y="120" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-0" target="RMgAkj6IqEivbKdGbQ9N-2">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-58" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-4">
<mxGeometry x="-0.3442" y="-10" relative="1" as="geometry">
<mxPoint x="510" y="320" as="targetPoint" />
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-4" value="Any checkbox toggled?" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="540" y="270" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-5" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-2" target="RMgAkj6IqEivbKdGbQ9N-4">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-6" value="Save updated platform details to list" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="528" y="900" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-52" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-7" target="RMgAkj6IqEivbKdGbQ9N-51">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-7" value="Add row clicked?" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="540" y="430" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-61" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.747;exitY=0.74;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-8">
<mxGeometry x="-0.847" relative="1" as="geometry">
<mxPoint x="820" y="930" as="targetPoint" />
<Array as="points">
<mxPoint x="670" y="674" />
<mxPoint x="670" y="930" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-8" value="Remove row clicked?" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="540" y="600" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-13" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-9" target="RMgAkj6IqEivbKdGbQ9N-6">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-9" value="Save button clicked?" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="540" y="750" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-10" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-4" target="RMgAkj6IqEivbKdGbQ9N-7">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-11" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-7" target="RMgAkj6IqEivbKdGbQ9N-8">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-12" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-8" target="RMgAkj6IqEivbKdGbQ9N-9">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-19" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-15" target="RMgAkj6IqEivbKdGbQ9N-18">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-15" value="All&amp;nbsp;&lt;div&gt;required fields non zero?&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="386" y="120" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-17" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-2" target="RMgAkj6IqEivbKdGbQ9N-15">
<mxGeometry x="0.1111" y="-10" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-22" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-18" target="RMgAkj6IqEivbKdGbQ9N-20">
<mxGeometry relative="1" as="geometry">
<mxPoint x="276" y="320" as="targetPoint" />
<Array as="points">
<mxPoint x="276" y="320" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-18" value="Mark main input not valid" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="216" y="140" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-23" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.37;exitY=0.89;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-20" target="RMgAkj6IqEivbKdGbQ9N-18">
<mxGeometry x="-0.4497" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-24" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-20" target="RMgAkj6IqEivbKdGbQ9N-25">
<mxGeometry relative="1" as="geometry">
<mxPoint x="436" y="400" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-20" value="Are&amp;nbsp;&lt;div&gt;all ACTIVE optional fields non-zero?&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="386" y="270" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-21" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-15" target="RMgAkj6IqEivbKdGbQ9N-20">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-27" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-25" target="RMgAkj6IqEivbKdGbQ9N-26">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-25" value="Each row of&lt;div&gt;fund_plat_fee&lt;/div&gt;&lt;div&gt;bigger than&amp;nbsp;&lt;/div&gt;&lt;div&gt;the previous?&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;fontSize=11;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="378.5" y="420" width="115" height="120" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-26" value="Mark fund platform fee input not valid" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="216" y="450" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-30" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-29" target="RMgAkj6IqEivbKdGbQ9N-26">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-29" value="Each&amp;nbsp;&lt;div&gt;fee field in fund_plat fee&lt;div&gt;non zero?&lt;/div&gt;&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;fontSize=11;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="386" y="590" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-31" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-25" target="RMgAkj6IqEivbKdGbQ9N-29">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-35" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-32" target="RMgAkj6IqEivbKdGbQ9N-34">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-32" value="Maximum&amp;nbsp;&lt;div&gt;number of rows&lt;/div&gt;&lt;div&gt;(6) reached?&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;fontSize=11;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="386" y="750" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-33" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-29" target="RMgAkj6IqEivbKdGbQ9N-32">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-34" value="Disale add row button" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="376" y="900" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-39" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-37" target="RMgAkj6IqEivbKdGbQ9N-38">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-66" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.75;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-37" target="RMgAkj6IqEivbKdGbQ9N-50">
<mxGeometry x="-0.7078" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-37" value="Fund&lt;div&gt;platform fee input marked valid?&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;fontSize=11;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="226" y="750" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-43" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-38" target="RMgAkj6IqEivbKdGbQ9N-42">
<mxGeometry relative="1" as="geometry">
<mxPoint x="276.0000000000002" y="1000" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-38" value="Enable add row button" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="216" y="900" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-40" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-32" target="RMgAkj6IqEivbKdGbQ9N-37">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-46" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-42" target="RMgAkj6IqEivbKdGbQ9N-44">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-47" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.198;entryY=0.298;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-42" target="RMgAkj6IqEivbKdGbQ9N-2">
<mxGeometry x="-0.9531" relative="1" as="geometry">
<mxPoint x="536" y="120" as="targetPoint" />
<Array as="points">
<mxPoint x="120" y="1060" />
<mxPoint x="120" y="110" />
<mxPoint x="560" y="110" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-42" value="Is main input marked valid?" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;fontSize=11;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="226" y="1010" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-44" value="Enable save button" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="216" y="1160" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-45" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-34" target="RMgAkj6IqEivbKdGbQ9N-42">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="436" y="1060" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-50" value="Disable save button" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="58" y="900" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-54" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-51" target="RMgAkj6IqEivbKdGbQ9N-53">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-51" value="Add new row of widgets to grid layout and list of widgets" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="690" y="450" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-56" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-53" target="RMgAkj6IqEivbKdGbQ9N-55">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-53" value="Expand grid layout to accommodate new row" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="690" y="560" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-57" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-55">
<mxGeometry relative="1" as="geometry">
<mxPoint x="510" y="170" as="targetPoint" />
<Array as="points">
<mxPoint x="750" y="991" />
<mxPoint x="751" y="991" />
<mxPoint x="751" y="1220" />
<mxPoint x="510" y="1220" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-55" value="Set new rows label to reflect the value of the previous box. Also update max fee label" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="690" y="670" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-60" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-9" target="RMgAkj6IqEivbKdGbQ9N-2">
<mxGeometry x="-0.9279" relative="1" as="geometry">
<Array as="points">
<mxPoint x="880" y="800" />
<mxPoint x="880" y="170" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-64" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-62" target="RMgAkj6IqEivbKdGbQ9N-63">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-62" value="Remove each widget in the last row from the grid layout" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="820" y="900" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-65" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RMgAkj6IqEivbKdGbQ9N-63">
<mxGeometry relative="1" as="geometry">
<mxPoint x="750" y="1120" as="targetPoint" />
<Array as="points">
<mxPoint x="880" y="1095" />
<mxPoint x="881" y="1095" />
<mxPoint x="881" y="1120" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="RMgAkj6IqEivbKdGbQ9N-63" value="Contract the grid layout to fit smaller number of rows" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="820" y="1010" width="120" height="60" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

View File

@ -0,0 +1,176 @@
<mxfile host="Electron" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/26.2.2 Chrome/134.0.6998.178 Electron/35.1.2 Safari/537.36" version="26.2.2">
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
<mxGraphModel dx="753" dy="830" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-0" value="Get list of platforms from&amp;nbsp;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;DB&lt;/span&gt;&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.start_1;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="363" y="50" width="100" height="60" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-10" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-1" target="uiKHp9CyL3H5hJtIc4hJ-9">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-1" value="Add platform clicked?" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="363" y="160" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-0" target="uiKHp9CyL3H5hJtIc4hJ-1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-29" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-3">
<mxGeometry relative="1" as="geometry">
<mxPoint x="510" y="360" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-3" value="Remove&lt;div&gt;platform&lt;/div&gt;&lt;div&gt;clicked?&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="363" y="310" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-4" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-1" target="uiKHp9CyL3H5hJtIc4hJ-3">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-16" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-5" target="uiKHp9CyL3H5hJtIc4hJ-15">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-5" value="Edit platform clicked?" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="363" y="460" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-6" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-3" target="uiKHp9CyL3H5hJtIc4hJ-5">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-27" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-7" target="uiKHp9CyL3H5hJtIc4hJ-26">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-7" value="Save button clicked?" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="363" y="610" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-8" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-5" target="uiKHp9CyL3H5hJtIc4hJ-7">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-9" value="Show name dialog" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="190" y="130" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-13" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-11" target="uiKHp9CyL3H5hJtIc4hJ-14">
<mxGeometry relative="1" as="geometry">
<mxPoint x="249.75862068965523" y="370" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-18" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.247;entryY=0.262;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-11" target="uiKHp9CyL3H5hJtIc4hJ-1">
<mxGeometry x="-0.572" relative="1" as="geometry">
<mxPoint x="350" y="140" as="targetPoint" />
<Array as="points">
<mxPoint x="160" y="290" />
<mxPoint x="160" y="120" />
<mxPoint x="388" y="120" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-11" value="Dialog&lt;div&gt;accepted?&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="200" y="240" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-9" target="uiKHp9CyL3H5hJtIc4hJ-11">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-14" target="uiKHp9CyL3H5hJtIc4hJ-15">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-14" value="Add new platform to list" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="190" y="390" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-15" value="Show platform editor" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="190" y="500" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-25" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-20" target="uiKHp9CyL3H5hJtIc4hJ-24">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-20" value="Platform&lt;div&gt;editor saved?&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="200" y="610" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-15" target="uiKHp9CyL3H5hJtIc4hJ-20">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-23" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-20" target="uiKHp9CyL3H5hJtIc4hJ-1">
<mxGeometry x="-0.9007" relative="1" as="geometry">
<Array as="points">
<mxPoint x="250" y="1010" />
<mxPoint x="660" y="1010" />
<mxPoint x="660" y="210" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-24" value="Delete new platform from list" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="30" y="630" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-26" value="Write updated platform list to DB" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="353" y="760" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-32" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-30" target="uiKHp9CyL3H5hJtIc4hJ-31">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-39" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-30">
<mxGeometry relative="1" as="geometry">
<mxPoint x="660" y="360" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-30" value="Is platform&lt;div&gt;present in&amp;nbsp;&lt;div&gt;on-disk DB&lt;/div&gt;&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="510" y="310" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-31" value="Show warning dialog" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="500" y="460" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-36" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-33" target="uiKHp9CyL3H5hJtIc4hJ-35">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-37" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-33">
<mxGeometry relative="1" as="geometry">
<mxPoint x="660" y="620" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-33" value="User accepted dialog?" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="510" y="570" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-34" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-31" target="uiKHp9CyL3H5hJtIc4hJ-33">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-38" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-35">
<mxGeometry relative="1" as="geometry">
<mxPoint x="660" y="820" as="targetPoint" />
<Array as="points">
<mxPoint x="560" y="820" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-35" value="Delete platform from DB" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="500" y="720" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-42" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-40" target="uiKHp9CyL3H5hJtIc4hJ-41">
<mxGeometry x="-0.0811" y="-10" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-44" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-40">
<mxGeometry relative="1" as="geometry">
<mxPoint x="660" y="990" as="targetPoint" />
<Array as="points">
<mxPoint x="413" y="990" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-40" value="Enable&amp;nbsp;&lt;div&gt;checkbox&amp;nbsp;&lt;/div&gt;&lt;div&gt;toggled&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="363" y="870" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-43" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-41">
<mxGeometry relative="1" as="geometry">
<mxPoint x="660" y="920.344827586207" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-41" value="Update platforms enable state in list" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="500" y="890" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="uiKHp9CyL3H5hJtIc4hJ-45" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="uiKHp9CyL3H5hJtIc4hJ-26" target="uiKHp9CyL3H5hJtIc4hJ-40">
<mxGeometry relative="1" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

View File

@ -0,0 +1,83 @@
<mxfile host="Electron" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/26.2.2 Chrome/134.0.6998.178 Electron/35.1.2 Safari/537.36" version="26.2.2">
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
<mxGraphModel dx="910" dy="1003" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="4t4bEUU0-kKJdhU_jB_B-0" value="Display/update graph of results" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.start_1;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="220" y="60" width="100" height="60" as="geometry" />
</mxCell>
<mxCell id="4t4bEUU0-kKJdhU_jB_B-4" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="4t4bEUU0-kKJdhU_jB_B-1" target="4t4bEUU0-kKJdhU_jB_B-0">
<mxGeometry relative="1" as="geometry">
<mxPoint x="180" y="90" as="targetPoint" />
<Array as="points">
<mxPoint x="180" y="220" />
<mxPoint x="180" y="90" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="4t4bEUU0-kKJdhU_jB_B-6" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="4t4bEUU0-kKJdhU_jB_B-1" target="4t4bEUU0-kKJdhU_jB_B-7">
<mxGeometry relative="1" as="geometry">
<mxPoint x="270" y="320" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="4t4bEUU0-kKJdhU_jB_B-1" value="Slider moved?" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="220" y="170" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="4t4bEUU0-kKJdhU_jB_B-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="4t4bEUU0-kKJdhU_jB_B-0" target="4t4bEUU0-kKJdhU_jB_B-1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="4t4bEUU0-kKJdhU_jB_B-11" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="4t4bEUU0-kKJdhU_jB_B-7" target="4t4bEUU0-kKJdhU_jB_B-10">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="330" y="370" />
<mxPoint x="330" y="230" />
<mxPoint x="430" y="230" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="4t4bEUU0-kKJdhU_jB_B-7" value="Output&lt;div&gt;graph button&lt;div&gt;clicked?&lt;/div&gt;&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="220" y="320" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="4t4bEUU0-kKJdhU_jB_B-12" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="4t4bEUU0-kKJdhU_jB_B-8" target="4t4bEUU0-kKJdhU_jB_B-10">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="350" y="520" />
<mxPoint x="350" y="280" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="4t4bEUU0-kKJdhU_jB_B-8" value="Output&amp;nbsp;&lt;div&gt;CSV button&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;clicked?&lt;/span&gt;&lt;/div&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="220" y="470" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="4t4bEUU0-kKJdhU_jB_B-9" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="4t4bEUU0-kKJdhU_jB_B-7" target="4t4bEUU0-kKJdhU_jB_B-8">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="4t4bEUU0-kKJdhU_jB_B-10" value="Show file dialog" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="370" y="250" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="4t4bEUU0-kKJdhU_jB_B-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="4t4bEUU0-kKJdhU_jB_B-13" target="4t4bEUU0-kKJdhU_jB_B-15">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="4t4bEUU0-kKJdhU_jB_B-13" value="File writable?" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="380" y="360" width="100" height="100" as="geometry" />
</mxCell>
<mxCell id="4t4bEUU0-kKJdhU_jB_B-14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="4t4bEUU0-kKJdhU_jB_B-10" target="4t4bEUU0-kKJdhU_jB_B-13">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="4t4bEUU0-kKJdhU_jB_B-17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="4t4bEUU0-kKJdhU_jB_B-15" target="4t4bEUU0-kKJdhU_jB_B-1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="530" y="90" as="targetPoint" />
<Array as="points">
<mxPoint x="530" y="540" />
<mxPoint x="530" y="220" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="4t4bEUU0-kKJdhU_jB_B-15" value="Save results to CSV or PNG file" style="whiteSpace=wrap;html=1;strokeWidth=2;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="370" y="510" width="120" height="60" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PlatformRename</class>
<widget class="QDialog" name="PlatformRename">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>300</width>
<height>100</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>100</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>100</height>
</size>
</property>
<property name="windowTitle">
<string>Name Platform</string>
</property>
<widget class="QLineEdit" name="rename_plat_box">
<property name="geometry">
<rect>
<x>7</x>
<y>41</y>
<width>287</width>
<height>22</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
</widget>
<widget class="QLabel" name="rename_plat_lab">
<property name="geometry">
<rect>
<x>35</x>
<y>9</y>
<width>241</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>Enter a new name for the platform</string>
</property>
</widget>
<widget class="QPushButton" name="rename_plat_ok_but">
<property name="geometry">
<rect>
<x>220</x>
<y>70</y>
<width>75</width>
<height>24</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>OK</string>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>rename_plat_ok_but</sender>
<signal>clicked()</signal>
<receiver>PlatformRename</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>257</x>
<y>71</y>
</hint>
<hint type="destinationlabel">
<x>149</x>
<y>44</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -7,27 +7,24 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>420</width> <width>420</width>
<height>250</height> <height>240</height>
</rect> </rect>
</property> </property>
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>420</width> <width>420</width>
<height>250</height> <height>240</height>
</size> </size>
</property> </property>
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>420</width> <width>420</width>
<height>250</height> <height>240</height>
</size> </size>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>SIPPCompare</string> <string>SIPPCompare</string>
</property> </property>
<property name="tabShape">
<enum>QTabWidget::TabShape::Rounded</enum>
</property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="formLayoutWidget"> <widget class="QWidget" name="formLayoutWidget">
<property name="geometry"> <property name="geometry">
@ -35,7 +32,7 @@
<x>10</x> <x>10</x>
<y>0</y> <y>0</y>
<width>401</width> <width>401</width>
<height>191</height> <height>188</height>
</rect> </rect>
</property> </property>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
@ -53,13 +50,47 @@
</property> </property>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="value_lab"> <widget class="QLabel" name="value_lab">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text"> <property name="text">
<string>Pension value</string> <string>Pension value</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="value_input">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="specialValueText">
<string>£</string>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="prefix">
<string>£</string>
</property>
<property name="maximum">
<double>999999999.990000009536743</double>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2"> <item row="1" column="0" colspan="2">
<widget class="QLabel" name="mix_lab"> <widget class="QLabel" name="mix_lab">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text"> <property name="text">
<string>Investment mix (funds 50% / shares 50%)</string> <string>Investment mix (funds 50% / shares 50%)</string>
</property> </property>
@ -86,58 +117,73 @@
</item> </item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="share_trades_lab"> <widget class="QLabel" name="share_trades_lab">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text"> <property name="text">
<string>Annual share trades</string> <string>Annual share trades</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1">
<widget class="QComboBox" name="share_trades_combo">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0"> <item row="4" column="0">
<widget class="QLabel" name="fund_trades_lab"> <widget class="QLabel" name="fund_trades_lab">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text"> <property name="text">
<string>Annual fund trades</string> <string>Annual fund trades</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1">
<widget class="QComboBox" name="fund_trades_combo">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2"> <item row="5" column="0" colspan="2">
<widget class="QPushButton" name="calc_but"> <widget class="QPushButton" name="calc_but">
<property name="enabled">
<bool>false</bool>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text"> <property name="text">
<string>Calculate</string> <string>Calculate</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="3" column="1">
<widget class="QDoubleSpinBox" name="value_input"> <widget class="FastEditQSpinBox" name="share_trades_box">
<property name="frame"> <property name="font">
<bool>true</bool> <font>
</property> <pointsize>11</pointsize>
<property name="buttonSymbols"> </font>
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="specialValueText">
<string>£</string>
</property> </property>
<property name="correctionMode"> <property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum> <enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property> </property>
<property name="prefix"> <property name="prefix">
<string>£</string> <string/>
</property> </property>
<property name="maximum"> <property name="maximum">
<double>999999999.990000009536743</double> <number>999</number>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="FastEditQSpinBox" name="fund_trades_box">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="prefix">
<string/>
</property> </property>
</widget> </widget>
</item> </item>
@ -145,8 +191,10 @@
</widget> </widget>
</widget> </widget>
<widget class="QStatusBar" name="statusbar"> <widget class="QStatusBar" name="statusbar">
<property name="enabled"> <property name="font">
<bool>true</bool> <font>
<pointsize>10</pointsize>
</font>
</property> </property>
</widget> </widget>
<widget class="QMenuBar" name="menuBar"> <widget class="QMenuBar" name="menuBar">
@ -158,20 +206,49 @@
<height>33</height> <height>33</height>
</rect> </rect>
</property> </property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<widget class="QMenu" name="menuPlatforms"> <widget class="QMenu" name="menuPlatforms">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="title"> <property name="title">
<string>File</string> <string>File</string>
</property> </property>
<addaction name="actionEdit_Platforms"/> <addaction name="actionList_Platforms"/>
</widget> </widget>
<addaction name="menuPlatforms"/> <addaction name="menuPlatforms"/>
</widget> </widget>
<action name="actionEdit_Platforms"> <action name="actionList_Platforms">
<property name="text"> <property name="text">
<string>Edit Platforms</string> <string>Platform List</string>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property> </property>
</action> </action>
</widget> </widget>
<customwidgets>
<customwidget>
<class>FastEditQSpinBox</class>
<extends>QSpinBox</extends>
<header>widgets/fastedit_spinbox</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>value_input</tabstop>
<tabstop>mix_slider</tabstop>
<tabstop>share_trades_box</tabstop>
<tabstop>fund_trades_box</tabstop>
<tabstop>calc_but</tabstop>
</tabstops>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>

View File

@ -1,84 +1,133 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>ResultsWindow</class> <class>OutputWindow</class>
<widget class="QWidget" name="ResultsWindow"> <widget class="QWidget" name="OutputWindow">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>1330</width>
<height>300</height> <height>685</height>
</rect> </rect>
</property> </property>
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>400</width> <width>1330</width>
<height>300</height> <height>685</height>
</size> </size>
</property> </property>
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>400</width> <width>1330</width>
<height>300</height> <height>685</height>
</size> </size>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Results</string> <string>Results</string>
</property> </property>
<widget class="QTextEdit" name="output"> <widget class="MplWidget" name="graphWidget" native="true">
<property name="geometry">
<rect>
<x>-10</x>
<y>-10</y>
<width>1350</width>
<height>650</height>
</rect>
</property>
</widget>
<widget class="QPushButton" name="save_graph_but">
<property name="geometry">
<rect>
<x>1231</x>
<y>642</y>
<width>91</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Save graph</string>
</property>
</widget>
<widget class="QSlider" name="time_slider">
<property name="geometry">
<rect>
<x>370</x>
<y>635</y>
<width>581</width>
<height>41</height>
</rect>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>20</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksAbove</enum>
</property>
<property name="tickInterval">
<number>1</number>
</property>
</widget>
<widget class="QPushButton" name="save_csv_but">
<property name="geometry">
<rect>
<x>1134</x>
<y>642</y>
<width>91</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Save CSV</string>
</property>
</widget>
<widget class="QLabel" name="time_lab">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>10</x> <x>10</x>
<y>10</y> <y>640</y>
<width>380</width> <width>341</width>
<height>250</height> <height>31</height>
</rect> </rect>
</property> </property>
</widget> <property name="font">
<widget class="QPushButton" name="res_ok_but"> <font>
<property name="geometry"> <pointsize>11</pointsize>
<rect> </font>
<x>320</x>
<y>270</y>
<width>75</width>
<height>24</height>
</rect>
</property> </property>
<property name="text"> <property name="text">
<string>OK</string> <string>Fees over 1 year(s) (assuming no change in value)</string>
</property>
</widget>
<widget class="QPushButton" name="res_save_but">
<property name="geometry">
<rect>
<x>240</x>
<y>270</y>
<width>75</width>
<height>24</height>
</rect>
</property>
<property name="text">
<string>Save</string>
</property> </property>
</widget> </widget>
</widget> </widget>
<customwidgets>
<customwidget>
<class>MplWidget</class>
<extends>QWidget</extends>
<header>widgets/mpl_widget</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>time_slider</tabstop>
<tabstop>save_csv_but</tabstop>
<tabstop>save_graph_but</tabstop>
</tabstops>
<resources/> <resources/>
<connections> <connections/>
<connection>
<sender>res_ok_but</sender>
<signal>clicked(bool)</signal>
<receiver>ResultsWindow</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>357</x>
<y>281</y>
</hint>
<hint type="destinationlabel">
<x>199</x>
<y>149</y>
</hint>
</hints>
</connection>
</connections>
</ui> </ui>

View File

@ -6,20 +6,32 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>481</width> <width>630</width>
<height>305</height> <height>580</height>
</rect> </rect>
</property> </property>
<property name="minimumSize">
<size>
<width>630</width>
<height>580</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>630</width>
<height>580</height>
</size>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Platform Editor</string> <string>Platform Editor</string>
</property> </property>
<widget class="QWidget" name="gridLayoutWidget"> <widget class="QWidget" name="gridLayoutWidget">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>10</x> <x>13</x>
<y>10</y> <y>20</y>
<width>461</width> <width>616</width>
<height>251</height> <height>242</height>
</rect> </rect>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
@ -39,101 +51,232 @@
<number>10</number> <number>10</number>
</property> </property>
<property name="verticalSpacing"> <property name="verticalSpacing">
<number>5</number> <number>6</number>
</property> </property>
<item row="0" column="0" colspan="2"> <item row="0" column="3">
<widget class="QLabel" name="plat_name_lab"> <widget class="QCheckBox" name="plat_name_check">
<property name="text"> <property name="text">
<string>Platform name</string> <string> </string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="3">
<widget class="QCheckBox" name="share_deal_fee_check">
<property name="enabled">
<bool>false</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="FastEditQDoubleSpinBox" name="share_plat_max_fee_box">
<property name="enabled">
<bool>false</bool>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="prefix">
<string>£</string>
</property>
<property name="maximum">
<double>999.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QCheckBox" name="fund_deal_fee_check">
<property name="enabled">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="FastEditQDoubleSpinBox" name="share_deal_fee_box">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="prefix">
<string>£</string>
</property>
<property name="maximum">
<double>999.000000000000000</double>
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="0" colspan="2"> <item row="8" column="0" colspan="2">
<widget class="QLabel" name="share_deal_reduce_amount_lab">
<property name="text">
<string>Share dealing discount amount</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="plat_name_box"/>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="share_plat_fee_lab">
<property name="text">
<string>Share platform fee*</string>
</property>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="QLabel" name="share_deal_reduce_trades_lab"> <widget class="QLabel" name="share_deal_reduce_trades_lab">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text"> <property name="text">
<string>Share dealing discount # of trades</string> <string>Share dealing discount # of trades</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="3">
<widget class="QCheckBox" name="share_deal_reduce_amount_check"/>
</item>
<item row="5" column="0" colspan="2"> <item row="5" column="0" colspan="2">
<widget class="QLabel" name="share_plat_max_fee_lab">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>Share platform monthly fee cap</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="FastEditQDoubleSpinBox" name="fund_deal_fee_box">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="prefix">
<string>£</string>
</property>
<property name="maximum">
<double>999.000000000000000</double>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QLabel" name="share_deal_fee_lab"> <widget class="QLabel" name="share_deal_fee_lab">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text"> <property name="text">
<string>Share dealing fee*</string> <string>Share dealing fee*</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0" colspan="2"> <item row="8" column="2">
<widget class="FastEditQSpinBox" name="share_deal_reduce_trades_box">
<property name="enabled">
<bool>false</bool>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
<item row="8" column="3">
<widget class="QCheckBox" name="share_deal_reduce_trades_check"/>
</item>
<item row="4" column="3">
<widget class="QCheckBox" name="share_plat_fee_check">
<property name="enabled">
<bool>false</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="fund_deal_fee_lab"> <widget class="QLabel" name="fund_deal_fee_lab">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text"> <property name="text">
<string>Fund dealing fee*</string> <string>Fund dealing fee</string>
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QCheckBox" name="share_plat_max_fee_check"/>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="plat_name_box">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
</widget>
</item>
<item row="9" column="0" colspan="2">
<widget class="QLabel" name="share_deal_reduce_amount_lab">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>Share dealing discount amount</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0" colspan="2"> <item row="4" column="0" colspan="2">
<widget class="QLabel" name="share_plat_max_fee_lab"> <widget class="QLabel" name="share_plat_fee_lab">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text"> <property name="text">
<string>Share platform fee cap/mth</string> <string>Share platform fee*</string>
</property>
</widget>
</item>
<item row="8" column="2">
<widget class="QDoubleSpinBox" name="share_deal_reduce_amount_box">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="prefix">
<string>£</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QDoubleSpinBox" name="share_deal_fee_box">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="prefix">
<string>£</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="2"> <item row="4" column="2">
<widget class="QDoubleSpinBox" name="share_plat_max_fee_box"> <widget class="FastEditQDoubleSpinBox" name="share_plat_fee_box">
<property name="buttonSymbols"> <property name="font">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum> <font>
<pointsize>11</pointsize>
</font>
</property> </property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="prefix">
<string>£</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QDoubleSpinBox" name="share_plat_fee_box">
<property name="buttonSymbols"> <property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum> <enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property> </property>
@ -143,10 +286,33 @@
<property name="suffix"> <property name="suffix">
<string>%</string> <string>%</string>
</property> </property>
<property name="maximum">
<double>99.000000000000000</double>
</property>
</widget> </widget>
</item> </item>
<item row="1" column="2"> <item row="0" column="0" colspan="2">
<widget class="QDoubleSpinBox" name="fund_deal_fee_box"> <widget class="QLabel" name="plat_name_lab">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>Platform name</string>
</property>
</widget>
</item>
<item row="9" column="2">
<widget class="FastEditQDoubleSpinBox" name="share_deal_reduce_amount_box">
<property name="enabled">
<bool>false</bool>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="buttonSymbols"> <property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum> <enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property> </property>
@ -156,15 +322,8 @@
<property name="prefix"> <property name="prefix">
<string>£</string> <string>£</string>
</property> </property>
</widget> <property name="maximum">
</item> <double>999.000000000000000</double>
<item row="7" column="2">
<widget class="QSpinBox" name="share_deal_reduce_trades_box">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property> </property>
</widget> </widget>
</item> </item>
@ -176,21 +335,26 @@
</property> </property>
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>330</x> <x>482</x>
<y>270</y> <y>545</y>
<width>141</width> <width>141</width>
<height>24</height> <height>24</height>
</rect> </rect>
</property> </property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text"> <property name="text">
<string>Save</string> <string>Save</string>
</property> </property>
</widget> </widget>
<widget class="QLabel" name="label"> <widget class="QLabel" name="req_field_lab">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>20</x> <x>8</x>
<y>270</y> <y>549</y>
<width>191</width> <width>191</width>
<height>21</height> <height>21</height>
</rect> </rect>
@ -199,14 +363,225 @@
<string>* Indicates required field</string> <string>* Indicates required field</string>
</property> </property>
</widget> </widget>
<widget class="QLabel" name="active_lab">
<property name="geometry">
<rect>
<x>572</x>
<y>10</y>
<width>61</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Active?</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
<widget class="QWidget" name="gridLayoutWidget_2">
<property name="geometry">
<rect>
<x>19</x>
<y>307</y>
<width>591</width>
<height>40</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="3">
<widget class="FastEditQDoubleSpinBox" name="first_tier_fee_box">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="suffix">
<string>%</string>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="first_tier_lab">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>on the first</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="first_tier_fee_lab">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>the fee is</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="FastEditQDoubleSpinBox" name="first_tier_box">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="correctionMode">
<enum>QAbstractSpinBox::CorrectionMode::CorrectToNearestValue</enum>
</property>
<property name="prefix">
<string>£</string>
</property>
<property name="maximum">
<double>9999999.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QPushButton" name="add_row_but">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>532</x>
<y>487</y>
<width>91</width>
<height>24</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Add row</string>
</property>
</widget>
<widget class="QPushButton" name="del_row_but">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>440</x>
<y>487</y>
<width>91</width>
<height>24</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Remove row</string>
</property>
</widget>
<widget class="QLabel" name="val_above_lab">
<property name="geometry">
<rect>
<x>10</x>
<y>486</y>
<width>421</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>on the value above £ there is no charge</string>
</property>
</widget>
<widget class="QLabel" name="fund_plat_fee_lab">
<property name="geometry">
<rect>
<x>10</x>
<y>285</y>
<width>151</width>
<height>16</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>Fund platform fee*</string>
</property>
</widget>
</widget> </widget>
<customwidgets>
<customwidget>
<class>FastEditQSpinBox</class>
<extends>QSpinBox</extends>
<header>widgets/fastedit_spinbox</header>
</customwidget>
<customwidget>
<class>FastEditQDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>widgets/fastedit_spinbox</header>
</customwidget>
</customwidgets>
<tabstops> <tabstops>
<tabstop>plat_name_box</tabstop> <tabstop>plat_name_box</tabstop>
<tabstop>fund_deal_fee_box</tabstop> <tabstop>fund_deal_fee_box</tabstop>
<tabstop>share_plat_fee_box</tabstop> <tabstop>share_plat_fee_box</tabstop>
<tabstop>share_plat_max_fee_box</tabstop> <tabstop>share_plat_max_fee_box</tabstop>
<tabstop>share_deal_fee_box</tabstop> <tabstop>share_deal_fee_box</tabstop>
<tabstop>share_deal_reduce_trades_box</tabstop>
<tabstop>share_deal_reduce_amount_box</tabstop> <tabstop>share_deal_reduce_amount_box</tabstop>
<tabstop>plat_name_check</tabstop>
<tabstop>fund_deal_fee_check</tabstop>
<tabstop>share_plat_fee_check</tabstop>
<tabstop>share_plat_max_fee_check</tabstop>
<tabstop>share_deal_fee_check</tabstop>
<tabstop>share_deal_reduce_trades_check</tabstop>
<tabstop>share_deal_reduce_amount_check</tabstop>
<tabstop>first_tier_box</tabstop>
<tabstop>first_tier_fee_box</tabstop>
<tabstop>del_row_but</tabstop>
<tabstop>add_row_but</tabstop>
<tabstop>save_but</tabstop> <tabstop>save_but</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>

170
gui/platform_list.ui Normal file
View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PlatformList</class>
<widget class="QWidget" name="PlatformList">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>263</width>
<height>473</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>263</width>
<height>473</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>263</width>
<height>473</height>
</size>
</property>
<property name="windowTitle">
<string>Platform List</string>
</property>
<widget class="QListWidget" name="platListWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>34</y>
<width>243</width>
<height>371</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
</widget>
<widget class="QPushButton" name="add_plat_but">
<property name="geometry">
<rect>
<x>11</x>
<y>413</y>
<width>121</width>
<height>24</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Add platform</string>
</property>
</widget>
<widget class="QPushButton" name="del_plat_but">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>135</x>
<y>413</y>
<width>121</width>
<height>24</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Remove platform</string>
</property>
</widget>
<widget class="QPushButton" name="plist_save_but">
<property name="geometry">
<rect>
<x>136</x>
<y>442</y>
<width>121</width>
<height>24</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Save</string>
</property>
</widget>
<widget class="QPushButton" name="edit_plat_but">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>11</x>
<y>442</y>
<width>121</width>
<height>24</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Edit platform</string>
</property>
</widget>
<widget class="QCheckBox" name="plat_enabled_check">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>12</x>
<y>8</y>
<width>231</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="text">
<string>Platform enabled?</string>
</property>
</widget>
</widget>
<tabstops>
<tabstop>platListWidget</tabstop>
<tabstop>add_plat_but</tabstop>
<tabstop>del_plat_but</tabstop>
<tabstop>edit_plat_but</tabstop>
<tabstop>plist_save_but</tabstop>
<tabstop>plat_enabled_check</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>plist_save_but</sender>
<signal>clicked()</signal>
<receiver>PlatformList</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>196</x>
<y>453</y>
</hint>
<hint type="destinationlabel">
<x>131</x>
<y>236</y>
</hint>
</hints>
</connection>
</connections>
</ui>

BIN
icon2.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

16
src/data_struct.py Normal file
View File

@ -0,0 +1,16 @@
class Platform:
def __init__(self, plat_id, fund_plat_fee, plat_name, enabled, fund_deal_fee,
share_plat_fee, share_plat_max_fee, share_deal_fee,
share_deal_reduce_trades, share_deal_reduce_amount,
):
self.plat_id = plat_id
self.fund_plat_fee = fund_plat_fee
self.plat_name = plat_name
self.enabled = enabled
self.fund_deal_fee = fund_deal_fee
self.share_plat_fee = share_plat_fee
self.share_plat_max_fee = share_plat_max_fee
self.share_deal_fee = share_deal_fee
self.share_deal_reduce_trades = share_deal_reduce_trades
self.share_deal_reduce_amount = share_deal_reduce_amount

262
src/db_handler.py Normal file
View File

@ -0,0 +1,262 @@
import os
import sqlite3
import resource_finder
from data_struct import Platform
class DBHandler:
def __init__(self):
# Function to create all necessary database tables if the DB file doesn't exist
def create_tables():
self.cur.execute("""
CREATE TABLE "tblPlatforms" (
"PlatformID" INTEGER NOT NULL UNIQUE,
"PlatformName" TEXT,
"IsEnabled" INTEGER NOT NULL,
PRIMARY KEY("PlatformID")
)
""")
self.cur.execute("""
CREATE TABLE "tblFlatPlatFees" (
"PlatformID" INTEGER NOT NULL UNIQUE,
"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 UNIQUE,
"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" (
"UserID" INTEGER NOT NULL UNIQUE,
"PensionValue" REAL NOT NULL,
"SliderValue" INTEGER NOT NULL,
"ShareTrades" INTEGER NOT NULL,
"FundTrades" INTEGER NOT NULL,
PRIMARY KEY("UserID")
)
""")
if not os.path.exists(resource_finder.get_res_path("SIPPCompare.db")):
db_exists = False
else:
db_exists = True
self.conn = sqlite3.connect(resource_finder.get_res_path("SIPPCompare.db"))
self.cur = self.conn.cursor()
if not db_exists:
create_tables()
# Retrieve the list of platform names for list population
def retrieve_plat_list(self) -> list:
res = self.cur.execute("SELECT PlatformName FROM tblPlatforms").fetchall()
plat_name_list = []
for platform in res:
plat_name_list.append(platform[0])
return plat_name_list
# Write updated platform data to DB when changes are saved
def write_platforms(self, plat_list: list[Platform]):
for i in range(len(plat_list)):
platforms_data = [
plat_list[i].plat_id,
plat_list[i].plat_name,
plat_list[i].enabled
]
flat_plat_fees_data = [
plat_list[i].plat_id,
plat_list[i].share_plat_fee,
plat_list[i].share_plat_max_fee
]
flat_deal_fees_data = [
plat_list[i].plat_id,
plat_list[i].fund_deal_fee,
plat_list[i].share_deal_fee,
plat_list[i].share_deal_reduce_trades,
plat_list[i].share_deal_reduce_amount
]
res = self.cur.execute(f"""
SELECT EXISTS(
SELECT PlatformID FROM tblPlatforms
WHERE PlatformID = {i}
)""").fetchall()
if res[0][0] == 1:
self.cur.execute(f"""
UPDATE tblPlatforms SET
PlatformID = ?,
PlatformName = ?,
IsEnabled = ?
WHERE PlatformID = {i}
""", platforms_data)
self.cur.execute(f"""
UPDATE tblFlatPlatFees SET
PlatformID = ?,
SharePlatFee = ?,
SharePlatMaxFee = ?
WHERE PlatformID = {i}
""", flat_plat_fees_data)
self.cur.execute(f"""
UPDATE tblFlatDealFees SET
PlatformID = ?,
FundDealFee = ?,
ShareDealFee = ?,
ShareDealReduceTrades = ?,
ShareDealReduceAmount = ?
WHERE PlatformID = {i}
""", flat_deal_fees_data)
self.cur.execute(f"DELETE FROM tblFundPlatFee WHERE PlatformID = {i}")
else:
self.cur.execute("INSERT INTO tblPlatforms VALUES (?, ?, ?)", platforms_data)
self.cur.execute("INSERT INTO tblFlatPlatFees VALUES (?, ?, ?)", flat_plat_fees_data)
self.cur.execute("INSERT INTO tblFlatDealFees VALUES (?, ?, ?, ?, ?)", flat_deal_fees_data)
exec_str = f"INSERT INTO tblFundPlatFee VALUES\n"
for x in range(len(plat_list[i].fund_plat_fee[0])):
band = plat_list[i].fund_plat_fee[0][x]
fee = plat_list[i].fund_plat_fee[1][x]
exec_str += f"({i}, {band}, {fee}),\n"
exec_str = exec_str[:-2]
self.cur.execute(exec_str)
self.conn.commit()
# Retrieve all info about all platforms in DB and initialise Platform objects
def retrieve_platforms(self) -> list[Platform]:
# Retrieve all one-to-one relations
platforms_res = self.cur.execute("""
SELECT
-- tblPlatforms
tblPlatforms.PlatformID, PlatformName, IsEnabled,
-- tblFlatPlatFees
SharePlatFee, SharePlatMaxFee,
-- tblFlatDealFees
FundDealFee,
ShareDealFee,
ShareDealReduceTrades,
ShareDealReduceAmount
FROM tblPlatforms
INNER JOIN tblFlatPlatFees ON
tblPlatforms.PlatformID = tblFlatPlatFees.PlatformID
INNER JOIN tblFlatDealFees ON
tblPlatforms.PlatformID = tblFlatDealFees.PlatformID
""").fetchall()
platforms = []
for platform in platforms_res:
# plat_id, plat_name, enabled, share_plat_fee, share_plat_max_fee, fund_deal_fee,
# share_deal_fee, share_deal_reduce_trades, share_deal_reduce_amount
this_platform = [platform[0], platform[1], platform[2], platform[3], platform[4],
platform[5], platform[6], platform[7], platform[8]]
platforms.append(this_platform)
# Insert 2D array into each platform data in preparation for fund_plat_fee retrival
for platform in platforms:
platform.insert(1, [[], []])
# Get all records from tblFundPlatFee, add them to the platforms list based on ID
# WARNING: This code is dependent on PlatformID being sequential from 0 in DB records
fund_plat_fee_res = self.cur.execute("SELECT * FROM tblFundPlatFee ORDER BY PlatformID ASC").fetchall()
for i in range(len(fund_plat_fee_res)):
plat_id = fund_plat_fee_res[i][0]
platforms[plat_id][1][0].append(fund_plat_fee_res[i][1])
platforms[plat_id][1][1].append(fund_plat_fee_res[i][2])
platform_obj_list: list[Platform] = []
for platform in platforms:
platform_obj_list.append(Platform(
platform[0], platform[1], platform[2], platform[3],
platform[6], platform[4], platform[5], platform[7],
platform[8], platform[9]
))
return platform_obj_list
# This function writes the details the user entered this session to the DB
def write_user_details(self, pension_val: float, slider_val: int, share_trades: int, fund_trades: int):
# Hardcode UserID as 0
user_details_data = (0, pension_val, slider_val, share_trades, fund_trades)
# Check if there is already a record in tblUserDetails
res = self.cur.execute("SELECT EXISTS(SELECT 1 FROM tblUserDetails)").fetchone()
if res[0] == 0:
# If there isn't then insert a new record
self.cur.execute("INSERT INTO tblUserDetails VALUES (?, ?, ?, ?, ?)", user_details_data)
else:
# If there is then update the existing record (only ever one record as of now)
self.cur.execute("""
UPDATE tblUserDetails SET
UserID = ?,
PensionValue = ?,
SliderValue = ?,
ShareTrades = ?,
FundTrades = ?
""", user_details_data)
self.conn.commit()
# Function to retrieve details entered by the user in prev session from DB
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: tuple = res.fetchone()
user_details_dict: dict[str, float | int] = {
"user_id": res_tuple[0],
"pension_val": res_tuple[1],
"slider_val": res_tuple[2],
"share_trades": res_tuple[3],
"fund_trades": res_tuple[4]
}
return user_details_dict
# Remove a platform from the DB - update the IDs to keep them sequential
def remove_platform(self, index: int):
tbl_list = ["tblPlatforms", "tblFlatPlatFees", "tblFlatDealFees", "tblFundPlatFee"]
for tbl in tbl_list:
self.cur.execute(f"DELETE FROM {tbl} WHERE PlatformID = {index}")
res = self.cur.execute("SELECT PlatformID from tblPlatforms").fetchall()
n = len(res)
for i in range(n):
for tbl in tbl_list:
self.cur.execute(f"""
UPDATE {tbl}
SET PlatformID = {index + i}
WHERE PlatformID = {index + 1 + i}
""")
self.conn.commit()

View File

@ -1,20 +1,24 @@
from PyQt6.QtWidgets import QApplication ## Nuitka compilation options (Windows only)
# nuitka-project: --mode=onefile
# nuitka-project: --enable-plugin=pyqt6
# nuitka-project: --include-module=widgets.mpl_widget
# nuitka-project: --include-data-files=icon2.ico=icon2.ico
# nuitka-project: --include-data-dir=gui=gui
# nuitka-project: --windows-console-mode=disable
# nuitka-project: --windows-icon-from-ico=icon2.ico
# nuitka-project: --product-name=SIPPCompare
# nuitka-project: --file-description=SIPPCompare
# nuitka-project: --product-version=1.1
# nuitka-project: --output-dir=build
# nuitka-project: --output-filename=SIPPCompare
import sys import sys
import platform_edit from PyQt6.QtWidgets import QApplication
from main_window import SIPPCompare
app = QApplication(sys.argv) app = QApplication(sys.argv)
# Show platform edit window first, before main win window = SIPPCompare()
# When debugging, can be useful to autofill values to save time
if len(sys.argv) > 1:
if sys.argv[1] == "--DEBUG_AUTOFILL":
window = platform_edit.PlatformEdit(True)
else:
window = platform_edit.PlatformEdit(False)
else:
window = platform_edit.PlatformEdit(False)
window.show() window.show()
app.exec() app.exec()

View File

@ -1,48 +1,55 @@
from PyQt6.QtGui import QIntValidator
from PyQt6.QtWidgets import QMainWindow
from PyQt6 import uic from PyQt6 import uic
from PyQt6.QtCore import QTimer
from PyQt6.QtGui import QIntValidator, QIcon
from PyQt6.QtWidgets import QMainWindow, QApplication
import output_window import resource_finder
from db_handler import DBHandler
from platform_list import PlatformList
from output_window import OutputWindow
class SIPPCompare(QMainWindow): class SIPPCompare(QMainWindow):
# Receive instance of PlatformEdit() as parameter def __init__(self):
def __init__(self, plat_edit_win):
super().__init__() super().__init__()
# Import Qt Designer UI XML file # Import Qt Designer UI XML file
uic.loadUi("gui/main_gui.ui", self) uic.loadUi(resource_finder.get_res_path("gui/main_gui.ui"), self)
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
# Initialise class variables
# Inputs
self.fund_plat_fee = 0.0
self.plat_name = ""
self.fund_deal_fee = 0.0
self.share_plat_fee = 0.0
self.share_plat_max_fee = 0.0
self.share_deal_fee = 0.0
self.share_deal_reduce_trades = 0.0
self.share_deal_reduce_amount = 0.0
## Initialise class variables
# Results # Results
self.fund_plat_fees = 0.0 self.fund_plat_fees = 0.0
self.fund_deal_fees = 0.0 self.fund_deal_fees = 0.0
self.share_plat_fees = 0.0 self.share_plat_fees = 0.0
self.share_deal_fees = 0.0 self.share_deal_fees = 0.0
self.results = []
# Create window objects # Create window objects
self.platform_win = plat_edit_win self.db = DBHandler()
self.output_win = output_window.OutputWindow() self.platform_list_win = PlatformList(self.db)
if len(self.platform_list_win.plat_name_list) == 0:
QTimer.singleShot(1, self.platform_list_win.show)
self.output_win = None
# Handle events ## Handle events
self.calc_but.clicked.connect(self.calculate_fees) self.calc_but.clicked.connect(self.indicate_loading)
# Menu bar entry (File -> Edit Platforms) # Menu bar entry (File -> Platform List)
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 # Update percentage mix label when slider moved
self.mix_slider.valueChanged.connect(self.update_slider_lab) self.mix_slider.valueChanged.connect(self.update_slider_lab)
self.value_input.valueChanged.connect(self.check_valid)
# Validate input
self.share_trades_box.valueChanged.connect(self.check_valid)
self.fund_trades_box.valueChanged.connect(self.check_valid)
# Set validators ## Restore last session
self.share_trades_combo.setValidator(QIntValidator(0, 999)) prev_session_data = self.db.retrieve_user_details()
self.fund_trades_combo.setValidator(QIntValidator(0, 99)) 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_box.setValue(prev_session_data["share_trades"])
self.fund_trades_box.setValue(prev_session_data["fund_trades"])
self.calc_but.setFocus()
# Display slider position as mix between two nums (funds/shares) # Display slider position as mix between two nums (funds/shares)
def update_slider_lab(self): def update_slider_lab(self):
@ -50,66 +57,90 @@ class SIPPCompare(QMainWindow):
mix_lab_str = f"Investment mix (funds {slider_val}% / shares {100 - slider_val}%)" mix_lab_str = f"Investment mix (funds {slider_val}% / shares {100 - slider_val}%)"
self.mix_lab.setText(mix_lab_str) self.mix_lab.setText(mix_lab_str)
# Get variables from platform editor input fields # Ensure that trade fields aren't blank and pension value > 0
def init_variables(self): def check_valid(self):
self.plat_name = self.platform_win.get_plat_name() if self.value_input.value() != 0:
self.fund_plat_fee = self.platform_win.get_fund_plat_fee() self.calc_but.setEnabled(True)
self.fund_deal_fee = self.platform_win.get_fund_deal_fee() else:
self.share_plat_fee = self.platform_win.get_share_plat_fee() self.calc_but.setEnabled(False)
self.share_plat_max_fee = self.platform_win.get_share_plat_max_fee()
self.share_deal_fee = self.platform_win.get_share_deal_fee()
self.share_deal_reduce_trades = self.platform_win.get_share_deal_reduce_trades()
self.share_deal_reduce_amount = self.platform_win.get_share_deal_reduce_amount()
# Calculate fees def indicate_loading(self):
# TODO: Error checking on combo boxes self.calc_but.setText("Working...")
QTimer.singleShot(1, self.calculate_fees)
# Calculate fees for all active platforms
def calculate_fees(self): def calculate_fees(self):
self.init_variables() # Set to empty list each time to avoid persistence
# Set to zero each time to avoid persistence self.results = []
self.fund_plat_fees = 0
# Get user input
value_num = float(self.value_input.value()) value_num = float(self.value_input.value())
# Funds/shares mix slider_val: int = self.mix_slider.value()
slider_val = self.mix_slider.value() fund_trades_num = int(self.fund_trades_box.value())
funds_value = (slider_val / 100) * value_num share_trades_num = int(self.share_trades_box.value())
fund_trades_num = int(self.fund_trades_combo.currentText())
self.fund_deal_fees = fund_trades_num * self.fund_deal_fee
for i in range(1, len(self.fund_plat_fee[0])):
band = self.fund_plat_fee[0][i]
prev_band = self.fund_plat_fee[0][i - 1]
fee = self.fund_plat_fee[1][i]
gap = (band - prev_band)
if funds_value > gap:
self.fund_plat_fees += gap * (fee / 100)
funds_value -= gap
else:
self.fund_plat_fees += funds_value * (fee / 100)
break
shares_value = (1 - (slider_val / 100)) * value_num shares_value = (1 - (slider_val / 100)) * value_num
if (self.share_plat_fee * shares_value / 12) > self.share_plat_max_fee: index = 0
self.share_plat_fees = self.share_plat_max_fee * 12
else:
self.share_plat_fees = self.share_plat_fee * shares_value
share_trades_num = int(self.share_trades_combo.currentText()) for platform in self.platform_list_win.plat_list:
if (share_trades_num / 12) >= self.share_deal_reduce_trades: if not platform.enabled:
self.share_deal_fees = self.share_deal_reduce_amount * share_trades_num continue
else:
self.share_deal_fees = self.share_deal_fee * share_trades_num
fund_plat_fees = 0.0
fund_deal_fees = 0.0
share_plat_fees = 0.0
share_deal_fees = 0.0
plat_name = platform.plat_name
if plat_name is None or plat_name == "":
plat_name = f"Unnamed [ID: {index}]"
if platform.fund_deal_fee is not None:
fund_deal_fees = fund_trades_num * platform.fund_deal_fee
funds_value = (slider_val / 100) * value_num
for i in range(1, len(platform.fund_plat_fee[0])):
band = platform.fund_plat_fee[0][i]
prev_band = platform.fund_plat_fee[0][i - 1]
fee = platform.fund_plat_fee[1][i]
gap = (band - prev_band)
if funds_value > gap:
fund_plat_fees += gap * (fee / 100)
funds_value -= gap
else:
fund_plat_fees += funds_value * (fee / 100)
break
if platform.share_plat_max_fee is not None:
if (platform.share_plat_fee * shares_value / 12) > platform.share_plat_max_fee:
share_plat_fees = platform.share_plat_max_fee * 12
else:
share_plat_fees = platform.share_plat_fee * shares_value
if platform.share_deal_reduce_trades is not None:
if (share_trades_num / 12) >= platform.share_deal_reduce_trades:
share_deal_fees = platform.share_deal_reduce_amount * share_trades_num
else:
share_deal_fees = platform.share_deal_fee * share_trades_num
self.results.append([fund_plat_fees, fund_deal_fees, share_plat_fees, share_deal_fees, plat_name])
index += 1
# Save details entered by user for next session
self.db.write_user_details(value_num, slider_val, share_trades_num, fund_trades_num)
self.show_output_win() self.show_output_win()
# Show the output window - this func is called from calculate_fee() # Show the output window - this func is called from calculate_fee()
def show_output_win(self): def show_output_win(self):
# Refresh the results when new fees are calculated # Refresh the results when new fees are calculated
self.output_win.display_output(self.fund_plat_fees, self.fund_deal_fees, if self.output_win is None:
self.share_plat_fees, self.share_deal_fees, self.output_win = OutputWindow()
self.plat_name years = self.output_win.get_slider_position()
) self.output_win.display_output(self.results, years)
self.calc_but.setText("Calculate")
self.output_win.activateWindow()
self.output_win.raise_()
self.output_win.show() self.output_win.show()
QApplication.alert(self.output_win)
# Show the platform editor window (currently run-time only) def show_platform_list(self):
def show_platform_edit(self): self.platform_list_win.show()
self.platform_win.show()

View File

@ -1,52 +1,140 @@
from PyQt6.QtWidgets import QWidget
from PyQt6 import uic
import datetime
import os import os
from datetime import datetime
from PyQt6 import uic
from PyQt6.QtGui import QIcon, QFont
from PyQt6.QtWidgets import QWidget, QFileDialog, QMessageBox, QDialogButtonBox
import resource_finder
class SaveFailure(QMessageBox):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
self.setWindowTitle("Save failure")
self.setIcon(QMessageBox.Icon.Critical)
font = QFont()
font.setPointSize(11)
self.setFont(font)
self.setText("Failed to save file")
self.setDetailedText(
"This could be due to a permissions issue, or the file being in use by another process"
)
self.setStandardButtons(QMessageBox.StandardButton.Ok)
font.setPointSize(10)
self.findChild(QDialogButtonBox).setFont(font)
class OutputWindow(QWidget): class OutputWindow(QWidget):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
# Import Qt Designer UI XML file # Import Qt Designer UI XML file
uic.loadUi("gui/output_window.ui", self) uic.loadUi(resource_finder.get_res_path("gui/output_window.ui"), self)
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
# Initialise class variables # Define class variables
self.results_str = "" self.save_err_dialog = SaveFailure()
self.platform_name = "" self.canvas = self.graphWidget.canvas
self.ax = self.canvas.axes
self.fig = self.canvas.figure
self.results = []
# Handle events # Handle events
self.res_save_but.clicked.connect(self.save_results) self.save_graph_but.clicked.connect(self.save_graph)
self.save_csv_but.clicked.connect(self.save_csv)
self.time_slider.valueChanged.connect(self.change_time)
def save_results(self): def get_slider_position(self) -> int:
# Use datatime for txt filename return self.time_slider.value()
cur_time = datetime.datetime.now()
if not os.path.exists("output"):
os.makedirs("output")
filename_str = f"output/{self.platform_name}-{cur_time.year}.{cur_time.month}.{cur_time.day}.txt"
output_file = open(filename_str, "wt")
output_file.write(self.results_str)
# Display fees in output window as plaintext readout def display_output(self, results: list, years: int):
def display_output(self, fund_plat_fees: float, fund_deal_fees: float, self.results = results
share_plat_fees: float, share_deal_fees: float, plat_name: str): self.ax.clear()
self.platform_name = plat_name self.ax.cla()
self.results_str = f"Fees breakdown (Platform \"{self.platform_name}\"):" self.canvas.draw_idle()
self.results_str += "\n\nPlatform fees:" names = []
# :.2f is used in order to display 2 decimal places (currency form) values = []
self.results_str += f"\n\tFund platform fees: £{round(fund_plat_fees, 2):.2f}" for result in results:
self.results_str += f"\n\tShare platform fees: £{round(share_plat_fees, 2):.2f}" names.append(result[4])
total_plat_fees = fund_plat_fees + share_plat_fees values.append(sum(result[:4]) * years)
self.results_str += f"\n\tTotal platform fees: £{round(total_plat_fees, 2):.2f}"
self.results_str += "\n\nDealing fees:" names = sorted(names, key=lambda x: values[names.index(x)], reverse=True)
self.results_str += f"\n\tFund dealing fees: £{round(fund_deal_fees, 2):.2f}" values = sorted(values, reverse=True)
self.results_str += f"\n\tShare dealing fees: £{round(share_deal_fees, 2):.2f}"
total_deal_fees = fund_deal_fees + share_deal_fees
self.results_str += f"\n\tTotal dealing fees: £{round(total_deal_fees, 2):.2f}"
total_fees = total_plat_fees + total_deal_fees h_bars = self.ax.barh(names, values)
self.results_str += f"\n\nTotal fees: £{round(total_fees, 2):.2f}" self.ax.bar_label(h_bars, label_type='center', labels=[f"£{x:,.2f}" for x in h_bars.datavalues])
self.output.setText(self.results_str) def save_graph(self):
file_picker = QFileDialog(self)
file_picker.setFileMode(QFileDialog.FileMode.AnyFile)
file_picker.setDefaultSuffix("png")
file_picker.setWindowTitle("Save results as PNG")
file_picker.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
file_picker.setNameFilter("*.png")
file_path = ""
cur_time = datetime.now()
filename_str = f"{file_path}/SIPPCompare-{cur_time.year}.{cur_time.month}.{cur_time.day}.png"
file_picker.selectFile(filename_str)
if file_picker.exec():
file_path = file_picker.selectedFiles()[0]
try:
self.fig.savefig(file_path, dpi=150)
except OSError:
self.save_err_dialog.exec()
def save_csv(self):
# TODO: Sort CSV output, either alphabetically or by total fees
file_picker = QFileDialog(self)
file_picker.setFileMode(QFileDialog.FileMode.AnyFile)
file_picker.setDefaultSuffix("csv")
file_picker.setWindowTitle("Save results as CSV")
file_picker.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
file_picker.setNameFilter("*.csv")
file_path = ""
cur_time = datetime.now()
filename_str = f"{file_path}/SIPPCompare-{cur_time.year}.{cur_time.month}.{cur_time.day}.csv"
file_picker.selectFile(filename_str)
if file_picker.exec():
file_path = file_picker.selectedFiles()[0]
try:
csvfile = open(file_path, "wt")
csv_string = (
"Platform Name,Fund Platform Fee,Share Platform Fee,Fund Dealing Fee,"
"Share Dealing Fee,Total Platform Fees,Total Dealing Fees,Total Fund Fees,"
"Total Share Fees,Total Fees"
)
for result in self.results:
csv_string += '\n'
pn = result[4]
fpf = result[0]
spf = result[2]
fdf = result[1]
sdf = result[3]
tpf = fpf + spf
tdf = sdf + fdf
tff = fpf + fdf
tsf = spf + sdf
tf = tff + tsf
csv_string += (
f"{pn},\"£{fpf:,.2f}\",\"£{spf:,.2f}\",\"£{fdf:,.2f}\",\"£{sdf:,.2f}\","
f"\"£{tpf:,.2f}\",\"£{tdf:,.2f}\",\"£{tff:,.2f}\",\"£{tsf:,.2f}\",\"£{tf:,.2f}\""
)
csvfile.write(csv_string)
csvfile.close()
except OSError:
self.save_err_dialog.exec()
def change_time(self):
years: int = self.time_slider.value()
self.time_lab.setText(f"Fees over {years} year(s) (assuming no change in value)")
self.display_output(self.results, years)

View File

@ -1,50 +1,131 @@
from PyQt6.QtCore import QRegularExpression, QEvent, QObject, QTimer
from PyQt6.QtGui import QRegularExpressionValidator
from PyQt6.QtWidgets import QWidget
from PyQt6 import uic from PyQt6 import uic
from PyQt6.QtCore import QRegularExpression, QRect
from PyQt6.QtGui import QRegularExpressionValidator, QFont, QIcon
from PyQt6.QtWidgets import QLabel, QDialog
import main_window import resource_finder
from db_handler import DBHandler
from data_struct import Platform
from widgets.fastedit_spinbox import FastEditQDoubleSpinBox
class PlatformEdit(QWidget): class PlatformEdit(QDialog):
def __init__(self, autofill: bool): def __init__(self, plat: Platform):
super().__init__() super().__init__()
# Import Qt Designer UI XML file # Import Qt Designer UI XML file
uic.loadUi("gui/platform_edit.ui", self) uic.loadUi(resource_finder.get_res_path("gui/platform_edit.ui"), self)
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
# Initialise class variables # Initialise class variables
# TODO: Make fund_plat_fee user-defined self.plat = plat
self.fund_plat_fee = [ self.widgets_list_list = []
[0, 250000, 1000000, 2000000], if len(self.plat.fund_plat_fee[0]) > 1:
[0, 0.25, 0.1, 0.05] self.fund_fee_rows = len(self.plat.fund_plat_fee[0]) - 1
] else:
self.plat_name = "" self.fund_fee_rows = 1
self.fund_deal_fee = 0.0
self.share_plat_fee = 0.0
self.share_plat_max_fee = 0.0
self.share_deal_fee = 0.0
self.share_deal_reduce_trades = 0.0
self.share_deal_reduce_amount = 0.0
# Debugging feature: set with "--DEBUG_AUTOFILL" cmd argument
self.autofill = autofill
# Create main window object, passing this instance as param self.required_fields = [
self.main_win = main_window.SIPPCompare(self) self.share_plat_fee_box,
self.share_deal_fee_box
]
self.optional_fields = [
self.plat_name_box,
self.fund_deal_fee_box,
self.share_plat_max_fee_box,
self.share_deal_reduce_trades_box,
self.share_deal_reduce_amount_box
]
self.optional_check_boxes = [
self.plat_name_check,
self.fund_deal_fee_check,
self.share_plat_max_fee_check,
self.share_deal_reduce_trades_check,
self.share_deal_reduce_amount_check
]
self.check_boxes_ticked = [
True,
True,
False,
False,
False
]
# Set optional checkboxes based on DB storage
if self.plat.plat_name is None:
self.check_boxes_ticked[0] = False
self.plat_name_check.setChecked(False)
else:
self.check_boxes_ticked[0] = True
self.plat_name_check.setChecked(True)
self.plat_name_box.setText(self.plat.plat_name)
if self.plat.fund_deal_fee is None:
self.check_boxes_ticked[1] = False
self.fund_deal_fee_check.setChecked(False)
else:
self.check_boxes_ticked[1] = True
self.fund_deal_fee_check.setChecked(True)
self.fund_deal_fee_box.setValue(self.plat.fund_deal_fee)
self.share_plat_fee_box.setValue(self.plat.share_plat_fee * 100)
if self.plat.share_plat_max_fee is None:
self.check_boxes_ticked[2] = False
self.share_plat_max_fee_check.setChecked(False)
else:
self.check_boxes_ticked[2] = True
self.share_plat_max_fee_check.setChecked(True)
self.share_plat_max_fee_box.setValue(self.plat.share_plat_max_fee)
self.share_deal_fee_box.setValue(self.plat.share_deal_fee)
if self.plat.share_deal_reduce_trades is None:
self.check_boxes_ticked[3] = False
self.share_deal_reduce_trades_check.setChecked(False)
else:
self.check_boxes_ticked[3] = True
self.share_deal_reduce_trades_check.setChecked(True)
self.share_deal_reduce_trades_box.setValue(int(self.plat.share_deal_reduce_trades))
if self.plat.share_deal_reduce_trades is None:
self.check_boxes_ticked[4] = False
self.share_deal_reduce_amount_check.setChecked(False)
else:
self.check_boxes_ticked[4] = True
self.share_deal_reduce_amount_check.setChecked(True)
self.share_deal_reduce_amount_box.setValue(self.plat.share_deal_reduce_amount)
# Populate fund platform fee rows from DB
if len(self.plat.fund_plat_fee[0]) > 1:
self.first_tier_box.setValue(self.plat.fund_plat_fee[0][1])
self.first_tier_fee_box.setValue(self.plat.fund_plat_fee[1][1])
self.add_row(loading=True)
# Handle events # Handle events
for field in self.required_fields:
field.valueChanged.connect(self.check_valid)
for field in self.optional_fields:
field_type = field.staticMetaObject.className()
if field_type == "QLineEdit":
field.textChanged.connect(self.check_valid)
elif field_type == "FastEditQDoubleSpinBox" or field_type == "FastEditQSpinBox":
field.valueChanged.connect(self.check_valid)
for check_box in self.optional_check_boxes:
check_box.checkStateChanged.connect(self.check_valid)
self.first_tier_box.valueChanged.connect(self.check_valid)
self.first_tier_fee_box.valueChanged.connect(self.check_valid)
self.first_tier_box.valueChanged.connect(self.update_tier_labels)
# NOTE: Signal defined in UI file to close window when save button clicked # NOTE: Signal defined in UI file to close window when save button clicked
self.save_but.clicked.connect(self.init_variables) self.save_but.clicked.connect(self.init_variables)
self.fund_deal_fee_box.valueChanged.connect(self.check_valid) self.add_row_but.clicked.connect(self.add_row)
self.share_plat_fee_box.valueChanged.connect(self.check_valid) self.del_row_but.clicked.connect(self.remove_row)
self.share_deal_fee_box.valueChanged.connect(self.check_valid)
# Install event filter on input boxes in order to select all text on focus
self.fund_deal_fee_box.installEventFilter(self)
self.share_plat_fee_box.installEventFilter(self)
self.share_plat_max_fee_box.installEventFilter(self)
self.share_deal_fee_box.installEventFilter(self)
self.share_deal_reduce_trades_box.installEventFilter(self)
self.share_deal_reduce_amount_box.installEventFilter(self)
# Set validators # Set validators
# Regex accepts any characters that match [a-Z], [0-9] or _ # Regex accepts any characters that match [a-Z], [0-9] or _
@ -52,76 +133,231 @@ class PlatformEdit(QWidget):
QRegularExpressionValidator(QRegularExpression("\\w*")) QRegularExpressionValidator(QRegularExpression("\\w*"))
) )
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())
for i in range(len(self.widgets_list_list)):
band = self.widgets_list_list[i][1].value()
fee = self.widgets_list_list[i][3].value()
plat_fee_struct[0].append(band)
plat_fee_struct[1].append(fee)
return plat_fee_struct
# Get fee structure variables from user input when "Save" clicked # Get fee structure variables from user input when "Save" clicked
def init_variables(self): def init_variables(self):
# If debugging, save time by hardcoding self.plat.fund_plat_fee = self.create_plat_fee_struct()
if self.autofill: self.plat.share_plat_fee = float(self.share_plat_fee_box.value()) / 100
self.plat_name = "AJBell" self.plat.share_deal_fee = float(self.share_deal_fee_box.value())
self.fund_deal_fee = 1.50
self.share_plat_fee = 0.0025 if self.check_boxes_ticked[0]:
self.share_plat_max_fee = 3.50 self.plat.plat_name = self.plat_name_box.text()
self.share_deal_fee = 5.00
self.share_deal_reduce_trades = 10
self.share_deal_reduce_amount = 3.50
else: else:
self.plat_name = self.plat_name_box.text() self.plat.plat_name = None
self.fund_deal_fee = float(self.fund_deal_fee_box.value())
self.share_plat_fee = float(self.share_plat_fee_box.value()) / 100
self.share_plat_max_fee = float(self.share_plat_max_fee_box.value())
self.share_deal_fee = float(self.share_deal_fee_box.value())
self.share_deal_reduce_trades = float(self.share_deal_reduce_trades_box.value())
self.share_deal_reduce_amount = float(self.share_deal_reduce_amount_box.value())
# Once user input is received show main window if self.check_boxes_ticked[1]:
self.main_win.show() self.plat.fund_deal_fee = float(self.fund_deal_fee_box.value())
else:
self.plat.fund_deal_fee = None
# When focus is given to an input box, select all text in it (easier to edit) if self.check_boxes_ticked[2]:
def eventFilter(self, obj: QObject, event: QEvent): self.plat.share_plat_max_fee = float(self.share_plat_max_fee_box.value())
if event.type() == QEvent.Type.FocusIn: else:
# Alternative condition for % suffix - currently unused self.plat.share_plat_max_fee = None
#if obj.value() == 0 or obj == self.share_plat_fee_box:
QTimer.singleShot(0, obj.selectAll)
return False
# Check if all required fields have valid (non-zero) input if self.check_boxes_ticked[3]:
# TODO: Find a better way of doing this if possible self.plat.share_deal_reduce_trades = int(self.share_deal_reduce_trades_box.value())
else:
self.plat.share_deal_reduce_trades = None
if self.check_boxes_ticked[4]:
self.plat.share_deal_reduce_amount = float(self.share_deal_reduce_amount_box.value())
else:
self.plat.share_deal_reduce_amount = None
self.accept()
# This method does multiple things in order to validate the user's inputs:
# 1) Check all required fields have a non-zero value
# 2) If an optional checkbox is toggled: toggle editing of the corresponding field
# 3) Check all optional fields the user has picked have a non-zero value
# 4) If the above two conditions are met (1 & 3), make the 'Save' button clickable
# 5) Keep a record of which optional fields the user has chosen to fill in
# It's called when an optional check box emits a checkStateChanged() signal
# It's also called when any field emits a textChanged() or valueChanged() signal
def check_valid(self): def check_valid(self):
values = [self.fund_deal_fee_box.value(),
self.share_plat_fee_box.value(),
self.share_deal_fee_box.value()
]
valid = True valid = True
tiers_valid = True
for value in values: # Check all required fields have a non-zero value
if value == 0: for field in self.required_fields:
if field.value() == 0:
valid = False valid = False
if valid: for i in range(len(self.optional_check_boxes)):
# Find the coordinates of the input box corresponding to the checkbox
# It will be on the same row, in the column to the left (-1)
check_box_idx = self.gridLayout.indexOf(self.optional_check_boxes[i])
check_box_pos = self.gridLayout.getItemPosition(check_box_idx)
input_box_pos = list(check_box_pos)[:2]
input_box_pos[1] -= 1
# Return copy of input field widget from its coordinates
input_box_item = self.gridLayout.itemAtPosition(input_box_pos[0], input_box_pos[1]).widget()
if self.optional_check_boxes[i].isChecked():
input_box_item.setEnabled(True)
self.check_boxes_ticked[i] = True
input_box_type = input_box_item.staticMetaObject.className()
if input_box_type == "QLineEdit":
if input_box_item.text() == "":
valid = False
elif input_box_type == "FastEditQDoubleSpinBox" or input_box_type == "FastEditQSpinBox":
if input_box_item.value() == 0:
valid = False
else:
input_box_item.setEnabled(False)
self.check_boxes_ticked[i] = False
if self.first_tier_fee_box.value() == 0:
tiers_valid = False
if self.fund_fee_rows > 1:
if self.widgets_list_list[0][1].value() <= self.first_tier_box.value():
tiers_valid = False
if self.widgets_list_list[0][3].value() == 0:
tiers_valid = False
for i in range(len(self.widgets_list_list) - 1, 0, -1):
if self.widgets_list_list[i][1].value() <= self.widgets_list_list[i-1][1].value():
tiers_valid = False
if self.widgets_list_list[i][3].value() == 0:
tiers_valid = False
if tiers_valid and self.fund_fee_rows < 6:
self.add_row_but.setEnabled(True)
else:
self.add_row_but.setEnabled(False)
if valid and tiers_valid:
self.save_but.setEnabled(True) self.save_but.setEnabled(True)
else: else:
self.save_but.setEnabled(False) self.save_but.setEnabled(False)
# Getter functions (is this necessary? maybe directly reading class vars would be best...) def update_tier_labels(self):
def get_plat_name(self): if self.fund_fee_rows > 1:
return self.plat_name prev_value = self.first_tier_box.value()
self.widgets_list_list[0][0].setText(f"between £{int(prev_value)} and")
def get_fund_plat_fee(self): for i in range(len(self.widgets_list_list) - 1, 0, -1):
return self.fund_plat_fee prev_value = self.widgets_list_list[i-1][1].value()
self.widgets_list_list[i][0].setText(f"between £{int(prev_value)} and")
def get_fund_deal_fee(self): if self.fund_fee_rows > 1:
return self.fund_deal_fee max_band = self.widgets_list_list[self.fund_fee_rows - 2][1].value()
if self.fund_fee_rows > 2:
prev_band = self.widgets_list_list[self.fund_fee_rows - 3][1].value()
else:
prev_band = self.first_tier_box.value()
else:
max_band = self.first_tier_box.value()
prev_band = self.first_tier_box.value()
def get_share_plat_fee(self): if max_band > prev_band:
return self.share_plat_fee self.val_above_lab.setText(f"on the value above £{max_band:.2f} there is no charge")
else:
self.val_above_lab.setText(f"on the value above £{prev_band:.2f} there is no charge")
def get_share_plat_max_fee(self): def add_row(self, loading: bool = False):
return self.share_plat_max_fee if loading:
rows_needed = self.fund_fee_rows - 1
else:
rows_needed = 1
def get_share_deal_fee(self): for x in range(rows_needed):
return self.share_deal_fee widgets = []
font = QFont()
font.setPointSize(11)
def get_share_deal_reduce_trades(self): widgets.append(QLabel(self.gridLayoutWidget_2))
return self.share_deal_reduce_trades widgets[0].setFont(font)
def get_share_deal_reduce_amount(self): widgets.append(FastEditQDoubleSpinBox(self.gridLayoutWidget_2))
return self.share_deal_reduce_amount widgets[1].setPrefix("£")
widgets[1].setMaximum(9999999)
widgets[1].setButtonSymbols(FastEditQDoubleSpinBox.ButtonSymbols.NoButtons)
widgets[1].setFont(font)
if loading:
widgets[1].setValue(self.plat.fund_plat_fee[0][x+2])
widgets[1].valueChanged.connect(self.check_valid)
widgets[1].valueChanged.connect(self.update_tier_labels)
widgets.append(QLabel(self.gridLayoutWidget_2))
widgets[2].setText(f"the fee is")
widgets[2].setFont(font)
widgets.append(FastEditQDoubleSpinBox(self.gridLayoutWidget_2))
widgets[3].setSuffix("%")
widgets[3].setMaximum(100)
widgets[3].setButtonSymbols(FastEditQDoubleSpinBox.ButtonSymbols.NoButtons)
widgets[3].setFont(font)
if loading:
widgets[3].setValue(self.plat.fund_plat_fee[1][x+2])
widgets[3].valueChanged.connect(self.check_valid)
if loading:
grid_height = int(round(28.5 * self.fund_fee_rows))
else:
grid_height = int(round(28.5 * (self.fund_fee_rows + 1)))
self.gridLayoutWidget_2.setGeometry(QRect(19, 307, 591, grid_height))
for i in range(len(widgets)):
if loading:
self.gridLayout_2.addWidget(widgets[i], x + 1, i, 1, 1)
else:
self.gridLayout_2.addWidget(widgets[i], self.fund_fee_rows, i, 1, 1)
if not loading:
self.fund_fee_rows += 1
self.widgets_list_list.append(widgets)
cur_label_idx = self.gridLayout_2.indexOf(widgets[0])
cur_box_idx = self.gridLayout_2.indexOf(widgets[1])
cur_label_pos = list(self.gridLayout_2.getItemPosition(cur_label_idx))[:2]
cur_box_pos = list(self.gridLayout_2.getItemPosition(cur_box_idx))[:2]
prev_box_row = cur_box_pos[0] - 1
prev_box_item = self.gridLayout_2.itemAtPosition(prev_box_row, cur_box_pos[1]).widget()
cur_label_item = self.gridLayout_2.itemAtPosition(cur_label_pos[0], cur_label_pos[1]).widget()
cur_label_item.setText(f"between £{int(prev_box_item.value())} and")
if self.fund_fee_rows > 1:
self.del_row_but.setEnabled(True)
if self.fund_fee_rows > 5:
self.add_row_but.setEnabled(False)
self.check_valid()
self.update_tier_labels()
# TODO: Tab/focus order
def remove_row(self):
for widget in self.widgets_list_list[self.fund_fee_rows - 2]:
self.gridLayout_2.removeWidget(widget)
widget.hide()
self.widgets_list_list.pop()
self.fund_fee_rows -= 1
self.gridLayoutWidget_2.setGeometry(19, 307, 591, int(round(28.5 * self.fund_fee_rows, 0)))
if self.fund_fee_rows < 2:
self.del_row_but.setEnabled(False)
if self.fund_fee_rows < 6:
self.add_row_but.setEnabled(True)
self.check_valid()
self.update_tier_labels()
def closeEvent(self, event):
event.ignore()
self.reject()

185
src/platform_list.py Normal file
View File

@ -0,0 +1,185 @@
from PyQt6 import uic
from PyQt6.QtCore import QRegularExpression
from PyQt6.QtGui import QIcon, QRegularExpressionValidator, QFont
from PyQt6.QtWidgets import QWidget, QListWidgetItem, QDialog, QDialogButtonBox, QMessageBox
import resource_finder
from db_handler import DBHandler
from data_struct import Platform
from platform_edit import PlatformEdit
class PlatformRename(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
# 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")))
self.rename_plat_box.setFocus()
self.new_name = ""
# Set validators
# Regex accepts any characters that match [a-Z], [0-9] or _
self.rename_plat_box.setValidator(
QRegularExpressionValidator(QRegularExpression("\\w*"))
)
self.rename_plat_ok_but.clicked.connect(self.store_new_name)
def store_new_name(self):
self.new_name = self.rename_plat_box.text()
def closeEvent(self, event):
event.ignore()
self.reject()
class RemoveConfirm(QMessageBox):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico")))
self.setWindowTitle("Remove platform?")
self.setIcon(QMessageBox.Icon.Warning)
font = QFont()
font.setPointSize(11)
self.setFont(font)
self.setText("Are you sure you want to remove this platform?")
self.setInformativeText("This action is immediate and permanent")
self.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.Cancel)
self.setDefaultButton(QMessageBox.StandardButton.Cancel)
font.setPointSize(10)
self.findChild(QDialogButtonBox).setFont(font)
class PlatformList(QWidget):
def __init__(self, db: DBHandler):
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")))
# Initialise class variables
self.db = db
self.plat_edit_win = None
self.plat_list_dialog = None
self.del_plat_dialog = RemoveConfirm()
self.plat_list = []
self.plat_name_list = []
self.new_plat_name = ""
self.update_plat_list()
self.db_indices = [x for x in range(len(self.plat_name_list))]
# Handle events
self.add_plat_but.clicked.connect(self.add_platform)
self.del_plat_but.clicked.connect(self.remove_platform)
self.edit_plat_but.clicked.connect(self.edit_platform)
self.plist_save_but.clicked.connect(self.save_platforms)
self.plat_enabled_check.checkStateChanged.connect(self.toggle_platform_state)
self.platListWidget.currentRowChanged.connect(self.get_enabled_state)
def update_plat_list(self):
self.plat_name_list = self.db.retrieve_plat_list()
self.plat_list = self.db.retrieve_platforms()
self.platListWidget.clear()
for i in range(len(self.plat_name_list)):
plat_name = self.plat_name_list[i]
item = QListWidgetItem()
if plat_name is not None:
item.setText(plat_name)
else:
item.setText(f"Unnamed [ID: {i}]")
self.platListWidget.addItem(item)
self.platListWidget.setCurrentRow(0)
def add_platform(self):
self.plat_list_dialog = PlatformRename()
name_dialog_res = self.plat_list_dialog.exec()
if name_dialog_res == QDialog.DialogCode.Accepted:
name = self.plat_list_dialog.new_name
index = self.platListWidget.count()
if name != "":
self.platListWidget.addItem(name)
name_param = name
else:
self.platListWidget.addItem(f"Unnamed [ID: {index}]")
name_param = None
self.plat_list.append(Platform(
index, [[0], [0]], name_param, True, 0, 0, None, 0, None, None)
)
self.plat_edit_win = PlatformEdit(self.plat_list[index])
plat_edit_res = self.plat_edit_win.exec()
if plat_edit_res == QDialog.DialogCode.Rejected:
self.plat_list.pop()
self.platListWidget.takeItem(self.platListWidget.count() - 1)
self.platListWidget.setCurrentRow(index - 1)
else:
index = len(self.plat_list) - 1
if self.plat_list[index].plat_name is not None:
item = self.platListWidget.takeItem(index)
item.setText(self.plat_list[index].plat_name)
self.platListWidget.insertItem(index, item)
self.platListWidget.setCurrentRow(index)
def get_enabled_state(self):
index = self.platListWidget.currentRow()
if len(self.plat_list) > 0:
is_enabled = self.plat_list[index].enabled
if is_enabled:
self.plat_enabled_check.setChecked(True)
else:
self.plat_enabled_check.setChecked(False)
else:
self.plat_enabled_check.setChecked(False)
if index in self.db_indices:
self.del_plat_but.setEnabled(True)
else:
self.del_plat_but.setEnabled(False)
valid_selection = False
for plat in self.plat_list:
if plat.plat_id == index:
valid_selection = True
if valid_selection:
self.edit_plat_but.setEnabled(True)
self.plat_enabled_check.setEnabled(True)
else:
self.edit_plat_but.setEnabled(False)
self.plat_enabled_check.setEnabled(False)
def edit_platform(self):
index = self.platListWidget.currentRow()
self.plat_edit_win = PlatformEdit(self.plat_list[index])
plat_edit_res = self.plat_edit_win.exec()
if plat_edit_res == QDialog.DialogCode.Accepted:
item = self.platListWidget.takeItem(index)
item.setText(self.plat_list[index].plat_name)
self.platListWidget.insertItem(index, item)
self.platListWidget.setCurrentRow(index)
def save_platforms(self):
self.db.write_platforms(self.plat_list)
self.update_plat_list()
self.db_indices = [x for x in range(len(self.plat_name_list))]
def toggle_platform_state(self):
index = self.platListWidget.currentRow()
state = self.plat_enabled_check.isChecked()
if len(self.plat_list) > 0 and index >= 0:
self.plat_list[index].enabled = state
def remove_platform(self):
index = self.platListWidget.currentRow()
del_dialog_res = self.del_plat_dialog.exec()
if del_dialog_res == QMessageBox.StandardButton.Yes:
self.db.remove_platform(index)
self.update_plat_list()
def showEvent(self, event):
self.get_enabled_state()

25
src/resource_finder.py Normal file
View File

@ -0,0 +1,25 @@
import os.path
import sys
# Returns the correct path for Nuitka onefile mode, standalone mode or normal Python
# Credit: https://nuitka.net/user-documentation/common-issue-solutions.html#onefile-finding-files
def get_res_path(relative_path):
path_a = ""
try:
path_a = os.path.join(sys.__compiled__.containing_dir, relative_path)
except AttributeError:
pass
path_b = os.path.join(os.path.dirname(__file__), relative_path)
path_c = os.path.join(os.path.dirname(sys.argv[0]), relative_path)
if os.path.isfile(path_a):
return path_a
elif os.path.isfile(path_b):
return path_b
elif os.path.isfile(path_c):
return path_c
else:
return os.path.join(os.path.abspath("."), relative_path)

View File

@ -0,0 +1,14 @@
from PyQt6.QtCore import QTimer
from PyQt6.QtWidgets import QSpinBox, QDoubleSpinBox
class FastEditQDoubleSpinBox(QDoubleSpinBox):
def focusInEvent(self, e):
QTimer.singleShot(0, self.selectAll)
super(FastEditQDoubleSpinBox, self).focusInEvent(e)
class FastEditQSpinBox(QSpinBox):
def focusInEvent(self, e):
QTimer.singleShot(0, self.selectAll)
super(FastEditQSpinBox, self).focusInEvent(e)

13
src/widgets/mpl_widget.py Normal file
View File

@ -0,0 +1,13 @@
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
from matplotlib.figure import Figure
from PyQt6.QtWidgets import QWidget, QVBoxLayout
class MplWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.canvas = FigureCanvasQTAgg(Figure(figsize=(15, 8), dpi=100))
vertical_layout = QVBoxLayout()
vertical_layout.addWidget(self.canvas)
self.canvas.axes = self.canvas.figure.add_subplot(1, 1, 1)
self.setLayout(vertical_layout)