From afc31c6611020b6362b71ae323bb73c609504acf Mon Sep 17 00:00:00 2001 From: eddyizm Date: Sun, 4 May 2025 13:43:26 -0700 Subject: [PATCH 01/16] chore: initial set up of briefcase/toga framework --- Docs/assets/BuyMeACoffee.png | Bin 0 -> 5433 bytes Docs/assets/ffe.ico | Bin 0 -> 67646 bytes FlatFileExporter/.gitignore | 62 ++++++ FlatFileExporter/CHANGELOG | 5 + FlatFileExporter/LICENSE | 19 ++ FlatFileExporter/README.rst | 12 ++ FlatFileExporter/pyproject.toml | 184 ++++++++++++++++++ .../src/flatfileexporter/__init__.py | 0 .../src/flatfileexporter/__main__.py | 4 + FlatFileExporter/src/flatfileexporter/app.py | 26 +++ .../src/flatfileexporter/resources/README | 2 + FlatFileExporter/tests/__init__.py | 0 FlatFileExporter/tests/flatfileexporter.py | 35 ++++ FlatFileExporter/tests/test_app.py | 3 + 14 files changed, 352 insertions(+) create mode 100644 Docs/assets/BuyMeACoffee.png create mode 100644 Docs/assets/ffe.ico create mode 100644 FlatFileExporter/.gitignore create mode 100644 FlatFileExporter/CHANGELOG create mode 100644 FlatFileExporter/LICENSE create mode 100644 FlatFileExporter/README.rst create mode 100644 FlatFileExporter/pyproject.toml create mode 100644 FlatFileExporter/src/flatfileexporter/__init__.py create mode 100644 FlatFileExporter/src/flatfileexporter/__main__.py create mode 100644 FlatFileExporter/src/flatfileexporter/app.py create mode 100644 FlatFileExporter/src/flatfileexporter/resources/README create mode 100644 FlatFileExporter/tests/__init__.py create mode 100644 FlatFileExporter/tests/flatfileexporter.py create mode 100644 FlatFileExporter/tests/test_app.py diff --git a/Docs/assets/BuyMeACoffee.png b/Docs/assets/BuyMeACoffee.png new file mode 100644 index 0000000000000000000000000000000000000000..f72203b66cbb8503f6360aeba80ebc7194ce4dbb GIT binary patch literal 5433 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 HK|cQnBZF-5 literal 0 HcmV?d00001 diff --git a/FlatFileExporter/.gitignore b/FlatFileExporter/.gitignore new file mode 100644 index 0000000..f6c30e2 --- /dev/null +++ b/FlatFileExporter/.gitignore @@ -0,0 +1,62 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# OSX useful to ignore +*.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.dist-info/ +*.egg-info/ +.installed.cfg +*.egg + +# IntelliJ Idea family of suites +.idea +*.iml +## File-based project format: +*.ipr +*.iws +## mpeltonen/sbt-idea plugin +.idea_modules/ + +# Briefcase log files +logs/ diff --git a/FlatFileExporter/CHANGELOG b/FlatFileExporter/CHANGELOG new file mode 100644 index 0000000..345fa5f --- /dev/null +++ b/FlatFileExporter/CHANGELOG @@ -0,0 +1,5 @@ +# Flat File Exporter Release Notes + +## 0.0.1 (04 May 2025) + +* Initial release diff --git a/FlatFileExporter/LICENSE b/FlatFileExporter/LICENSE new file mode 100644 index 0000000..08fae05 --- /dev/null +++ b/FlatFileExporter/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2025, eduardo cervantes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/FlatFileExporter/README.rst b/FlatFileExporter/README.rst new file mode 100644 index 0000000..07ff50f --- /dev/null +++ b/FlatFileExporter/README.rst @@ -0,0 +1,12 @@ +Flat File Exporter +================== + +**This cross-platform app was generated by** `Briefcase`_ **- part of** +`The BeeWare Project`_. **If you want to see more tools like Briefcase, please +consider** `becoming a financial member of BeeWare`_. + +Using a sql script to export flat files. + +.. _`Briefcase`: https://briefcase.readthedocs.io/ +.. _`The BeeWare Project`: https://beeware.org/ +.. _`becoming a financial member of BeeWare`: https://beeware.org/contributing/membership diff --git a/FlatFileExporter/pyproject.toml b/FlatFileExporter/pyproject.toml new file mode 100644 index 0000000..6375242 --- /dev/null +++ b/FlatFileExporter/pyproject.toml @@ -0,0 +1,184 @@ +# This project was generated with 0.3.22 using template: https://github.com/beeware/briefcase-template@v0.3.22 +[tool.briefcase] +project_name = "Flat File Exporter" +bundle = "com.eddyizm" +version = "0.0.1" +url = "https://eddyizm.com/flatfileexporter" +license.file = "LICENSE" +author = "eduardo cervantes" +author_email = "me@eddyizm.com" + +[tool.briefcase.app.flatfileexporter] +formal_name = "Flat File Exporter" +description = "Using a sql script to export flat files." +long_description = """More details about the app should go here. +""" +sources = [ + "src/flatfileexporter", +] +test_sources = [ + "tests", +] + +requires = [ +] +test_requires = [ + "pytest", +] + +[tool.briefcase.app.flatfileexporter.macOS] +universal_build = true +requires = [ + "toga-cocoa~=0.4.7", + "std-nslog~=1.0.3", +] + +[tool.briefcase.app.flatfileexporter.linux] +requires = [ + "toga-gtk~=0.4.7", +] + +[tool.briefcase.app.flatfileexporter.linux.system.debian] +system_requires = [ + # Needed to compile pycairo wheel + "libcairo2-dev", + # Needed to compile PyGObject wheel + "libgirepository1.0-dev", +] + +system_runtime_requires = [ + # Needed to provide GTK and its GI bindings + "gir1.2-gtk-3.0", + "libgirepository-1.0-1", + # Dependencies that GTK looks for at runtime + "libcanberra-gtk3-module", + # Needed to provide WebKit2 at runtime + # Note: Debian 11 and Ubuntu 20.04 require gir1.2-webkit2-4.0 instead + # "gir1.2-webkit2-4.1", +] + +[tool.briefcase.app.flatfileexporter.linux.system.rhel] +system_requires = [ + # Needed to compile pycairo wheel + "cairo-gobject-devel", + # Needed to compile PyGObject wheel + "gobject-introspection-devel", +] + +system_runtime_requires = [ + # Needed to support Python bindings to GTK + "gobject-introspection", + # Needed to provide GTK + "gtk3", + # Dependencies that GTK looks for at runtime + "libcanberra-gtk3", + # Needed to provide WebKit2 at runtime + # "webkit2gtk3", +] + +[tool.briefcase.app.flatfileexporter.linux.system.suse] +system_requires = [ + # Needed to compile pycairo wheel + "cairo-devel", + # Needed to compile PyGObject wheel + "gobject-introspection-devel", +] + +system_runtime_requires = [ + # Needed to provide GTK + "gtk3", + # Needed to support Python bindings to GTK + "gobject-introspection", "typelib(Gtk) = 3.0", + # Dependencies that GTK looks for at runtime + "libcanberra-gtk3-module", + # Needed to provide WebKit2 at runtime + # "libwebkit2gtk3", "typelib(WebKit2)", +] + +[tool.briefcase.app.flatfileexporter.linux.system.arch] +system_requires = [ + # Needed to compile pycairo wheel + "cairo", + # Needed to compile PyGObject wheel + "gobject-introspection", + # Runtime dependencies that need to exist so that the + # Arch package passes final validation. + # Needed to provide GTK + "gtk3", + # Dependencies that GTK looks for at runtime + "libcanberra", + # Needed to provide WebKit2 + # "webkit2gtk", +] + +system_runtime_requires = [ + # Needed to provide GTK + "gtk3", + # Needed to provide PyGObject bindings + "gobject-introspection-runtime", + # Dependencies that GTK looks for at runtime + "libcanberra", + # Needed to provide WebKit2 at runtime + # "webkit2gtk", +] + +[tool.briefcase.app.flatfileexporter.linux.appimage] +manylinux = "manylinux_2_28" + +system_requires = [ + # Needed to compile pycairo wheel + "cairo-gobject-devel", + # Needed to compile PyGObject wheel + "gobject-introspection-devel", + # Needed to provide GTK + "gtk3-devel", + # Dependencies that GTK looks for at runtime, that need to be + # in the build environment to be picked up by linuxdeploy + "libcanberra-gtk3", + "PackageKit-gtk3-module", + "gvfs-client", +] + +linuxdeploy_plugins = [ + "DEPLOY_GTK_VERSION=3 gtk", +] + +[tool.briefcase.app.flatfileexporter.linux.flatpak] +flatpak_runtime = "org.gnome.Platform" +flatpak_runtime_version = "47" +flatpak_sdk = "org.gnome.Sdk" + +[tool.briefcase.app.flatfileexporter.windows] +requires = [ + "toga-winforms~=0.4.7", +] + +# Mobile deployments +[tool.briefcase.app.flatfileexporter.iOS] +requires = [ + "toga-iOS~=0.4.7", + "std-nslog~=1.0.3", +] + +[tool.briefcase.app.flatfileexporter.android] +requires = [ + "toga-android~=0.4.7", +] + +base_theme = "Theme.MaterialComponents.Light.DarkActionBar" + +build_gradle_dependencies = [ + "com.google.android.material:material:1.12.0", + # Needed for DetailedList + # "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0", + # Needed for MapView + # "org.osmdroid:osmdroid-android:6.1.20", +] + +# Web deployments +[tool.briefcase.app.flatfileexporter.web] +requires = [ + "toga-web~=0.4.7", +] +style_framework = "Shoelace v2.3" + diff --git a/FlatFileExporter/src/flatfileexporter/__init__.py b/FlatFileExporter/src/flatfileexporter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/FlatFileExporter/src/flatfileexporter/__main__.py b/FlatFileExporter/src/flatfileexporter/__main__.py new file mode 100644 index 0000000..fe6a528 --- /dev/null +++ b/FlatFileExporter/src/flatfileexporter/__main__.py @@ -0,0 +1,4 @@ +from flatfileexporter.app import main + +if __name__ == "__main__": + main().main_loop() diff --git a/FlatFileExporter/src/flatfileexporter/app.py b/FlatFileExporter/src/flatfileexporter/app.py new file mode 100644 index 0000000..ee6ad29 --- /dev/null +++ b/FlatFileExporter/src/flatfileexporter/app.py @@ -0,0 +1,26 @@ +""" +Using a sql script to export flat files. +""" + +import toga +from toga.style import Pack +from toga.style.pack import COLUMN, ROW + + +class FlatFileExporter(toga.App): + def startup(self): + """Construct and show the Toga application. + + Usually, you would add your application to a main content box. + We then create a main window (with a name matching the app), and + show the main window. + """ + main_box = toga.Box() + + self.main_window = toga.MainWindow(title=self.formal_name) + self.main_window.content = main_box + self.main_window.show() + + +def main(): + return FlatFileExporter() diff --git a/FlatFileExporter/src/flatfileexporter/resources/README b/FlatFileExporter/src/flatfileexporter/resources/README new file mode 100644 index 0000000..4ef2794 --- /dev/null +++ b/FlatFileExporter/src/flatfileexporter/resources/README @@ -0,0 +1,2 @@ +Put any application resources (e.g., icons and resources) here; +they can be referenced in code as "resources/filename". diff --git a/FlatFileExporter/tests/__init__.py b/FlatFileExporter/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/FlatFileExporter/tests/flatfileexporter.py b/FlatFileExporter/tests/flatfileexporter.py new file mode 100644 index 0000000..d59e9f3 --- /dev/null +++ b/FlatFileExporter/tests/flatfileexporter.py @@ -0,0 +1,35 @@ +import os +import sys +import tempfile +from pathlib import Path + +import pytest + + +def run_tests(): + project_path = Path(__file__).parent.parent + os.chdir(project_path) + + # Determine any args to pass to pytest. If there aren't any, + # default to running the whole test suite. + args = sys.argv[1:] + if len(args) == 0: + args = ["tests"] + + returncode = pytest.main( + [ + # Turn up verbosity + "-vv", + # Disable color + "--color=no", + # Overwrite the cache directory to somewhere writable + "-o", + f"cache_dir={tempfile.gettempdir()}/.pytest_cache", + ] + args + ) + + print(f">>>>>>>>>> EXIT {returncode} <<<<<<<<<<") + + +if __name__ == "__main__": + run_tests() diff --git a/FlatFileExporter/tests/test_app.py b/FlatFileExporter/tests/test_app.py new file mode 100644 index 0000000..e1a335f --- /dev/null +++ b/FlatFileExporter/tests/test_app.py @@ -0,0 +1,3 @@ +def test_first(): + """An initial test for the app.""" + assert 1 + 1 == 2 From e1840c5a03099af0d6e01477023893c609f88a14 Mon Sep 17 00:00:00 2001 From: eddyizm Date: Sun, 4 May 2025 13:55:25 -0700 Subject: [PATCH 02/16] chore: cleaning up deleted files --- Docs/assets/BuyMeACoffee.png | Bin 5433 -> 0 bytes Docs/assets/ffe.ico | Bin 67646 -> 0 bytes FlatFileExporter/.gitignore | 62 ------ FlatFileExporter/CHANGELOG | 5 - FlatFileExporter/LICENSE | 19 -- FlatFileExporter/README.rst | 12 -- FlatFileExporter/pyproject.toml | 184 ------------------ .../src/flatfileexporter/__init__.py | 0 .../src/flatfileexporter/__main__.py | 4 - FlatFileExporter/src/flatfileexporter/app.py | 26 --- .../src/flatfileexporter/resources/README | 2 - FlatFileExporter/tests/__init__.py | 0 FlatFileExporter/tests/flatfileexporter.py | 35 ---- FlatFileExporter/tests/test_app.py | 3 - 14 files changed, 352 deletions(-) delete mode 100644 Docs/assets/BuyMeACoffee.png delete mode 100644 Docs/assets/ffe.ico delete mode 100644 FlatFileExporter/.gitignore delete mode 100644 FlatFileExporter/CHANGELOG delete mode 100644 FlatFileExporter/LICENSE delete mode 100644 FlatFileExporter/README.rst delete mode 100644 FlatFileExporter/pyproject.toml delete mode 100644 FlatFileExporter/src/flatfileexporter/__init__.py delete mode 100644 FlatFileExporter/src/flatfileexporter/__main__.py delete mode 100644 FlatFileExporter/src/flatfileexporter/app.py delete mode 100644 FlatFileExporter/src/flatfileexporter/resources/README delete mode 100644 FlatFileExporter/tests/__init__.py delete mode 100644 FlatFileExporter/tests/flatfileexporter.py delete mode 100644 FlatFileExporter/tests/test_app.py diff --git a/Docs/assets/BuyMeACoffee.png b/Docs/assets/BuyMeACoffee.png deleted file mode 100644 index f72203b66cbb8503f6360aeba80ebc7194ce4dbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5433 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 HK|cQnBZF-5 diff --git a/FlatFileExporter/.gitignore b/FlatFileExporter/.gitignore deleted file mode 100644 index f6c30e2..0000000 --- a/FlatFileExporter/.gitignore +++ /dev/null @@ -1,62 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# OSX useful to ignore -*.DS_Store -.AppleDouble -.LSOverride - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.dist-info/ -*.egg-info/ -.installed.cfg -*.egg - -# IntelliJ Idea family of suites -.idea -*.iml -## File-based project format: -*.ipr -*.iws -## mpeltonen/sbt-idea plugin -.idea_modules/ - -# Briefcase log files -logs/ diff --git a/FlatFileExporter/CHANGELOG b/FlatFileExporter/CHANGELOG deleted file mode 100644 index 345fa5f..0000000 --- a/FlatFileExporter/CHANGELOG +++ /dev/null @@ -1,5 +0,0 @@ -# Flat File Exporter Release Notes - -## 0.0.1 (04 May 2025) - -* Initial release diff --git a/FlatFileExporter/LICENSE b/FlatFileExporter/LICENSE deleted file mode 100644 index 08fae05..0000000 --- a/FlatFileExporter/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2025, eduardo cervantes - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/FlatFileExporter/README.rst b/FlatFileExporter/README.rst deleted file mode 100644 index 07ff50f..0000000 --- a/FlatFileExporter/README.rst +++ /dev/null @@ -1,12 +0,0 @@ -Flat File Exporter -================== - -**This cross-platform app was generated by** `Briefcase`_ **- part of** -`The BeeWare Project`_. **If you want to see more tools like Briefcase, please -consider** `becoming a financial member of BeeWare`_. - -Using a sql script to export flat files. - -.. _`Briefcase`: https://briefcase.readthedocs.io/ -.. _`The BeeWare Project`: https://beeware.org/ -.. _`becoming a financial member of BeeWare`: https://beeware.org/contributing/membership diff --git a/FlatFileExporter/pyproject.toml b/FlatFileExporter/pyproject.toml deleted file mode 100644 index 6375242..0000000 --- a/FlatFileExporter/pyproject.toml +++ /dev/null @@ -1,184 +0,0 @@ -# This project was generated with 0.3.22 using template: https://github.com/beeware/briefcase-template@v0.3.22 -[tool.briefcase] -project_name = "Flat File Exporter" -bundle = "com.eddyizm" -version = "0.0.1" -url = "https://eddyizm.com/flatfileexporter" -license.file = "LICENSE" -author = "eduardo cervantes" -author_email = "me@eddyizm.com" - -[tool.briefcase.app.flatfileexporter] -formal_name = "Flat File Exporter" -description = "Using a sql script to export flat files." -long_description = """More details about the app should go here. -""" -sources = [ - "src/flatfileexporter", -] -test_sources = [ - "tests", -] - -requires = [ -] -test_requires = [ - "pytest", -] - -[tool.briefcase.app.flatfileexporter.macOS] -universal_build = true -requires = [ - "toga-cocoa~=0.4.7", - "std-nslog~=1.0.3", -] - -[tool.briefcase.app.flatfileexporter.linux] -requires = [ - "toga-gtk~=0.4.7", -] - -[tool.briefcase.app.flatfileexporter.linux.system.debian] -system_requires = [ - # Needed to compile pycairo wheel - "libcairo2-dev", - # Needed to compile PyGObject wheel - "libgirepository1.0-dev", -] - -system_runtime_requires = [ - # Needed to provide GTK and its GI bindings - "gir1.2-gtk-3.0", - "libgirepository-1.0-1", - # Dependencies that GTK looks for at runtime - "libcanberra-gtk3-module", - # Needed to provide WebKit2 at runtime - # Note: Debian 11 and Ubuntu 20.04 require gir1.2-webkit2-4.0 instead - # "gir1.2-webkit2-4.1", -] - -[tool.briefcase.app.flatfileexporter.linux.system.rhel] -system_requires = [ - # Needed to compile pycairo wheel - "cairo-gobject-devel", - # Needed to compile PyGObject wheel - "gobject-introspection-devel", -] - -system_runtime_requires = [ - # Needed to support Python bindings to GTK - "gobject-introspection", - # Needed to provide GTK - "gtk3", - # Dependencies that GTK looks for at runtime - "libcanberra-gtk3", - # Needed to provide WebKit2 at runtime - # "webkit2gtk3", -] - -[tool.briefcase.app.flatfileexporter.linux.system.suse] -system_requires = [ - # Needed to compile pycairo wheel - "cairo-devel", - # Needed to compile PyGObject wheel - "gobject-introspection-devel", -] - -system_runtime_requires = [ - # Needed to provide GTK - "gtk3", - # Needed to support Python bindings to GTK - "gobject-introspection", "typelib(Gtk) = 3.0", - # Dependencies that GTK looks for at runtime - "libcanberra-gtk3-module", - # Needed to provide WebKit2 at runtime - # "libwebkit2gtk3", "typelib(WebKit2)", -] - -[tool.briefcase.app.flatfileexporter.linux.system.arch] -system_requires = [ - # Needed to compile pycairo wheel - "cairo", - # Needed to compile PyGObject wheel - "gobject-introspection", - # Runtime dependencies that need to exist so that the - # Arch package passes final validation. - # Needed to provide GTK - "gtk3", - # Dependencies that GTK looks for at runtime - "libcanberra", - # Needed to provide WebKit2 - # "webkit2gtk", -] - -system_runtime_requires = [ - # Needed to provide GTK - "gtk3", - # Needed to provide PyGObject bindings - "gobject-introspection-runtime", - # Dependencies that GTK looks for at runtime - "libcanberra", - # Needed to provide WebKit2 at runtime - # "webkit2gtk", -] - -[tool.briefcase.app.flatfileexporter.linux.appimage] -manylinux = "manylinux_2_28" - -system_requires = [ - # Needed to compile pycairo wheel - "cairo-gobject-devel", - # Needed to compile PyGObject wheel - "gobject-introspection-devel", - # Needed to provide GTK - "gtk3-devel", - # Dependencies that GTK looks for at runtime, that need to be - # in the build environment to be picked up by linuxdeploy - "libcanberra-gtk3", - "PackageKit-gtk3-module", - "gvfs-client", -] - -linuxdeploy_plugins = [ - "DEPLOY_GTK_VERSION=3 gtk", -] - -[tool.briefcase.app.flatfileexporter.linux.flatpak] -flatpak_runtime = "org.gnome.Platform" -flatpak_runtime_version = "47" -flatpak_sdk = "org.gnome.Sdk" - -[tool.briefcase.app.flatfileexporter.windows] -requires = [ - "toga-winforms~=0.4.7", -] - -# Mobile deployments -[tool.briefcase.app.flatfileexporter.iOS] -requires = [ - "toga-iOS~=0.4.7", - "std-nslog~=1.0.3", -] - -[tool.briefcase.app.flatfileexporter.android] -requires = [ - "toga-android~=0.4.7", -] - -base_theme = "Theme.MaterialComponents.Light.DarkActionBar" - -build_gradle_dependencies = [ - "com.google.android.material:material:1.12.0", - # Needed for DetailedList - # "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0", - # Needed for MapView - # "org.osmdroid:osmdroid-android:6.1.20", -] - -# Web deployments -[tool.briefcase.app.flatfileexporter.web] -requires = [ - "toga-web~=0.4.7", -] -style_framework = "Shoelace v2.3" - diff --git a/FlatFileExporter/src/flatfileexporter/__init__.py b/FlatFileExporter/src/flatfileexporter/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/FlatFileExporter/src/flatfileexporter/__main__.py b/FlatFileExporter/src/flatfileexporter/__main__.py deleted file mode 100644 index fe6a528..0000000 --- a/FlatFileExporter/src/flatfileexporter/__main__.py +++ /dev/null @@ -1,4 +0,0 @@ -from flatfileexporter.app import main - -if __name__ == "__main__": - main().main_loop() diff --git a/FlatFileExporter/src/flatfileexporter/app.py b/FlatFileExporter/src/flatfileexporter/app.py deleted file mode 100644 index ee6ad29..0000000 --- a/FlatFileExporter/src/flatfileexporter/app.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -Using a sql script to export flat files. -""" - -import toga -from toga.style import Pack -from toga.style.pack import COLUMN, ROW - - -class FlatFileExporter(toga.App): - def startup(self): - """Construct and show the Toga application. - - Usually, you would add your application to a main content box. - We then create a main window (with a name matching the app), and - show the main window. - """ - main_box = toga.Box() - - self.main_window = toga.MainWindow(title=self.formal_name) - self.main_window.content = main_box - self.main_window.show() - - -def main(): - return FlatFileExporter() diff --git a/FlatFileExporter/src/flatfileexporter/resources/README b/FlatFileExporter/src/flatfileexporter/resources/README deleted file mode 100644 index 4ef2794..0000000 --- a/FlatFileExporter/src/flatfileexporter/resources/README +++ /dev/null @@ -1,2 +0,0 @@ -Put any application resources (e.g., icons and resources) here; -they can be referenced in code as "resources/filename". diff --git a/FlatFileExporter/tests/__init__.py b/FlatFileExporter/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/FlatFileExporter/tests/flatfileexporter.py b/FlatFileExporter/tests/flatfileexporter.py deleted file mode 100644 index d59e9f3..0000000 --- a/FlatFileExporter/tests/flatfileexporter.py +++ /dev/null @@ -1,35 +0,0 @@ -import os -import sys -import tempfile -from pathlib import Path - -import pytest - - -def run_tests(): - project_path = Path(__file__).parent.parent - os.chdir(project_path) - - # Determine any args to pass to pytest. If there aren't any, - # default to running the whole test suite. - args = sys.argv[1:] - if len(args) == 0: - args = ["tests"] - - returncode = pytest.main( - [ - # Turn up verbosity - "-vv", - # Disable color - "--color=no", - # Overwrite the cache directory to somewhere writable - "-o", - f"cache_dir={tempfile.gettempdir()}/.pytest_cache", - ] + args - ) - - print(f">>>>>>>>>> EXIT {returncode} <<<<<<<<<<") - - -if __name__ == "__main__": - run_tests() diff --git a/FlatFileExporter/tests/test_app.py b/FlatFileExporter/tests/test_app.py deleted file mode 100644 index e1a335f..0000000 --- a/FlatFileExporter/tests/test_app.py +++ /dev/null @@ -1,3 +0,0 @@ -def test_first(): - """An initial test for the app.""" - assert 1 + 1 == 2 From 33d855b89da7936e94e0e74db038457371e34ae6 Mon Sep 17 00:00:00 2001 From: eddyizm Date: Sun, 4 May 2025 14:29:33 -0700 Subject: [PATCH 03/16] chore: moved folder structure --- .../AboutPage.xaml | 0 .../AboutPage.xaml.cs | 0 .../App.config | 0 .../App.xaml | 0 .../App.xaml.cs | 0 .../CredentialsPage.xaml | 0 .../CredentialsPage.xaml.cs | 0 .../DatabasePage.xaml | 0 .../DatabasePage.xaml.cs | 0 .../FlatFileExporter.csproj | 0 .../MainPage.xaml | 0 .../MainPage.xaml.cs | 0 .../MainWindow.xaml | 0 .../MainWindow.xaml.cs | 0 .../Models/VersionCheck.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../Properties/Resources.Designer.cs | 0 .../Properties/Resources.resx | 0 .../Properties/Settings.Designer.cs | 0 .../Properties/Settings.settings | 0 .../Resources/BuyMeACoffee.png | Bin .../ServerPage.xaml | 0 .../ServerPage.xaml.cs | 0 .../ffe.ico | Bin .../packages.config | 0 FlatFileExporter.sln | 25 ------------------ .../src}/PythonCodeBase/DAL.py | 0 .../src}/PythonCodeBase/__init__.py | 0 .../src}/PythonCodeBase/flatfile_cli.py | 0 .../src}/PythonCodeBase/flatfile_cli.txt | 0 .../src}/PythonCodeBase/gui/counter.py | 0 .../src}/PythonCodeBase/gui/pubsub_sample.py | 0 .../PythonCodeBase/gui/routing_example.py | 0 .../src}/PythonCodeBase/gui/user_control.py | 0 .../src}/PythonCodeBase/requirements.txt | 0 35 files changed, 25 deletions(-) rename {FlatFileExporter => FlatFileExporter.OLD}/AboutPage.xaml (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/AboutPage.xaml.cs (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/App.config (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/App.xaml (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/App.xaml.cs (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/CredentialsPage.xaml (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/CredentialsPage.xaml.cs (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/DatabasePage.xaml (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/DatabasePage.xaml.cs (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/FlatFileExporter.csproj (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/MainPage.xaml (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/MainPage.xaml.cs (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/MainWindow.xaml (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/MainWindow.xaml.cs (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/Models/VersionCheck.cs (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/Properties/AssemblyInfo.cs (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/Properties/Resources.Designer.cs (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/Properties/Resources.resx (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/Properties/Settings.Designer.cs (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/Properties/Settings.settings (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/Resources/BuyMeACoffee.png (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/ServerPage.xaml (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/ServerPage.xaml.cs (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/ffe.ico (100%) rename {FlatFileExporter => FlatFileExporter.OLD}/packages.config (100%) delete mode 100644 FlatFileExporter.sln rename {FlatFileExporter => flatfileexporter/src}/PythonCodeBase/DAL.py (100%) rename {FlatFileExporter => flatfileexporter/src}/PythonCodeBase/__init__.py (100%) rename {FlatFileExporter => flatfileexporter/src}/PythonCodeBase/flatfile_cli.py (100%) rename {FlatFileExporter => flatfileexporter/src}/PythonCodeBase/flatfile_cli.txt (100%) rename {FlatFileExporter => flatfileexporter/src}/PythonCodeBase/gui/counter.py (100%) rename {FlatFileExporter => flatfileexporter/src}/PythonCodeBase/gui/pubsub_sample.py (100%) rename {FlatFileExporter => flatfileexporter/src}/PythonCodeBase/gui/routing_example.py (100%) rename {FlatFileExporter => flatfileexporter/src}/PythonCodeBase/gui/user_control.py (100%) rename {FlatFileExporter => flatfileexporter/src}/PythonCodeBase/requirements.txt (100%) diff --git a/FlatFileExporter/AboutPage.xaml b/FlatFileExporter.OLD/AboutPage.xaml similarity index 100% rename from FlatFileExporter/AboutPage.xaml rename to FlatFileExporter.OLD/AboutPage.xaml diff --git a/FlatFileExporter/AboutPage.xaml.cs b/FlatFileExporter.OLD/AboutPage.xaml.cs similarity index 100% rename from FlatFileExporter/AboutPage.xaml.cs rename to FlatFileExporter.OLD/AboutPage.xaml.cs diff --git a/FlatFileExporter/App.config b/FlatFileExporter.OLD/App.config similarity index 100% rename from FlatFileExporter/App.config rename to FlatFileExporter.OLD/App.config diff --git a/FlatFileExporter/App.xaml b/FlatFileExporter.OLD/App.xaml similarity index 100% rename from FlatFileExporter/App.xaml rename to FlatFileExporter.OLD/App.xaml diff --git a/FlatFileExporter/App.xaml.cs b/FlatFileExporter.OLD/App.xaml.cs similarity index 100% rename from FlatFileExporter/App.xaml.cs rename to FlatFileExporter.OLD/App.xaml.cs diff --git a/FlatFileExporter/CredentialsPage.xaml b/FlatFileExporter.OLD/CredentialsPage.xaml similarity index 100% rename from FlatFileExporter/CredentialsPage.xaml rename to FlatFileExporter.OLD/CredentialsPage.xaml diff --git a/FlatFileExporter/CredentialsPage.xaml.cs b/FlatFileExporter.OLD/CredentialsPage.xaml.cs similarity index 100% rename from FlatFileExporter/CredentialsPage.xaml.cs rename to FlatFileExporter.OLD/CredentialsPage.xaml.cs diff --git a/FlatFileExporter/DatabasePage.xaml b/FlatFileExporter.OLD/DatabasePage.xaml similarity index 100% rename from FlatFileExporter/DatabasePage.xaml rename to FlatFileExporter.OLD/DatabasePage.xaml diff --git a/FlatFileExporter/DatabasePage.xaml.cs b/FlatFileExporter.OLD/DatabasePage.xaml.cs similarity index 100% rename from FlatFileExporter/DatabasePage.xaml.cs rename to FlatFileExporter.OLD/DatabasePage.xaml.cs diff --git a/FlatFileExporter/FlatFileExporter.csproj b/FlatFileExporter.OLD/FlatFileExporter.csproj similarity index 100% rename from FlatFileExporter/FlatFileExporter.csproj rename to FlatFileExporter.OLD/FlatFileExporter.csproj diff --git a/FlatFileExporter/MainPage.xaml b/FlatFileExporter.OLD/MainPage.xaml similarity index 100% rename from FlatFileExporter/MainPage.xaml rename to FlatFileExporter.OLD/MainPage.xaml diff --git a/FlatFileExporter/MainPage.xaml.cs b/FlatFileExporter.OLD/MainPage.xaml.cs similarity index 100% rename from FlatFileExporter/MainPage.xaml.cs rename to FlatFileExporter.OLD/MainPage.xaml.cs diff --git a/FlatFileExporter/MainWindow.xaml b/FlatFileExporter.OLD/MainWindow.xaml similarity index 100% rename from FlatFileExporter/MainWindow.xaml rename to FlatFileExporter.OLD/MainWindow.xaml diff --git a/FlatFileExporter/MainWindow.xaml.cs b/FlatFileExporter.OLD/MainWindow.xaml.cs similarity index 100% rename from FlatFileExporter/MainWindow.xaml.cs rename to FlatFileExporter.OLD/MainWindow.xaml.cs diff --git a/FlatFileExporter/Models/VersionCheck.cs b/FlatFileExporter.OLD/Models/VersionCheck.cs similarity index 100% rename from FlatFileExporter/Models/VersionCheck.cs rename to FlatFileExporter.OLD/Models/VersionCheck.cs diff --git a/FlatFileExporter/Properties/AssemblyInfo.cs b/FlatFileExporter.OLD/Properties/AssemblyInfo.cs similarity index 100% rename from FlatFileExporter/Properties/AssemblyInfo.cs rename to FlatFileExporter.OLD/Properties/AssemblyInfo.cs diff --git a/FlatFileExporter/Properties/Resources.Designer.cs b/FlatFileExporter.OLD/Properties/Resources.Designer.cs similarity index 100% rename from FlatFileExporter/Properties/Resources.Designer.cs rename to FlatFileExporter.OLD/Properties/Resources.Designer.cs diff --git a/FlatFileExporter/Properties/Resources.resx b/FlatFileExporter.OLD/Properties/Resources.resx similarity index 100% rename from FlatFileExporter/Properties/Resources.resx rename to FlatFileExporter.OLD/Properties/Resources.resx diff --git a/FlatFileExporter/Properties/Settings.Designer.cs b/FlatFileExporter.OLD/Properties/Settings.Designer.cs similarity index 100% rename from FlatFileExporter/Properties/Settings.Designer.cs rename to FlatFileExporter.OLD/Properties/Settings.Designer.cs diff --git a/FlatFileExporter/Properties/Settings.settings b/FlatFileExporter.OLD/Properties/Settings.settings similarity index 100% rename from FlatFileExporter/Properties/Settings.settings rename to FlatFileExporter.OLD/Properties/Settings.settings diff --git a/FlatFileExporter/Resources/BuyMeACoffee.png b/FlatFileExporter.OLD/Resources/BuyMeACoffee.png similarity index 100% rename from FlatFileExporter/Resources/BuyMeACoffee.png rename to FlatFileExporter.OLD/Resources/BuyMeACoffee.png diff --git a/FlatFileExporter/ServerPage.xaml b/FlatFileExporter.OLD/ServerPage.xaml similarity index 100% rename from FlatFileExporter/ServerPage.xaml rename to FlatFileExporter.OLD/ServerPage.xaml diff --git a/FlatFileExporter/ServerPage.xaml.cs b/FlatFileExporter.OLD/ServerPage.xaml.cs similarity index 100% rename from FlatFileExporter/ServerPage.xaml.cs rename to FlatFileExporter.OLD/ServerPage.xaml.cs diff --git a/FlatFileExporter/ffe.ico b/FlatFileExporter.OLD/ffe.ico similarity index 100% rename from FlatFileExporter/ffe.ico rename to FlatFileExporter.OLD/ffe.ico diff --git a/FlatFileExporter/packages.config b/FlatFileExporter.OLD/packages.config similarity index 100% rename from FlatFileExporter/packages.config rename to FlatFileExporter.OLD/packages.config diff --git a/FlatFileExporter.sln b/FlatFileExporter.sln deleted file mode 100644 index 3253b27..0000000 --- a/FlatFileExporter.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.329 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlatFileExporter", "FlatFileExporter\FlatFileExporter.csproj", "{97C6201D-5812-406D-A1EF-C6CF943ACFEB}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {97C6201D-5812-406D-A1EF-C6CF943ACFEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {97C6201D-5812-406D-A1EF-C6CF943ACFEB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {97C6201D-5812-406D-A1EF-C6CF943ACFEB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {97C6201D-5812-406D-A1EF-C6CF943ACFEB}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {6E99294D-EC27-42DC-970A-DD2B66909606} - EndGlobalSection -EndGlobal diff --git a/FlatFileExporter/PythonCodeBase/DAL.py b/flatfileexporter/src/PythonCodeBase/DAL.py similarity index 100% rename from FlatFileExporter/PythonCodeBase/DAL.py rename to flatfileexporter/src/PythonCodeBase/DAL.py diff --git a/FlatFileExporter/PythonCodeBase/__init__.py b/flatfileexporter/src/PythonCodeBase/__init__.py similarity index 100% rename from FlatFileExporter/PythonCodeBase/__init__.py rename to flatfileexporter/src/PythonCodeBase/__init__.py diff --git a/FlatFileExporter/PythonCodeBase/flatfile_cli.py b/flatfileexporter/src/PythonCodeBase/flatfile_cli.py similarity index 100% rename from FlatFileExporter/PythonCodeBase/flatfile_cli.py rename to flatfileexporter/src/PythonCodeBase/flatfile_cli.py diff --git a/FlatFileExporter/PythonCodeBase/flatfile_cli.txt b/flatfileexporter/src/PythonCodeBase/flatfile_cli.txt similarity index 100% rename from FlatFileExporter/PythonCodeBase/flatfile_cli.txt rename to flatfileexporter/src/PythonCodeBase/flatfile_cli.txt diff --git a/FlatFileExporter/PythonCodeBase/gui/counter.py b/flatfileexporter/src/PythonCodeBase/gui/counter.py similarity index 100% rename from FlatFileExporter/PythonCodeBase/gui/counter.py rename to flatfileexporter/src/PythonCodeBase/gui/counter.py diff --git a/FlatFileExporter/PythonCodeBase/gui/pubsub_sample.py b/flatfileexporter/src/PythonCodeBase/gui/pubsub_sample.py similarity index 100% rename from FlatFileExporter/PythonCodeBase/gui/pubsub_sample.py rename to flatfileexporter/src/PythonCodeBase/gui/pubsub_sample.py diff --git a/FlatFileExporter/PythonCodeBase/gui/routing_example.py b/flatfileexporter/src/PythonCodeBase/gui/routing_example.py similarity index 100% rename from FlatFileExporter/PythonCodeBase/gui/routing_example.py rename to flatfileexporter/src/PythonCodeBase/gui/routing_example.py diff --git a/FlatFileExporter/PythonCodeBase/gui/user_control.py b/flatfileexporter/src/PythonCodeBase/gui/user_control.py similarity index 100% rename from FlatFileExporter/PythonCodeBase/gui/user_control.py rename to flatfileexporter/src/PythonCodeBase/gui/user_control.py diff --git a/FlatFileExporter/PythonCodeBase/requirements.txt b/flatfileexporter/src/PythonCodeBase/requirements.txt similarity index 100% rename from FlatFileExporter/PythonCodeBase/requirements.txt rename to flatfileexporter/src/PythonCodeBase/requirements.txt From 8e01a8ebe719b0129d2e5179e40e899742e0cf8d Mon Sep 17 00:00:00 2001 From: eddyizm Date: Sun, 4 May 2025 14:30:42 -0700 Subject: [PATCH 04/16] chore: looks like i was missing the briefcase work --- flatfileexporter/.gitignore | 62 ++++++ flatfileexporter/CHANGELOG | 5 + flatfileexporter/LICENSE | 19 ++ flatfileexporter/README.rst | 12 ++ flatfileexporter/pyproject.toml | 184 ++++++++++++++++++ .../src/flatfileexporter/__init__.py | 0 .../src/flatfileexporter/__main__.py | 4 + flatfileexporter/src/flatfileexporter/app.py | 26 +++ .../src/flatfileexporter/resources/README | 2 + flatfileexporter/tests/__init__.py | 0 flatfileexporter/tests/flatfileexporter.py | 35 ++++ flatfileexporter/tests/test_app.py | 3 + 12 files changed, 352 insertions(+) create mode 100644 flatfileexporter/.gitignore create mode 100644 flatfileexporter/CHANGELOG create mode 100644 flatfileexporter/LICENSE create mode 100644 flatfileexporter/README.rst create mode 100644 flatfileexporter/pyproject.toml create mode 100644 flatfileexporter/src/flatfileexporter/__init__.py create mode 100644 flatfileexporter/src/flatfileexporter/__main__.py create mode 100644 flatfileexporter/src/flatfileexporter/app.py create mode 100644 flatfileexporter/src/flatfileexporter/resources/README create mode 100644 flatfileexporter/tests/__init__.py create mode 100644 flatfileexporter/tests/flatfileexporter.py create mode 100644 flatfileexporter/tests/test_app.py diff --git a/flatfileexporter/.gitignore b/flatfileexporter/.gitignore new file mode 100644 index 0000000..f6c30e2 --- /dev/null +++ b/flatfileexporter/.gitignore @@ -0,0 +1,62 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# OSX useful to ignore +*.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.dist-info/ +*.egg-info/ +.installed.cfg +*.egg + +# IntelliJ Idea family of suites +.idea +*.iml +## File-based project format: +*.ipr +*.iws +## mpeltonen/sbt-idea plugin +.idea_modules/ + +# Briefcase log files +logs/ diff --git a/flatfileexporter/CHANGELOG b/flatfileexporter/CHANGELOG new file mode 100644 index 0000000..345fa5f --- /dev/null +++ b/flatfileexporter/CHANGELOG @@ -0,0 +1,5 @@ +# Flat File Exporter Release Notes + +## 0.0.1 (04 May 2025) + +* Initial release diff --git a/flatfileexporter/LICENSE b/flatfileexporter/LICENSE new file mode 100644 index 0000000..08fae05 --- /dev/null +++ b/flatfileexporter/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2025, eduardo cervantes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/flatfileexporter/README.rst b/flatfileexporter/README.rst new file mode 100644 index 0000000..07ff50f --- /dev/null +++ b/flatfileexporter/README.rst @@ -0,0 +1,12 @@ +Flat File Exporter +================== + +**This cross-platform app was generated by** `Briefcase`_ **- part of** +`The BeeWare Project`_. **If you want to see more tools like Briefcase, please +consider** `becoming a financial member of BeeWare`_. + +Using a sql script to export flat files. + +.. _`Briefcase`: https://briefcase.readthedocs.io/ +.. _`The BeeWare Project`: https://beeware.org/ +.. _`becoming a financial member of BeeWare`: https://beeware.org/contributing/membership diff --git a/flatfileexporter/pyproject.toml b/flatfileexporter/pyproject.toml new file mode 100644 index 0000000..6375242 --- /dev/null +++ b/flatfileexporter/pyproject.toml @@ -0,0 +1,184 @@ +# This project was generated with 0.3.22 using template: https://github.com/beeware/briefcase-template@v0.3.22 +[tool.briefcase] +project_name = "Flat File Exporter" +bundle = "com.eddyizm" +version = "0.0.1" +url = "https://eddyizm.com/flatfileexporter" +license.file = "LICENSE" +author = "eduardo cervantes" +author_email = "me@eddyizm.com" + +[tool.briefcase.app.flatfileexporter] +formal_name = "Flat File Exporter" +description = "Using a sql script to export flat files." +long_description = """More details about the app should go here. +""" +sources = [ + "src/flatfileexporter", +] +test_sources = [ + "tests", +] + +requires = [ +] +test_requires = [ + "pytest", +] + +[tool.briefcase.app.flatfileexporter.macOS] +universal_build = true +requires = [ + "toga-cocoa~=0.4.7", + "std-nslog~=1.0.3", +] + +[tool.briefcase.app.flatfileexporter.linux] +requires = [ + "toga-gtk~=0.4.7", +] + +[tool.briefcase.app.flatfileexporter.linux.system.debian] +system_requires = [ + # Needed to compile pycairo wheel + "libcairo2-dev", + # Needed to compile PyGObject wheel + "libgirepository1.0-dev", +] + +system_runtime_requires = [ + # Needed to provide GTK and its GI bindings + "gir1.2-gtk-3.0", + "libgirepository-1.0-1", + # Dependencies that GTK looks for at runtime + "libcanberra-gtk3-module", + # Needed to provide WebKit2 at runtime + # Note: Debian 11 and Ubuntu 20.04 require gir1.2-webkit2-4.0 instead + # "gir1.2-webkit2-4.1", +] + +[tool.briefcase.app.flatfileexporter.linux.system.rhel] +system_requires = [ + # Needed to compile pycairo wheel + "cairo-gobject-devel", + # Needed to compile PyGObject wheel + "gobject-introspection-devel", +] + +system_runtime_requires = [ + # Needed to support Python bindings to GTK + "gobject-introspection", + # Needed to provide GTK + "gtk3", + # Dependencies that GTK looks for at runtime + "libcanberra-gtk3", + # Needed to provide WebKit2 at runtime + # "webkit2gtk3", +] + +[tool.briefcase.app.flatfileexporter.linux.system.suse] +system_requires = [ + # Needed to compile pycairo wheel + "cairo-devel", + # Needed to compile PyGObject wheel + "gobject-introspection-devel", +] + +system_runtime_requires = [ + # Needed to provide GTK + "gtk3", + # Needed to support Python bindings to GTK + "gobject-introspection", "typelib(Gtk) = 3.0", + # Dependencies that GTK looks for at runtime + "libcanberra-gtk3-module", + # Needed to provide WebKit2 at runtime + # "libwebkit2gtk3", "typelib(WebKit2)", +] + +[tool.briefcase.app.flatfileexporter.linux.system.arch] +system_requires = [ + # Needed to compile pycairo wheel + "cairo", + # Needed to compile PyGObject wheel + "gobject-introspection", + # Runtime dependencies that need to exist so that the + # Arch package passes final validation. + # Needed to provide GTK + "gtk3", + # Dependencies that GTK looks for at runtime + "libcanberra", + # Needed to provide WebKit2 + # "webkit2gtk", +] + +system_runtime_requires = [ + # Needed to provide GTK + "gtk3", + # Needed to provide PyGObject bindings + "gobject-introspection-runtime", + # Dependencies that GTK looks for at runtime + "libcanberra", + # Needed to provide WebKit2 at runtime + # "webkit2gtk", +] + +[tool.briefcase.app.flatfileexporter.linux.appimage] +manylinux = "manylinux_2_28" + +system_requires = [ + # Needed to compile pycairo wheel + "cairo-gobject-devel", + # Needed to compile PyGObject wheel + "gobject-introspection-devel", + # Needed to provide GTK + "gtk3-devel", + # Dependencies that GTK looks for at runtime, that need to be + # in the build environment to be picked up by linuxdeploy + "libcanberra-gtk3", + "PackageKit-gtk3-module", + "gvfs-client", +] + +linuxdeploy_plugins = [ + "DEPLOY_GTK_VERSION=3 gtk", +] + +[tool.briefcase.app.flatfileexporter.linux.flatpak] +flatpak_runtime = "org.gnome.Platform" +flatpak_runtime_version = "47" +flatpak_sdk = "org.gnome.Sdk" + +[tool.briefcase.app.flatfileexporter.windows] +requires = [ + "toga-winforms~=0.4.7", +] + +# Mobile deployments +[tool.briefcase.app.flatfileexporter.iOS] +requires = [ + "toga-iOS~=0.4.7", + "std-nslog~=1.0.3", +] + +[tool.briefcase.app.flatfileexporter.android] +requires = [ + "toga-android~=0.4.7", +] + +base_theme = "Theme.MaterialComponents.Light.DarkActionBar" + +build_gradle_dependencies = [ + "com.google.android.material:material:1.12.0", + # Needed for DetailedList + # "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0", + # Needed for MapView + # "org.osmdroid:osmdroid-android:6.1.20", +] + +# Web deployments +[tool.briefcase.app.flatfileexporter.web] +requires = [ + "toga-web~=0.4.7", +] +style_framework = "Shoelace v2.3" + diff --git a/flatfileexporter/src/flatfileexporter/__init__.py b/flatfileexporter/src/flatfileexporter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/flatfileexporter/src/flatfileexporter/__main__.py b/flatfileexporter/src/flatfileexporter/__main__.py new file mode 100644 index 0000000..fe6a528 --- /dev/null +++ b/flatfileexporter/src/flatfileexporter/__main__.py @@ -0,0 +1,4 @@ +from flatfileexporter.app import main + +if __name__ == "__main__": + main().main_loop() diff --git a/flatfileexporter/src/flatfileexporter/app.py b/flatfileexporter/src/flatfileexporter/app.py new file mode 100644 index 0000000..ee6ad29 --- /dev/null +++ b/flatfileexporter/src/flatfileexporter/app.py @@ -0,0 +1,26 @@ +""" +Using a sql script to export flat files. +""" + +import toga +from toga.style import Pack +from toga.style.pack import COLUMN, ROW + + +class FlatFileExporter(toga.App): + def startup(self): + """Construct and show the Toga application. + + Usually, you would add your application to a main content box. + We then create a main window (with a name matching the app), and + show the main window. + """ + main_box = toga.Box() + + self.main_window = toga.MainWindow(title=self.formal_name) + self.main_window.content = main_box + self.main_window.show() + + +def main(): + return FlatFileExporter() diff --git a/flatfileexporter/src/flatfileexporter/resources/README b/flatfileexporter/src/flatfileexporter/resources/README new file mode 100644 index 0000000..4ef2794 --- /dev/null +++ b/flatfileexporter/src/flatfileexporter/resources/README @@ -0,0 +1,2 @@ +Put any application resources (e.g., icons and resources) here; +they can be referenced in code as "resources/filename". diff --git a/flatfileexporter/tests/__init__.py b/flatfileexporter/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/flatfileexporter/tests/flatfileexporter.py b/flatfileexporter/tests/flatfileexporter.py new file mode 100644 index 0000000..d59e9f3 --- /dev/null +++ b/flatfileexporter/tests/flatfileexporter.py @@ -0,0 +1,35 @@ +import os +import sys +import tempfile +from pathlib import Path + +import pytest + + +def run_tests(): + project_path = Path(__file__).parent.parent + os.chdir(project_path) + + # Determine any args to pass to pytest. If there aren't any, + # default to running the whole test suite. + args = sys.argv[1:] + if len(args) == 0: + args = ["tests"] + + returncode = pytest.main( + [ + # Turn up verbosity + "-vv", + # Disable color + "--color=no", + # Overwrite the cache directory to somewhere writable + "-o", + f"cache_dir={tempfile.gettempdir()}/.pytest_cache", + ] + args + ) + + print(f">>>>>>>>>> EXIT {returncode} <<<<<<<<<<") + + +if __name__ == "__main__": + run_tests() diff --git a/flatfileexporter/tests/test_app.py b/flatfileexporter/tests/test_app.py new file mode 100644 index 0000000..e1a335f --- /dev/null +++ b/flatfileexporter/tests/test_app.py @@ -0,0 +1,3 @@ +def test_first(): + """An initial test for the app.""" + assert 1 + 1 == 2 From 347060a1e58a3f2d8d076f96ceb742ed45e3ff50 Mon Sep 17 00:00:00 2001 From: eddyizm Date: Sun, 4 May 2025 14:31:42 -0700 Subject: [PATCH 05/16] chore: more moving files --- flatfileexporter/docs/assets/BuyMeACoffee.png | Bin 0 -> 5433 bytes flatfileexporter/docs/assets/ffe.ico | Bin 0 -> 67646 bytes flatfileexporter/docs/assets/ffe_01.png | Bin 0 -> 17756 bytes flatfileexporter/docs/assets/ffe_02.png | Bin 0 -> 11579 bytes flatfileexporter/docs/assets/ffe_03.png | Bin 0 -> 13959 bytes flatfileexporter/docs/assets/ffe_04.png | Bin 0 -> 17750 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 flatfileexporter/docs/assets/BuyMeACoffee.png create mode 100644 flatfileexporter/docs/assets/ffe.ico create mode 100644 flatfileexporter/docs/assets/ffe_01.png create mode 100644 flatfileexporter/docs/assets/ffe_02.png create mode 100644 flatfileexporter/docs/assets/ffe_03.png create mode 100644 flatfileexporter/docs/assets/ffe_04.png diff --git a/flatfileexporter/docs/assets/BuyMeACoffee.png b/flatfileexporter/docs/assets/BuyMeACoffee.png new file mode 100644 index 0000000000000000000000000000000000000000..f72203b66cbb8503f6360aeba80ebc7194ce4dbb GIT binary patch literal 5433 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 HK|cQnBZF-5 literal 0 HcmV?d00001 diff --git a/flatfileexporter/docs/assets/ffe_01.png b/flatfileexporter/docs/assets/ffe_01.png new file mode 100644 index 0000000000000000000000000000000000000000..e5fa22505ac45e9f28fe143f50f333342ec8268f GIT binary patch literal 17756 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 JN@0Vq{|{}u^JM@4 literal 0 HcmV?d00001 diff --git a/flatfileexporter/docs/assets/ffe_02.png b/flatfileexporter/docs/assets/ffe_02.png new file mode 100644 index 0000000000000000000000000000000000000000..adc9fe5185e15fbd025e8be96c545dae7db2363d GIT binary patch literal 11579 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 BhUEYN literal 0 HcmV?d00001 diff --git a/flatfileexporter/docs/assets/ffe_03.png b/flatfileexporter/docs/assets/ffe_03.png new file mode 100644 index 0000000000000000000000000000000000000000..83b064d38ed67f37ad60765cb9818f6e0110d502 GIT binary patch literal 13959 zcmeHubx>SSwB{rP2n0z&0>J~pH3SQTyM@6a=!9UwgWI4%0t85K8#K5K?#vJf?(RN= z4emb6Z(r4JZPji)*;l)@Rr|-St}A`I?>*Ar`A*+3HIO_ZJ_SAi03cLUkkJ4D9zgDo z!^aQrudEdROucU&SxTx%0ss}!Pi{=G@6YkRD(E=_07UKo91pr23d{fi0aisBNi7fK z-37c)B-(el2a01Sd4?!Bj6S>zpH2VMuRx6#KaG%Dtyl${_>Uhx45trmWm^V+lxUEt zHrH;>qz`@kqUd#?#1l^W1|*xja%!z&?2RAqzIh6hRqF2^_BmhiIYQ^=WY5xLxaYCO zsP*9A)1Lr<%rEKuL;pu&xPWD4%Qe%R3kHDy)5p|+r}$Y|04X{bfCNhr5#Vv~@B=`W zVT(DdKqI`f3xOScmV(|7IYGk z6K-bZ$QM2Y=1w@HLh^~#%M8|ZGMpIYK4_4-*0bUQS>`y#UPPJGbbFYrI)u1vr>J{9 z=inEyf8`9q57Vwrz3UWeVMSNEdfah5xKKRmwDBw0ij>LmgEG9oVO8q)CXIfSB8RK0 z>b{0< z)1YE<>`!YLG0RM44^s&RwN{>C(Z*6=jh&BRPDaRMNt5y-qGKfk;C!=MlV= zIldSp{24Iz%O>37AobG8bIU2UREAkHaDQRy*rbbHZQ<_pC0@0nsuc-BX^z(|uR5ew z#egM}I?q-`m}<@~wz9oCSl+8huX>{!>Ll4P7_9B;<)Y)1{EiTiwvpvb*LATZ=e8eDrRcKdz)&GMS`Me%x%U0ixRRX4WPXAE$^SJ$uu@JBogEQwybqeh3n0FL* zS$8WqSA+hPrKT*`!%ykE3N%(}--=+G?}Jt&MWg3u!kIv}hKNwkBdxyf+89W1r=@b1 z;m46+HPusnyP>Pd-dZP>IHIV^c8BS0k_Zc~x-+jdr~OR;W{yfeYN409PC8$wTs%WV zZFty5YeDTw!_w&G@s*Tf0}FY=e8RcQ=X8of!~7S#PMXb0=0{%%Xd4`-o4+b3kn`+$ znysLDj#efrQsifuh+}p8*4|Glzx8C1pOK@1IqtMf>(n4~u(Viq=j!TIs6tRg|5zhC zdSAf;t4G^KU$JujU5eF}-@&418_Phy@YuZ90WLsfC3xwVA+yZh#r@rntL1tC|J8a5 zU>lcUvkdfX5_^bUd9PlX+@cM-mE+9zi z-~3Q6rconjq&pt~nEyStbrJUvQTF0LbC4t1&EGIT*4ag6^cbUaCoScq9cUnnzH_{W zyYs`voyc6~{Kq5i-D}&~a2iDJl=Pvy{R^8hBy;xC zV#-du73-oJ7BkY8`Z7O|qS8jd$~@*`?*(q?wvDoj76A7jkIPx2xWuUI0t4 z-|Lxdj9m}=e(pTx>WCZu@q9&@@Vr0iW=5T^g5*YG>5s~&7>&5wN{CN>74i?2%Ga9N znl>zk!q>OORxrPpB47W^a*Kg^PDg&IGIP(Sdv=w-oP%e~>J`oI-`b_FpY9r(orU4p z<_A~$empZ>7>Stjz3UNPcp}h@a5{IrvT!CU^YH0RDEhWudI>pgzLQLyWIh_b4An)5 zq3cLt5E=1c# z@kAG9z_V6Z-a`Aqz*B;z1*?3cYFN=jlU%|AX@dNm#C3BaD>Xn@@eO5@1{?q)4tAtB z5KFE>uv1o*Zm6`lA+M#vLfpid@y@kO2HB#1J8q2gxlfHi3tG7%S|A@?cIAf_ZnmuX z&4~bO)H>-+ufbHkQ{ctG0h%2W=c!V0jlzh;^Nq8CWOW!^Oigdmr1eQl>d|N(B{_M3 zyV!74)W`f8X)NacSI1umVsIQ=?5EPiZV;rr_P2y^CSEIPh}^mQkgK(No!c(XIq|DI zv*j~8gym69FpY;}oA`bixXEpdXh`*y<5DTKDTCim%_K9RZMG_v0YVFBzdQ+@!_7X z3qlb+zl}p07riMLFBleo>3ytdm_R%ZhY^^JXq}v+l#@AWR2) z1w^Gq$F5Q$7u!mA;0VurU2XXKYS^2)G9OF`e=H3pnR|!UGBQ+Bx0pg~rra~NXsT@S zsVkHKqxqMBp|8EWgC`X~5U9f9)aG|&YXBcjNHV}`s{b!X1E z)^nU%F9dxo#9&dM2axh;w$%7wCKi@yZieq=OH^ugHr?_|zAccea@->7>-ExSw_Rnz zY$r!-Z>yOwu#O;HgCW8zZZo*)MfT8zsuWL_=6mpy@lvusp{$Oi35rsHx$ zPCo4DQtGlt98!%8)xyZR%b)tJEHEmAt)Vv)gIe&)vBspR?`$TEjBgAGwLYV0fdh?%+n+Do{#;WMK@mp@w z(j`Y)dC~B|6rcLCH`i2HcL3~K?%3b{tM{pMjx;8@h2t=;nd$O6SsJY8EaUqaL_$TH zC!PYi*miA!1egdK6fL`{-sFH*Lyy-sQ(GWFR^JYHNV&3ZQ*WtV1jd11c=>SYEFVoV zUf4MqU#CxewyWu|H0qhbl-}%u>J4sRY8H0sZ(NJQ0^y2*8@D#WPGPWwGLetP-X{wt z5JLY*Ct~Uz*x3?uv$zl&x>zuTkb)06VQ!ht6uqk-3OgzEpOhx$^vhX0#egVv{*;oNTM|4}-oSfs za6tDgQ5OJUl=wH}fPW29u-yvcJ_L9?@pXO;_#W~P+W!xLjel(rsm&LreSH)i?aHGU z&jANG|6~@Z(qTn|qqk`C_m4{^MBcsg>b|%vuDkW6OC-bf@9jMorIzm8s;=J6@OyjM zV82VLQ#^5)tF4$A384mP0T+p&-$6qy7M3zFw*mTx)7H{*tD_sV_Vd2mLyH77Yo%-6 zI*5hqMdk$48|>v;cc!w?jwc#q?rLsRCXQ@ijW{}V|J}tQaS&^;8KFET?$1=bo)zx- zU)7v=BQMtsKs*h0R6&55QGDm_73R8*JyiG1u7Kk`0@(xKOaf?$1@0ST*ZFYW<##~i zA~@U2{045gl=TPz@gd8@bd3TzQ>7k(9fSF_KCU3W$AI|Yi($G*oDUQvSs&~!w-c3n zg9z`ZXps1U_Br)TvreYhREy7k-4w=3@SK{2u|FP)0}968BI;#J3e6&am|SgAiI$~rvC zKreA|fO2y}``s^iW_}80?q`3A5BUJDycXdGy!OuwFcS{RkUPHCdm}(`@vb8aQ$=oD zOEzqECYCy4iROV;mG*Sdeoj4H6B_xtIkhFrMps3xFgPvLlB6Fj%dPA-J)W}`|Mh9U zxK;}gxc+)MomH3`cZ;yc=9)~ma76r23LwVx_4Q0t8LEZ2qOg5y%Qmnft&%%6MMY+O zxsJ38(^{IcC~d_RVx)NmZ!+?kf|1+}Zdyb&{bVzw_krB2#w4;Av5m3ZbT z4|{g3b9gHLmpjV5^yVfXE5k^8C&^86HU_@1IAi%`(o|&P3;OOhEcj(iWnE+=Y~PY7 z@w+|kP*OIP%-v+_X%Z;acA-&w(PEh?C$b)R8B?*Dw z;NA@~lvVahc&he?&(6%LQtmX4E#@$y>%6h+N(9UQ6>4mZbw^R=gCbJ-#hk?%xEbA? zKJ;>Q-+Kd8`-jAa`^daVgE+mB-tFcd>QOb4g(x3>`xsGG9HMedPme5XKT~$2q~x-r z8t|rgAdMJ6Dhs(s=eykiIh;lv-))}Cl3LvSjiws58Zl|2jlVmf7x(}^2{pOJll?Kh ziIl`{Sva^K}H^O0hueQQAp&UU(97I>atK9URS106E{&=+U@G(0t! z!SarQ`*T-(BTS?(F}`=}aQ+JCE^TPFuAUiMB5cEd4FGFgEGz@@d-X1jj6E~$p-jDq z5G-a8JISJYv7|C;2^(?Az=3%6VAoH*`^zIPY$dw((T-RS$o z$BLP?IsI_502x*Hn2RdWJ+$d%MS;D!XkdE%&I|EjoE3hwlTcr?$F{^vRyMF#7<>2n0+h_hLnBf`wmYt>F-%iwv74|I8^@sE+HE90TnyI)Z(=hqi z^=i}H=C0Vatyz&{*X^+V&Z_!&ak6WtbKy1{k~d9IyWD2bl`DDkVsVOHFEV9 z+|C*{IbC6#IJ;!$oO<^+p)SX>8e61d5;f-lFjuEZ)IBo&iiP+IOaD!DyaX9tzw29R zp^W!ya`*0D4_A&)aPwDfVBB4HGheRJ-VeQG(RkK*lEb(!V0HfMvv{2KhuKQwOOSqt z$nXa}jk7hqt?hZ8OwITk#(I|PDCJwZt$LU@)Pvp4HlZcCd$Xg^}-)#k91hcH>o zyS>=td`!>h@*^>JU1sV0h^rwB$3?&SC|6(y#yf;-J>8!4!!i0_SD>hQ=$FkfA(yOL7T)dgiLlx{i7K%c{Rsel81$*ebi-;bBD^aXuA@RifN z^K2L1&T=`WEOu~-2yhuYO!YcPmr%U@yF-k-W7Fv=Xknoi=hz`oX{ztlG~bL#Qd?w)08DkDCX->1kQ4-+P^i&nPi} zW87}#+lBbvO#b~_THM)H_Njv)Ota)Xd!{Su`T73R6&7tV;U=HgBSflJe-fxk@*C2J zU9iGxE=L~47P&#&LQgb=?d>Gu_V$v*vRA(5ISEO8w5(qA=-2*Q`W(X$vpe=Mg=y** ztq6J^K= zER47WO)i9@=_CM&T(y?XdbhB`tM~VI&5m-d zq&e_&9NgWwOsx}sIjR21@=Gzdt*=skg6k?f)rjB!SdX_Fy0-X7-;M) zGdtAv+U%Ae%b2eGxX^S2P`?jHCHkNSZ;|#~=_T$w!ttd~Fp&jb-^24zrS31cm$!#w zcD26*vomVOXlw3d=ud5JfzV!QHLdC(hlj1#EMNIfFr8S>FOnbs3r*aMuF?glDgKZ2 z@P8g)gpdKZ9N#oer=)i4{`}hj$?A8*Ua~ba1ZH6S<9FtZLc6CYgi9d}42%^)HM!7tBy@)=kI%4*FtRXd5kul4~lS4k*g}tPwM&cc&Mqp_*H~mBCCnE0TRK?Y%Zf~azxHYIE@IZz^)D!byc6_qvAMLb)xCKd& z*|D%6MlJ2u34ApQ2Y>rN_vv<}r}dKNckSA@^$l59xYXMzJdg;c+c{#h9*xrD!Ly^DD{*?;kw_Xg!va-e^l6Cyn$-xc`W5 zAAVWPMKbR1)th!PG`)W4k1zFU^LJON;`go1r%eB-2Dv5}_x>@JKR!t7{zl|)n>;RYM1X+1vdoAPo3fm;IxCXRNlYUhcClcFQgN==d z057{{E?zAK69N21@u{=0dH{fbJ@ffbV0HgRJ!WJE!G5r`+YTq~5plD@A3rqj2Hyg- z-iz&S$#j-2n(!;9=reeHwx~cAo2r6P5hO$WCMex5C1uMuZU8n0lSEzhShxOg#gA)G z8UIB!nhA1*NI%(ntI)Dvy^roi(RaCI13Q1)t5zyQ|1RM1wC~)Rd3uey724q5kURT_ zD{M+QeParyPI}Zh8>1phF8n&2yNU>FAR&*Vu8$5-@H!}DFqpTHlj87gMby?#68%7J z)F~fzy`hno*)lnRjUXT^mZpx0E-$A>Hzx-7>qTai_E>=C=iS5y5*@GETsC{HE=EbK zn1sxtMEu*Y#KRo7S)4!LjJAp0>zyHm*HM&$SYg?D@%P^kY#HiY`S>Kvj`oR#+dOYf zb=lv%ucds!N(gw_@qa~0|4x4X|A^uI|2h48%O?MisU_~e#jSHA6~p(}eY_ARHaAJc zBxLqU_>O_~s6+2`fj*>Dr3*pjeqj%rye}LIQPRH2%$K$*_h*G`Yx;70XzsO~SF7J= zCEiID-RoO}G+?O`0|3T4CR{LU*+}T$( zU%RbXBikQXJ_f`;!H&9Lt6b9r*%Bi6s^P-h{T=AuY^f>kbfII^j$?Rk*ytqzDzH!y zfoS6EpL;7MgmWsfG;Xk@^NwxIh`&-kel(yH8wyRZmn0;`%pe#FNV+p+?oOOFr zoEj$pfmo$K^j%0<=&r1sxo3W9w+Qs1JEjavn zF-hiBoR^2ZV71yULN5?|BLGX#|D1;Gt*h&2R!RAr4+u{YcIV0n?0qz}b-%oN zfN=l!Dx0oNK}48iKgLyA2g+vs|K2=+YJ4<5McK-Qev*iF7oRkFYb!yODk z9*njJ`lKCGdWVq@nv3S8LNlfWED=3&0f8!2+9d=lsqyl$%eZpT2fdk8_w1^0^bDSg z?T~|1l{I;!oHWeW*OxD=5~|&IK~Ws7AxXYXuuMLJD2s!x_6l+59H&pio1Vi5XJ5Ut z27oGRN`6BCw4eA*wCPJ?3V8i3gF=YJyI#7CYEr7H6JvCY{|JUU`-xfAr`Qd?Q}FtB zBSuv+7KE$7FMWIOQ>O^--xf)r;)$#4>oNA6THFxo?g$Upmr&E|ugtj8hi>#hSmFIdHgs3;Py)i!o?nXKMeA5$dCLr-zW z{!j*x9R(6th@*b?MmHW{D6Uw?*uC+FKW=Xx?ZJ{MbuLWLw4qs*&z=EmIVFATwDki= zzVr_Wa?C($&WOJ$?v2%h*77SvW|%%XO|hB$c@R{CTcg;`*gH=XUR=e)E$A_Wy8`yf z!y|YnKHf2U=Br$#P#dP160yOD>o>8p#A&|RALhF{c~R$LqWx$}XzA05(luTAdA{Y4p5pAc|2^w`R6P$F(u&7r)f74Z>K+h1Y?~|&$hx?MZ z7^XUtNrnQ(x7D>AyIJ)>8#mjii%%g;*9i^#RqLTZWnX=r#g{44h|5y2Z>*J zjAuFnJ z-P}ODyz{XRrM+f@S1ISlg}9lUu8<=uXx-FwTARHY@F?~{_TJH0im<4K*Xh9UU+QOX zuE*?sbS-`C7bhdML0jz}VefwV4f>G>`dt*W?M@oFhV=I9X0;D0cwKYaws?BN?CTwo z&s`liKb+1ABQH6WF*Sl;He4Q0(oB1ctuPvP)>alfU!ZdWeV1$~j%#!6gpJDxI0ood zV%Imhzy+s<;H8Pn#6CK~4-Xdmur9@Q%4RTK$`j2RmIX-2bKs85Z`~T6VPRyNT{0p) zXDn{gqej*K*WhCHjZM3XHL=TUYvK}~Zku^4Vi20D5n{6W$0zTQmyRp$$OpSHJUGxv7$zW(w=;3vVeRf<`Ys2RVFiSl=oIGjMe%o_W}A3`o}PO0+ZJzR+@x!HMuQhdE!(1ck3Avm%xu)f@{vX|ZCPkXqY0K#!zTH!@9k-@uUPNW02KC~ zbt@^9OCJPAy&*bb>Mz#Nk6jM7-8TH`$OyS+IvyKpI(kdgGNlS`I-Bu49_LQU4TiXe z^?#EKzWZec5?4P1T5gctn3vEKw_K#iHtIcvNOOaS8?-F}oqfc(m9fzv8Q`0(fw#=e zmilc2oXj%NXo>;ovxEt(;?SU=N7&d~E=QI%NOk1tKYP$rDY z-W3B__Xjwd))E{QCjBK#-=CW;q(S+^@XVMw?3hvWyP4xY31fOy60b@C%{aNgM^(PG zC5;W93)koz%orw~;aEImc?qczzDw~IY?;h(Ng8N0H~Hb$)m^lsk|bISZc8mUg`ScP z*1Q_c`E=&Q`aX!vX}xZAROa*(dJ@vhTGfL&-54CWP@|maSObEG#vNKFzQf%9G{h~P z>IoW%Gl;+1t-zJ$7AVYft{4RTNEG+(puj>OZ=g`>8Gd`U{jg|OVywoVwc#x1y8y4z zP@m$E;NXVQWB2>WB=2mqbMKZIE^c*^Q~csbj9>#E-(phKqfPsMy;Vo(x%s0s?t;D5 z=;Y@c$K-GD2`;5AeP;_IL(kU>EckCKg{OMm{u%U~8}`xr^^VU)v~oHFNKZ{r9F<}= zx62GM;`vD4woT=9;h*dC0<24a`miX7+qhaL;hL66%)5xg6>fs>c6hZud`7s$k@lr~ zO#Ej2_*B0ZrMw_7-^UkP|BtnVngU&b7RZBOdK8(0%a&uIHqKy5FA} z52$8nCIB}K46kHfF%Ax1MB5uTJUxwm2E~Q1!k!@6MND{X`1U5r{V$60+Xp*DNWfe% zBMg4Xl$u8&Fcutcv(hT?8Rs3k-jMY9s{(D`2`myCjnPj(NmeHK8m=nG8~nMfm6}Qp ztyiB$&SX0l-@mb7m&-J(g3(@cvq+0^MtuB)Yy2-5`>(agBKefiQFOZInP{%>eMzbC zxgF&mEe#C~1qH>cR|R8RH!mPY>QS!t3$O^!QSXG58qcs*S@^-j7LC=CSAu)i7VGuq zeJp@HBC6>^yE)5^p?t<_0oM-vTwB%~4F79X=#og;vFgl#vi*@QRcKk!mqy1_H;Pha z1i_chri`P)&myF5qa8r3x9~>uL1QC7bk6sRYc@~M!s!vbjJnC9_qL^#!NFzZ9A!0% zd9p2Db`*kpBk*5U{bjsWf>&-n4tnb+GY#Yb+~O?=(~ZtkHEyooOm*;`M`V;b73Rlg zY>00KBORWLmK{%~kuv(lB|P7VAb-PRe{)kUDhDSYY0b6FOsV$ysVbChJEE{4ty=%_ z-d31Jsb4}N56X7av;XcDBTkbn=buyua$!v7#!l)%jzrK&h)APM%wC@@);Sb;9dQiY&6H5BWiS^*&ZLy zM4lO9V)aq6HVk2=aYe&esM935@c^c_cN2fM!R<(!yLX)oJNO|ts$}7IFZE%4Yid?b zI;1331;&X4<8$dl)v9KPn5$)=Uw?DyqDx>#VBr4fSN^d)^&HFW99DZ{!*YN4mu&g# zGw6N-t-Z^ybx0#!Hb`%q%DN{j*VY7&#og=42{hkZx|ZMe7qiG^9b-4}ql!fj-%6v!1Use<651 zHo6(XWKm*pew-x@sd>VuILDjaSebKNRIj%o=SvQewb}&Q3>(mD?u{4By)W^41m1oS zsnxX)6b8cZS8)v2V*A*oHnMe!?KBOFZ|npeZ>fPALkeL;Dj&U<*_F%;Hf|>EIP)HE z`y9A(T{vQV!zl$+NHXk}2J?(!oRys~S5Lb#*p#8g=pPh`4wM!1S==eh5%Y3gm3+e% zjRuy0)!1|*8@#)4or!^5@%r`5rAkdMu^7#`T#KKKED~K}e%%yUE9?<}dZ#CanoM{H z0$L!hhbiM+C_Nh8^yeqS<$?$uYK+8PvSKuviV19TSd*Jw&nmsz{g5%|$kZ+8J`kvS z0j*7kw_LdAb(bZ@yH35eWv$X1`V??;r8Th)9q`;-#W1!dOHq$l6UC=B(te40<7^`l zji?u{fDl)7r)9XQfR#WQ;$WQD9OkUKm0eGiD>tUlxGDLMd-;VW>Un;g%)H@@+u7ME zDJj`N)EJIlUtd-5G)5Ia|8%)ZyMAzRP+nfX{OJ-#q*?rFzrFbQa*LTyitRAiFE5*C7FhcjS6B?3$*d`7~GA zxo!~fS9fS@8jdwaE>u+&ppOUM;I)Y0JIsRI;~}CC6U%7BPr4gxFwm*;tok;Va7~ep zPr0j2J~)>tDlQ{`;b=EZoupZNLzt8K0)MGOfiyC&236hVigVC|dTFwI4>4L>TdyXt zga!C6&g|53!b*$}>Tr%$KAL9Ym!}*&Wciqw*>b7l$(#y~XA+rFHn+DroU5p2rK%Opv3o*RbHlrC(9XpV zJBdK-2_OQ|;Ze69XX%Rb2{Ou)cN{$^drF-&sNQ%4zWHdJnbASU%mm$~b?d`>f|rM- zuAC`XAb?}sh7CgBY#7jf985d8cEsXFGN$-6@hc*zyev)B3T|x9n+-OuIeI91khhZN zsCFaw)F?J_i=lfMR($Uh`ER^Q^3Eh8v#dWJTpOL<={u|w?e?m^X_?XkBfK6{2e#Z+ zcDAyw=zA~AQg2gf)y!sD9p{1tu6)iL>dLBa+!!uO!HynBhDHlG@B-&A3Tg(Xl^Mth z@F?h6Ud6oCheC?yY6(EBr(nVdqk08!yfHkb{YW}Gx&a$(Sa=T}@d>hrTHYlc*P2tg z(Xy_Tdq0gxGBSW@<8Tf(2DC}7nXNmh3PgUJ4R%nz^lUD$>}NutjtAy^r^VeHP-Wf_ z-3^MYJ3sdKzpGdf{RRC-G)1vG?-p!CUOc1s%pFwgvFbXt(W}|zg$yd&s>-BTRPAVQ zFpDnA_;E-##-Ka*OB`i{ZJz2Mn4HfyP6QS*KJ~)2a6km{pt5t)H^X61-Ic&m^eR zM$VBx>>bJ|Iw_=n)q&fH{tA92pOaabtR18%Jv)+y!uYLqoZIv73=oO**2*>8N)(A# zVJUIav#8_#&@*bvgSOxeFdSsW(eOI#s0qPo9}XK{={##T^`c zWOl9u&3czJe0bCzP&+;5f3%-kdHDu&d3ovS>DkcGaH3S379*yNH}ES*URt*BwA>Dw zsr)YDMP|*3UHb8mfCcaxuLWIcA+g_L9h%YIA!d%OiyXh_eCJKaytu_0cGF0ZR@##{n(#kJeA zE0U}!2L;B>E(!+KtUNFGjfI?SiRE&B@#9UhZN^)`%qjhVrE`b6-H{MZ@9^O^khysX zQ}N~p;;8}jM4MWEcml{rc$}c1c)v7Z`7@Mrig(-oHtQ~+MN*gBir_M#oK0h5yE=;r zofj8tH)2i5Zv%#-@1F4Z4Ah_x!)I9%f#}F}cHbI%oN@MpDzSQ7Q6_BQ?%$=)1cHWz zHt-j#4l6HfRu=Aa^?Q1N&e}T@&zd-B7R%;2TWwGZe(3;j3h&A2Da7+{e$n8IXKcYZ z-B`ItPXiI+En`Imgv0CxAnbw%9>t9H2A0akWqY#|uTi>ws8C(r(GFdOO*`X_heb6V z2nlwK%45GOIyJUoE0pR+Rb$2MJ9sg#mK42IX!)50?1-}BNQWciq_>I325TpN*hweE zPAZ>2i^Rx|AZQk_!Ykm=TtZkrRVfYaxVJz`$t>ydGl}Uf(8pLcFdkUFnrAp{a2Bhb z0+1ts)z;gGd;DAnH@CN-iWp50A6`FfGR<06{k@IpPXT8@DWg^l6cq$Lk+A~=apvoq zFTt(xN|;KNNFPT8b~x6h;}k&9TWi7zT{*olpmeTW)|;h}r>de^Tk(q^ za?b+^x@ZIaLs=z1TGVDe(hl?m+w*?ZcJ7exXj5Y7tQtsdnHUuF*{ z)pS!Mx>!-S1gOSaZWdhN?LCD5vGdpM#)f>G1HgVhxxfL4eKok>?x;ocAFM1eVxDu0 g;Q^LyVooqoLc#d;sQjq=Kgj@!vLKlj0Kpx)u?9K`?g<2UZLIOeC3u3n)40<# z?lS!TYtF2DX3jn9%-s2SKUCMMTD$hH+WVE~ecrcsh^n#--ZS!N0000_PF7MK0KoFT zKMtNgxWBVh^dtHH^vF_7Neloek9vM>^7#H5$4OS#6#&3*|L4H!cFg|*07#z8Ns4KB z7@_B}yN`xqcfLIR|s&k5lN0ARpsCJtG}2LRb`Ls)=<7eK(9SK`=!9Pv%SSBpppi&A*1MKgZ8Vnfv?&X#RiutY^I6OH1Q5+0>kwj z?OF!x)Bbq6P^odP>CH#^4421SmE1mI!tNf+_hWX(d#G&J2~Ry*)!XP z%xW1emoD7qsq(@BnMwJ9&#r)z$T)#mc}n$@rB|4la}pt&l`*8O(()(a&5Oh4$#g^S zg!SgeD1v|#88Pa!&oMmj98Nrh125GxeL_0FSM%E|nN_kwg=!ficiI|Il z?!hgO<{WXV2Z3IAC{3{1`hKQy)t9=9lW-qh)4B}%ff{9l(_ELzZ*OI0r@v~W)JEaG zDGYm{8Vi9&jEXn1`UlxZ-(S6*@6Tl1wTUvChom)U_!NrsWi`b&xgu`e={N2|75>i#(C0hAY$@2%Vrb@_a{_`W6+JN9Q7>w(`bFp_8B zki(sc3dnxkvRtqAGYlKKR{4?(9@-<_&6Op>rB*lhcP5@ALxxg`^41jc#8^Yqu>Mqi z>ZwwnmME3Zu;`|ReQ{j*<#UL>7BSK^iNm~Ob`(>e*2vD?Xq{h+MXW2>6 z-6}TWthm1AU81q9gB7mUOWVw8@lh-fjG%qs(loVh?ByZ>Ktl@eI&xL(XYmTiiftpL z-4Y8Rc)$&|@J;U(A=y0MxY#Kf`W{Gc6{qqLP#bXf{Rv>T6(;r&P(cd@h%))(17rjK zV?BJ9`4FJ~Uv5+w3NtL2wj%)n0I^@V$UA>MYbkjN_#*LdTK&J*+kfz0hccV1#c8pN zb`#r_ozZ7UGh?OrhFNddB{1_bd}l`n&0>i1xU{+3TVhc6?N1v%)8iZ&)E_<*qN2i# z>pm4*(4^(7km=vf%e~#wbb?JwSqW=y>T4x$>}kK{77xHLk}s0su_Z^tv(b9|f+{TJvn7mgx-H;TK%EB2cL&g5#6y_@4YGOm5{G>_Rn zQegqrj0F5fSNFEf4W17dG(ss(NAGNT`TKmG4Olaj_o;_!JB{rxd1ocsg3CGM?D#tg z%vgn5fw;W?Y}&Y2|RMB=ILkIO}!d*IfrVOpBZcb3RM$$RETSkqs#!BtPRpiC$&9<=`=7oc7$*F3Dh`DSD?#XeiW|s@x zXQ=ZwrQ|c9QPLCDzjZ(Q*xo@u-<~YqR4w#R6xsNK*(i(xwDEvI!vDKmhObt*0YWW_ejafFIZOi)kvc*r>$=*I5N5n(0p)bnV2n1$A|`U}eyc_`E)a!hk8seU3f$ih z&iLa*-cpz0pgjT;Hazk=9P?Ge_dXnJTA@*#XNGvZn#icTpIT|cYF9ra+TPevk{0$f zobkEVhz`B4@`We1Kw7i_goEnxMDd|>NsZlbu`b9>w-Y1(X`#`5yK)EO{wD%4TWuy3 zJ%rDZ`s-d%97r@xAvO~%pLAXp)&JP6rfTz@P46h-B7dRO?2_~XZ#H^u-Ph8DeV8`7Yp9d1`zGP{%NV-p+KWcJv-%vS-_5GX2>TrV&RYwMvviYf zY%cN&sk6m@N{7hbZ--bPDqq7l)17q*dL{4iuY9+5fn)@b{JU4F2Yl`xY7+* zPWp8mZ!Uu^e7absWtwXo;HIE=eYxT{;B(&L6GN|5zmf^NdCh-|xvOOsj*furd7NGp z*!y}pPvpqOz!_k(*J(2JC!rbkZ!g2sNMP%!*l^aFj(TS0%2&_PJkG74cZb@7pCE@- zGFqv7~ z*a~qhgmi3{bRw(`t}~VCg%W>v5xvG&RXKOT!pI~(P@W=T=?`pde99M6ST+y5fYG|_ zRJWH%Sab8i@5dmkQ8r#>Pp63l=AG7vH^<=L4=2;`??L*tY}fuX!0+e(H&KJ@8-S=% zt0Vp+!1Vtb3;cgZ4gWo=V9-@4<;~wSW{)!qvAq|J;h;-#z>j{WC@PkUxi7upb<;Yx6%r2;nR-Ou+R_ClQFu5 zU~>T0@4HMh;gj}5-ANu>@Y?UKsK$effns9P&Sz3}9&edYem($Eu%xSk(EKT{5ssf@m&b{q zx*ND@@A&jp^q**|2$tY7p=iwzNklI2>7w%CWBPLWQbzag!quOH4lgT_=%MJ_#-7R+ z5(S(7$;!3y9DE_W{7FWq>xt11=N708k(T6M`q>Dm?FOm}F5_-%rJ>CqYBg(lJ!%bW zPv-u;)~b>T+V2N}NRK)aZ#pA2N1;;rVOWb58|g#SN0)cgZ#NK@4i0N2eh&fh3^4yl z&m$=NE(k6ycr+EfzO9j4+>*Ya-s1b=>QU&$7=Z$&wq7d1D@w?dnB7#{`%C+XF3wEq zd7%AOWMTujoe*=jI4$kkeLU`Xe7rpm?=wYeuY(%2MTGJk8IaOXBy_@*apb}T?RKvd z8OxEV~A>wM*f^by5rZ~^;S0K z&rhG6brZ;`VHQWV2-tOz4()+>I}-@K!~H)~Ltzw_b6jS*y*xbYHwt%C8zsnMpxTW^ zg_8TOEL_PFKRj=h6K%w|8V5t44Mk+wcjKkhsMWb}jcZW4&^z|!?RM5E`rPp%Uyt6D zK<2)S96p8_ydnAM4%v9#1Vp9_wGC~4i%HM2dDa`VMLQxtx_+@j;=Q6z6QBvLy#$AB z=pcaTozB1Q`_|a`SxK0!DO5rB20V-|HunUar?@3%d38>g>>6g}thTov(A$B4j5gEx zdD6h=ciEMJZq|wnEiam~H>ZsSj**AWwejw4`TBK)HM=clF@nVS)#Hp{dV@TPL)1ORncNxU3}U=m!@8|loB`on19I7IQmdYFw&|(17t)ZmMLIu+5h=Vzurg<6qXg(l zwyB`DkBk>A4}{BQK_^28yJu(81+)GG&exxRU8PRPnNmLH?qqK-0<~C%71(elO1E5x zH!u$~h(K=)$+bQVyQVeNG&DfXA{?$qJ-CIma_onn2$C^hoS>-(g@gy%*U+cL>vLub zRM_|98<9s*IKyW|D}_;w#)gZGUIW?0GhxBszTf}Kn{+kPK^^KdGf82fMnRr@#8CAF6-6L1T`xW971;)%DaF6 zs@-Y++BN1WnoL^WlifFvuosZIa`(=45IC=|&bp<{OL|@~$(_ z=R$nQjJ?aftPk6Fd#ep+d8+1#IOAgva2!pE;YX2Emx;WJI`xl@djpbGk21}0qgH(a zXijI6pqeLJu-sYZVSaw-7)&XQ_?*>%w;pxpr`NT&U(&zb;#0a?e5+4ZhwWZ?=a4om zx4RIF;BPSJlD{d7zGR&hV{LnzT!ZG{kJG20yXzB~inl*ms6LUq5+G@Tqe?iW*H z+HQH;JtZHfSH6ixhwY+9Bi|L}9f%8ma%(Z`&umW@Dn@016g{^}=S7x#r29)!k=H>? z-YD#^=kLImbX^6+y1gO`OOwQ$d#JlYUcaUbs5rA)Jw4xC)9G(`^2{x0f7;klH$};r zcpPJLN;TWI62z0&e0|t^lovrBI|K)f%Bl^x6Q;_vRIN#(zJX`E34evn8aM1*vW1jF zFTVo5@^Zs^=X~Maz4mWQtMu2Y9g?OTe(*1RKNvVol;S-L0sHFxAs#*8iL~EZG;ax| zYd%=QkJ*4~Xx2l`8-&)bt+6Nw1`&fbZHf%;tSvG(jRe-bB2>Knnd;XrXQ9Vwx4Z98 zs<;pdd=A$AEmSQ9A9_dvrX?AocV3&bLi@exSsWj2pCUm-?$&SCqk}c z?x59$lT)#T?WL}~p?y77@pxwCDt6FxP6l&ja_7If`IT4?I;3c5i((k801^wk5UL?)Le=Vy=mSp+xQr zGmvHC#yZSxoh|PmuR&N8#z4q@=(^`7X#qt{tu4|iV_0AUyPBA%Q*vEu9o!i3QdqFJ zhV48Q!5JK1EM;~W z@{huvbevy<4s;%`uH>-C6}xSllH65zxA_OeUUCh^bwibPw%0=sri=U9w>$E8Z1;`` zou>D@xv5&ghhhBH!!1mO5x4QNwK-aJ?Oq_#8>GQ ztYd)F6}f`qOK|^u6P?%&7KpTwXz)oupBz`W;5pSL*zEdt7dEvx`0kiou?IxiNKiB) zPo`RVyCBE6gw5+d8xDN^zVM?>VLcippEvFK%s+hJU=|BQmQmXM!3~%qpLDvYW_ijm zuTW0MP&J084ES2AM34w{7!~z};NwMQd(gz((y73^G(3H<-Q}6z%H7h>4@KumiHNcS@B_8~XF~nI3ZhjVBVKcuwtj!iB_>kzaf#tPj$gD%YWiR` zMkO>+FTgSJp#e!@3ZE}}>kSyN&Dvri6MIh&{!TQ;Hiz(nM?m#J@^mka;=aHLT8>vV9(jCAix39B`Pm+9DNv5 zl=%C_{YU|Ow;$jvK*ff6W+-3q7!-u0$*6Oigyf+X~X_RrX`#H6%wC{fI8eQHGUuUA5GlNiLFFigquy}mv_CE{XZEZVK^P@D+p{!X7JpWog8q9#d)R)^ z7TF28I?!R*|0yhhPb2Jou-wV2U(L6MPls=ukeZ6Pab&B7nopJ}c+=A`4i#9@h?ZrN z3cS-THKaH$J3GzR$G0_)R)=4CPgGDLR3f#br}+9eOhmVj@!F>|8&8<>QKM&}V$+wa zY*qevlax&TxLSsVA{ued9eHrOj{bRsn2jd6j21W48*M>A%_(FQ~sz1;&>BiJMkA#OTWlD{j z>cLy$gq1#5r`a-{oGJ0~HIN-G`4k@e83QO9+1lFbU1$7QXY(V}brVMcCLkqe^hLq% z+vsNa+~T_bZo;qPShBPZ-V>TqD%{jGjz(pwnCQ7*mWHu8Yt?zynE7FefiZSt$;{1d zs{teXJ2p!9*0W3~YT15}Q6RrPCfB|r&6@Bk&FBH-!V4-iFC;C1;uv+4xR9)~!1Q|+ z5NkvZTkXy_xX)JGvm4g`N=%d!lfXLeWp7Cl_Ng~%51ce`Rl27QB-LAT7q?R5kqQ1heztqzkWwbc5e>%p^5(llOcm zK{wpPHYxUxU#O=CPRggkx3Mhv&943UJ*mPzm;25W0nbU#^LsuxU$bV2%TQ7EnJ~CX zDTQV88rqP|Scywua!utsQFgZEMH%=zBz&K>`KS78YYe;ni`^V9ne3wQC1eT4wYvGk z=JOJJ*~YvyIRfuLr#L%{f?Bf!$=ARb@cQtbeaafJaOAT`XMVisnGInU59>Un^Y|t2 zPu&8m9T~MHh<4(4AZ8g2J?8u_(lUCsu zRq0T>^hCucrPtkK&FSyrK4lQ$%Lw(U8^-eLon`vPKf9b!XPz%6+pril+J{U}9ooD$RCn^Zct< zn0R;TL%M|}ZGFJMOld9jF+g)^ayS2zKfb7t%y9c?_ZdzLevgyVJ=cOi2?k_L+?(3} zlPrxU9<0~kisk_pfJ;nFY^J8~pokUl8?%4^`+wZ$zfaTt%d3pv49Tf%%^+>%>l0j~ zdcswNm@FtMm8dXoX4TpoYiVcdbk!0?1iwqZ2R%?-7{3^jTlhgvLz7-lyO?BiIr~BW zueannR!QNbwOB3O$&^Tp;j$c_`mI1k5@&`DdFh%IDHj&~ zkV8DvD6?a_%6gPQZ&6)zXy(LofiYY<2sJcNXqxCw4bexjwUJ^zZ_K& zp%V7iDgKeG95kRNPu@ehO{2Q~#kXF{cBJ@~@`5czCGNsUAx#$T+1=n@X}uRoQa#JR z6w9+djevHqbcEyB%>-`2#eA!WX&U0Tw}~IZVx_9UkZ4&l{Mrx!CfsLO9Su3+4uHh<2flCmm{iCyVQt2#` zGGWnxv%7f>Y+lzS@r8!R+K_wlT@is0q`rL-FJq)iO%tx1dU=MSAtDlgzOuZ`=x(LA z&R4V-h;Li}%+`+5cCDT;F8+%%jg0aPJmUQBBaGprEi^n>{cFmD;6yU!-sk z4_Hp%KnkFZyBL?82|wfk$^!j^sD0@f!A_tPm6;b4{#l?7{0~+9cW-w5Z@E0DYjWFk zc3Q$mW$f{}(|mnLbYJiqMp7D$!rsVSzMC!M(n~Miw*+KVc%AN&(a@-5GoiYNv*)&E zYcPwgetzG-JQ6p=qdJ)hA2la*Ta&(yJv zTw9IbGd%Wkmbyw1RJzRfYck?1xN)JnViX(Rl(ZLFED#NU`gmVzV<6R#IA~|8JiGa7 zMH|8e&fj7xi8nv6N%>C8 z<{H2w=>JA~`9DYq|Br%^{|w(2u4KkkdTF&4kMo-j;2^-2Y%96`@t-!0s|$d%$yYWP zT?)BHm9vE|`l;L69j8QfGPWUuLHQ9Iot8#MgS|TQ_^B!O3vSB0jl0=}jdmO>#`Apc zUlR&O!S-d5n)M`wF_o81?>JF&wFlz|9Ht?Ijh2amkh7EGciZ62vOcnp?xLo8Pf#p? z?XY@Epv^k2>Jz>^e2h;_e02z&Kg3M4Qfp&|^uVD8_>=aTm5}p1Nzr_hgw4)4WLr3& zGTl|L;LwOwpZ`Q@NK3;|no}k3gSE-x?0Nt==*1kRLH4+~plZ@2HHS!T!Vu}Q z3{nWFWy3A-2r*WeJu7{8K8KcLaVkiz?ztr*?50vpY>1}2&QksAB+`=zg%pakJvIxH;)EbuE)dEM=` zy0^e&Q}c37la#ie?OP`;+m1_}%0Y5xS;1CK=#NZ3wD#SSW8sKu*3rVXyp($N-EQBf zv+`LBfunI#^x)joveX^79OWH1Mjo&$mzIwHpHtIWNn_C(3J!EV!#oj#9dmh)FV`g1 zCOz10?RaO-Zt--pYRCkpG1n}rFGtK)8Wv3vT>uh`X{Q-RM z6#VJa2vI?L`_vVXA@Tz!^{ea~teXDKLgm#}YB_^9(gPeT7)1>RO7JU+P6L6eou39? z4|{}0AYJxpHJ`hiN4K)RSGR_kZ9~gLQDalLb6UMh{9Rg_UaCQnsj8%PbSSK8ffk8! z9;*60p{;9&fz9+MCu-#z;nd}aXdW%TcPme!k2K_cXxwIMPB&l2ckgXkyQ5-MAfstc z1e)cQ5Pa~Uk$GKX^kq;Rb(n|STnpPk@7!2I{YvOuqIiEMmgI31uzp)c>ba&W+p`D->W*d%FGC?4W^RxU99$}u#d zsPUQcUwPLO=f`lOS~YEnpeBheJr?e!sxz&PGLoG6-kx(SGLaZvnP9EL(XrZ*F`QDS zDRH5`=MJbc&0X|dHOEG?UdftwmtGxTtnPWeDIz(IDCqev2O0<$vbNpXRXd1$3t<7m zu7+h6+}0h>HMUjF9kJ_S-f5?+B@PbhP8iM136{g}K;qMPp={r)`vfMnE0nP3A9%5Z zvWEIB<#p};v>vRN83)=lfn+3t%p+gxI~QNcZRq!%%UDtp-lT`)SM&bHawP?mJWM40 zmFWJYZ*xGD5+z zzQO|h{a^ogG#KA+GG}RFB_haJ&Kui05+RX;<-<8SzXIAgO zWE%g~2*LxdXfeFBuTG%f_#|@u@hBw$>+A1;w0|oL`c6}iP*^c?53aMR)L}6#tY=%b zYZ=NhwgYkKd%GA{j}%z$JmuNb`&pg3`dXmUljl&cc^z84YFbr~75k@;cTcC%BE9_R z6~iP#fXo`z_&v7FJ`*k8@*Wg2pxa6^YD7-6)=-6TbA18WH&j)9amQ|Tc$W7>Z}X+4 z!_=&AU)8Y09*Ekn!wZ zH@O^a6rReIOIQx3QN>8~FSzj*LEo|xI@DN3$o%4C9z4?wQ=uFZXDOC7CwKs$9-AIg zYv5{-j9NEX@4}@k@tc_V;7O0KS`nFQHeCgiS=-A6wd6{TcG+#ul^1hvXFbzcdIMM# zo2N4B#82gW!yXZ3tz{v}aCjSF|G5fmu0=_~%^lVv#4*EjJPZ^u>NFA(UDCG`q(OsJ z;LotJ00jS&zXkw!XlZG$#SiZ}qw|!1Ti*XQ<60Pr`jjpLYfgCZb*9p3Nt{Dd=u&_E zpC`_#gy9bX$S40>Wnup%zg>8b$L^)iz~;zgoxi-w?F+2oc-D>s4=@gjhXPrjD5mr) zhWF})NE4{4JZjr7)D5a7@w`CJPGfx~C%PW8HzYr+m$=6zou-7Kan1iBmRI1-R)YA_ z3ts_Oj{19v)suLb{>s`;EV7(47LvevYhs<3MUtI4b9nQ!`Y%l=bmXP}dVOV|_evzU z?P+=HtJ!C(ul1#GwMP8ovA!}refDhm?y=G>YD{!<>kFnwbs5M7O6puF!LPAUq5M(f zXSmKEU-wQ0x78uaab0k>wqTPcRUj_6>QAr9LoS}_Dh`E|SY7AF&0R@4r3R}i5d}Hb znt@w4b7^o^y|CEdnZd~iq9@~PL;Nx2>P?X;dG9M;(2kLsXj!E2y9%dw8AfCmguB&s zdsa)NL_>BdpC@fL`(%#;dPGXQNN+gbe8Z@#nU+###F^EfttkwyZ9r5g$&1)*Nfv4v zfLH+TH!s-U53VE9i7aJ6&F|H6M0Zt_LjGn_s$VfFSH6WD4$5?QbahFNesNQ#?oA0@ zOBZ>U2gtB^+_oQ@JrG1RpWyD{a=bznniO){9O@gFJ8)shFOM5QqL3OPAi2)vQ252J z3a`K}o408yVee|3kTO$^;+~7-rhmiszUZx~pj1{p&(Ym!xnF)o|H>|GS%Kt>*>7!U*_5A(~ziJ8*P!$nK4g)1LdYRR# z240LG%D)WgHM+s2a!%UOnIqh>>*zvCYKY5>jr`QHe8*Elmqi53ii_+3Qj~KTlx*v7 z1|R9UpT_U}Dmdpd?(BV0u4~VQi2V` zbE7Si3--$#aN;i@NFQC=U=s|jm6mR;HCO(ly{QJv&(^h{V&?r3x%TgR--_dE zs3bxH>$B7R@l$WE8DM^EmLl#;(VkKk&NE_1n1JH&fSZgn&_R5)iHNmb!2mK?Be0tc zejF0)@LFC4Pe$o`+B+0ezM>L04m+gUfREa3}XbjK1D$b z{fX(f%s0j#>aFZY3_jFpOjPveX1-&e_mWXPBFXAPs%@B7uBn<#l`jVq**>L;5-A01 zFpGRj9$znB5?iyE2y5VGa(K~`6?6nj%`C~BOlqmcq6A*X{YKf3$BD|Yb0mtOAl84i zKm}4&SSNF(=NdduDDsSmKjk`p@pCe>VpO0xHL|lM(#W_(w}Mi80aSB)L`FSXOF9w{EAOc2T$gPxCd|rM;lni)a^Qn z{b^pNCn;)WD!~nf&k7X=$KHHooT&ZtojF^+Olp3^UfdEl1EITo0@AK-1@&9VaOC_mZoYU zTeV{@=b0+K!cc=9%7csTxIq!>my~u?+^wIsgmcX)I)6j)go7Q-(IR6rS&*Sp>}(!y zlO`RHt!>|`gvl7IQJXC$7sS!GwxRTA*}b|#AEFCuUBgVz=U?jDRY+2VS;zgW(T9AI zO|*YY@dXt0kR6^&_w8?NkyYZYzZAlDr&)kp`)SUro zAy41f6c<$%)aL&cA>q1MeVFUBamfF7B7rT-${gunOT{(kw64*yUMd0m%knx!w0sQ? zjQ^EXof_7|X5V)y!s1kTVmJDkxkd0;qo6$E;4L~|dK(uKciQvh&z*Tmdoq?(@&u8* zf&!@nOU_yCD6N%_SkMaQLh|Jy*+Ngj0G_dL-f-YxL6N%f z5g{Fm!AnF^#%F~QMTKQFp~&59A&l_dxaZY$F!4HLc_sOuM_Q#v<|fI5JOw|25*bf( zhL#xK-i(nyOf7b_Z#ZA0b%qu`8OC?NbMSFkkpe!V3Z8lBh-;^5KI1nFlqr_8Z``u& z?O$Q7QQSXxrX0Y+8_tq0$Z2|))^+ELqJf~?X^%a0v&rQl6mDcn{MyvbN8Wxm6J!r$ z7f--daiW+A{mL-Zl-eFVCgxP$`E)w%EsDB|*Q9*LQW=7Oxz)WnHi>Au)Cokf$dA3) z;l4q%OLL>j*p6Z8V|J-31{(OWJZ7|ND}L0FpgQvi-+qDui&R?GM~a(6y7|aQULrYh zsv7E-HKj`1K02^+#z4}VU#YB&nO&*@10w7NrT5FW8T!~YHg7mi?krTCyC>Qsc?7B5 zo6DXwxIV6tsvxfch{fKMBXeMXX>{*pBQqT;H#jr)!6cc8YvvJ8-YYuDS})>*A? zM(m8H2-#=q;|$`skE~5!={;aC9j{xua(rZ5uj4x@!OdOhqA9#3?AJMBw-v^%x#1H) zXbw(0M_!CBy)@T0$!40opO??x%0cPh!E7VYH;^_3d3ojZ7A|^Z)!b221v5ECP={0q z+290vpyf;9Vq|h_Mlrc#xHZ`;Lc&a+{EiGFx;bZr>)$OPfIn)A;muPnw@8nG=3 z-1O4xlkr#+Dgn*Pfs1%~p4FQ8k;?*h^IF<%ouj~|*h0=nk8U==4?ckN>4t6#I9|Sb z)l^Zi-`+&M-~%VHO8ayf`r?)wyR>@EK;DZJ{(X*v@n)9k?)Cf+atRNQ+F`5J^o$eK zYlEG+{RNog?Kp$^hdZ^mZ>4BO%70B7Z;rjp?11=bFYw*DQ~IAiY)spXK3E=P&G6B%gVQWg;AFVJas(Df%V8Oft`k zOoeQ%RRe$$pK-npWpb)WY`jhV`M7ia?Y5eyFzqa_`GTI;X1f79%tKDR?g$fthE^Y3 zn~?cs_7zg{nP+7atG$Y4TofDJs3{Jj5>9@(czm`RaOp^&MRHS%CBn>-vGO71| zYY(QGI1FsHWNhSv%&)4-$d_d~eE)W2hKJq@jXhp)R7!`}sZOK1T03T!`9=r?A3B5Ij*wlfIF~m*QG>}S)=31RAU&c%d3k>*9un>dE}B0r zb!|^2o7i!9xg!Yu+?RkY9hHYQ)*^D%_rcvNj%1|pN+v4L)U?3zzDaQ3jCLw;^s`KH z{-1)~hv|)nf68wJC~B;dVe9@ndY)Wuz*4S!ja1sjM>;A&Lt7q)St{dcLp25QB3VC5 zapU{4W(O(!barE#2QuTI#gZU8pAF(ZdHA?zSKq<8hlTUO>Nn`Wm^OrHaB#U)XJ;qonb#&)EQd%@t3|I^FQB;HF1xIOy%ly_U1(# z9XHmZ7*{Jlg`M{xrHl7PUsjCspO4Vlj;@=gYTu_o2qGzX?K_@^Cj1&3yF*Wvb29H> zinK>}|1|sDIqvE(y$SW2%t_45sRcrK^tVJ`<>*Jy3lz3f|70}dgJDA24N(zaq7$0u3h1u zX^4Zihw?*OHS|(}P!=0ArVp136weu*t?D6mXN_~l?mm^(Z6e(})*P%A;PLKg(N zAu6d|r3$?Xp)Sew@9ZTZWuUoES!kH#7lA|3=Q?Y3TT`@n?dBe!kh3Zbf zCLDgYn!=*Fm)o~*MBv$(XF`6NOpZS0Bs(Z#x(1gph2AeMt`#+QUL(Ce6eKgn*Lw~2 z*aU}L*>DwDtu=9aiN6h(#;G9misdEe`DtS2(yp1z{d+e#zVN}6-eYT`sK zb(W-*n}xY`cHx9la&wcf5X9BWL#p!rF0Q2t_RY<(5c7m~-M~U@hb|NOSDI_*l|0Qm z{=}v`0^SIkPfNFf{dys)mJLUrMJGFuxE$Lp|3Q*;Hl>`FbA8fwmcHuv|^xXS~*BZ zQ>dHe8|>NjI-hf;jux{g`Ri8I7>FxW;b_meAO<@7^n5z?Xcq6QBNNfB@n_ z`=gOVTe8w_OxYjG2Y9u)ctStKj@kumrqGNu8!WsI*4g;R_@(G*ORODlmbrPo(1+S` zB43RloJ@k^aE(i>vTA7SNWkcIwXf^xc5_u`evo|e(LYL67aP4{W(J=l^TZHmF8#b3 z8CC3z?-*hF$T-|HEk927K>4#j{?Z0bYUF)x{~_>o@vGA-Dj~tf+*^f>sOxfBOXd|vw$|h9z{%QEWhK-`8GTw*drU12i)%x?En&Z zkRb+IGfg_trr+H(UyuwcC7;%W5I1>KTi$krsL;`|CUeT%dxAeR+7o5vnYv)ewjC}G zer4euait@p`({)^LGPW7_A5<*m`E=nem-%eD;PSjh%pd{m_li`Hwf|<9whadOQj)tHomlv?PxE z+{DH9G%>Cjy?*Hy`?HInrAfcQNuxjBW=a}Io7tf9j9h%)iiRq zak0J`$=?F0pJyyk{t=eQx_T*RVzRXUu5-{K;sxDxU|KfK*L8j3Pd zs^3Tp`d}t(ZLoQ}W#Omd_jD*9bSgirM&%y1ZWw9c2WadMuabXOok@;zvsN{j21w3dAR%!xyjpZqTN_iA#DSIRWrxR=&*OplfQ(D_UxObQ9YkLOk5s((kry)m;1oSGt5L4&Ix$ke zt*T>*-Fw?tcf$s;ChKar9D9oFdLw`S8#zF$T{SO+smmDC#)B6g&;o0ou{~c7WM(j?JXLUU|Yuthl3Orw9aFL!j za=D4P&36U+kCYz=GitczDT~K$wv~73XJ3i{)VHGP*T-uY$GL7C8~Qc&<={-BaUE-#c5Tf>6Jv{^XiI_x z;K_9Y&TWyaKaw%$xGi9DeH*6MIjQmd#CayE!mRz7P2qs5rM2PYbyLB&m%vMqsu1lo z%iT16&j@e52dG5t#@t=gvP@6HFP5RSg|DiTLp>Mys4bn(SCVGyx$W#>dM9EgWp}=G z?Xa`;Vgte999r(O(D$M*?_RH%()0E__qVddaBfRnYmqr7TLrGa1Om5Df8uE=q*%j} z_eaPJILDm7kpulF_3sCMBt&!@V?hzb%v<#V7+0FYRS;YG>|}G z7_jV`8!v$X8|Z9L05-58YSdJPW~A$SuIWc%o2NV-WX8!n(}JrUz1}R&3p7ZwB*6$N^Ly?A>6%3b?e`NIY-E1h^TcWM1&Vr;0s2mBAruG3o0ooxqc_mIue8}p3>?N0 z*}{Rfj1MuLR@+hpAEu870ekPImit)mh?KWXBFGB;ZGSX%pS~4#sN0;fe^~q>A5+vQ zw2y-V@F8E7S%`Xgx|NZ)?PO??K`cBtjMAuZx3e#afs`Vh<0K}IL@5(Q=X&CF<1r~q z3_`=?fTi9oc>O)I^XR^Mn9fK7@1BBG@p(?wHs~TOR#j=dfcunPV)MZEyr5KRA339P zH@w}x69|+L(jt%u8dY{jkGZEVZXO)LMX&xAFz50-L}=;Z88Yx80%uf|4MTtusd)rc zZY>!o)j>;Vm(ke|-E7qPnVaOSdloc7j{M6_msi93rk~#2Vaa(iK z>Dr(E(i&;V_kx)+tUw_D@$!B?r@@nLlTE7*oV|zTVpMOKR{zHd%K)|Qx(N`a^8Yu! z_dlq2`Y-<-{$F_y&Gwdrrp87CMJ&M7BW&!WkfghXHvm9mzT+LmBfyx?Kf7K0-$~>B v|2_R{>yUpxndW@;E;$L00Q Date: Sun, 4 May 2025 15:21:30 -0700 Subject: [PATCH 06/16] chore: moved into root, cleaned up initial set up of ui elements --- .gitattributes | 63 ---- .gitignore | 357 ++---------------- flatfileexporter/CHANGELOG => CHANGELOG | 0 Docs/assets/ffe_01.png | Bin 17756 -> 0 bytes Docs/assets/ffe_02.png | Bin 11579 -> 0 bytes Docs/assets/ffe_03.png | Bin 13959 -> 0 bytes Docs/assets/ffe_04.png | Bin 17750 -> 0 bytes flatfileexporter/LICENSE => LICENSE | 0 flatfileexporter/README.rst => README.rst | 0 .../docs => docs}/assets/BuyMeACoffee.png | Bin docs/assets/FlatFileExporter_TemporaryKey.pfx | Bin 0 -> 1708 bytes .../docs => docs}/assets/ffe.ico | Bin flatfileexporter/.gitignore | 62 --- flatfileexporter/docs/assets/ffe_01.png | Bin 17756 -> 0 bytes flatfileexporter/docs/assets/ffe_02.png | Bin 11579 -> 0 bytes flatfileexporter/docs/assets/ffe_03.png | Bin 13959 -> 0 bytes flatfileexporter/docs/assets/ffe_04.png | Bin 17750 -> 0 bytes .../src/PythonCodeBase/gui/counter.py | 58 --- .../src/PythonCodeBase/gui/pubsub_sample.py | 26 -- .../src/PythonCodeBase/gui/routing_example.py | 40 -- .../src/PythonCodeBase/gui/user_control.py | 19 - flatfileexporter/src/flatfileexporter/app.py | 26 -- .../pyproject.toml => pyproject.toml | 0 .../src => src}/PythonCodeBase/DAL.py | 0 .../src => src}/PythonCodeBase/__init__.py | 0 .../PythonCodeBase/flatfile_cli.py | 0 .../PythonCodeBase/flatfile_cli.txt | 0 .../PythonCodeBase/requirements.txt | 0 .../src => src}/flatfileexporter/__init__.py | 0 .../src => src}/flatfileexporter/__main__.py | 0 src/flatfileexporter/app.py | 17 + .../flatfileexporter/resources/README | 0 .../flatfileexporter/views}/__init__.py | 0 src/flatfileexporter/views/connection.py | 59 +++ src/flatfileexporter/views/export.py | 31 ++ src/flatfileexporter/views/main_window.py | 25 ++ tests/__init__.py | 0 .../tests => tests}/flatfileexporter.py | 0 {flatfileexporter/tests => tests}/test_app.py | 0 39 files changed, 169 insertions(+), 614 deletions(-) delete mode 100644 .gitattributes rename flatfileexporter/CHANGELOG => CHANGELOG (100%) delete mode 100644 Docs/assets/ffe_01.png delete mode 100644 Docs/assets/ffe_02.png delete mode 100644 Docs/assets/ffe_03.png delete mode 100644 Docs/assets/ffe_04.png rename flatfileexporter/LICENSE => LICENSE (100%) rename flatfileexporter/README.rst => README.rst (100%) rename {flatfileexporter/docs => docs}/assets/BuyMeACoffee.png (100%) create mode 100644 docs/assets/FlatFileExporter_TemporaryKey.pfx rename {flatfileexporter/docs => docs}/assets/ffe.ico (100%) delete mode 100644 flatfileexporter/.gitignore delete mode 100644 flatfileexporter/docs/assets/ffe_01.png delete mode 100644 flatfileexporter/docs/assets/ffe_02.png delete mode 100644 flatfileexporter/docs/assets/ffe_03.png delete mode 100644 flatfileexporter/docs/assets/ffe_04.png delete mode 100644 flatfileexporter/src/PythonCodeBase/gui/counter.py delete mode 100644 flatfileexporter/src/PythonCodeBase/gui/pubsub_sample.py delete mode 100644 flatfileexporter/src/PythonCodeBase/gui/routing_example.py delete mode 100644 flatfileexporter/src/PythonCodeBase/gui/user_control.py delete mode 100644 flatfileexporter/src/flatfileexporter/app.py rename flatfileexporter/pyproject.toml => pyproject.toml (100%) rename {flatfileexporter/src => src}/PythonCodeBase/DAL.py (100%) rename {flatfileexporter/src => src}/PythonCodeBase/__init__.py (100%) rename {flatfileexporter/src => src}/PythonCodeBase/flatfile_cli.py (100%) rename {flatfileexporter/src => src}/PythonCodeBase/flatfile_cli.txt (100%) rename {flatfileexporter/src => src}/PythonCodeBase/requirements.txt (100%) rename {flatfileexporter/src => src}/flatfileexporter/__init__.py (100%) rename {flatfileexporter/src => src}/flatfileexporter/__main__.py (100%) create mode 100644 src/flatfileexporter/app.py rename {flatfileexporter/src => src}/flatfileexporter/resources/README (100%) rename {flatfileexporter/tests => src/flatfileexporter/views}/__init__.py (100%) create mode 100644 src/flatfileexporter/views/connection.py create mode 100644 src/flatfileexporter/views/export.py create mode 100644 src/flatfileexporter/views/main_window.py create mode 100644 tests/__init__.py rename {flatfileexporter/tests => tests}/flatfileexporter.py (100%) rename {flatfileexporter/tests => tests}/test_app.py (100%) diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 1ff0c42..0000000 --- a/.gitattributes +++ /dev/null @@ -1,63 +0,0 @@ -############################################################################### -# Set default behavior to automatically normalize line endings. -############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore index 2a47ef8..083ec02 100644 --- a/.gitignore +++ b/.gitignore @@ -1,275 +1,38 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# DNX -project.lock.json -project.fragment.lock.json -artifacts/ - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings -# but database connection strings (with potential passwords) will be unencrypted -#*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -node_modules/ -orleans.codegen.cs - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class +# OSX useful to ignore +*.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + # C extensions *.so # Distribution / packaging .Python +env/ build/ develop-eggs/ dist/ @@ -281,68 +44,22 @@ lib64/ parts/ sdist/ var/ -wheels/ +*.dist-info/ *.egg-info/ .installed.cfg *.egg -MANIFEST -.vscode -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec -*.exe - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version -# celery beat schedule file -celerybeat-schedule +# IntelliJ Idea family of suites +.idea +*.iml +## File-based project format: +*.ipr +*.iws +## mpeltonen/sbt-idea plugin +.idea_modules/ -# SageMath parsed files -*.sage.py +# Briefcase log files +logs/ # Environments .env @@ -372,4 +89,4 @@ venv.bak/ *.xls # more docs -*.pdf \ No newline at end of file +*.pdf diff --git a/flatfileexporter/CHANGELOG b/CHANGELOG similarity index 100% rename from flatfileexporter/CHANGELOG rename to CHANGELOG diff --git a/Docs/assets/ffe_01.png b/Docs/assets/ffe_01.png deleted file mode 100644 index e5fa22505ac45e9f28fe143f50f333342ec8268f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17756 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 JN@0Vq{|{}u^JM@4 diff --git a/Docs/assets/ffe_02.png b/Docs/assets/ffe_02.png deleted file mode 100644 index adc9fe5185e15fbd025e8be96c545dae7db2363d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11579 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 BhUEYN diff --git a/Docs/assets/ffe_03.png b/Docs/assets/ffe_03.png deleted file mode 100644 index 83b064d38ed67f37ad60765cb9818f6e0110d502..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13959 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 CtJZ}8 literal 0 HcmV?d00001 diff --git a/flatfileexporter/docs/assets/ffe.ico b/docs/assets/ffe.ico similarity index 100% rename from flatfileexporter/docs/assets/ffe.ico rename to docs/assets/ffe.ico diff --git a/flatfileexporter/.gitignore b/flatfileexporter/.gitignore deleted file mode 100644 index f6c30e2..0000000 --- a/flatfileexporter/.gitignore +++ /dev/null @@ -1,62 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# OSX useful to ignore -*.DS_Store -.AppleDouble -.LSOverride - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.dist-info/ -*.egg-info/ -.installed.cfg -*.egg - -# IntelliJ Idea family of suites -.idea -*.iml -## File-based project format: -*.ipr -*.iws -## mpeltonen/sbt-idea plugin -.idea_modules/ - -# Briefcase log files -logs/ diff --git a/flatfileexporter/docs/assets/ffe_01.png b/flatfileexporter/docs/assets/ffe_01.png deleted file mode 100644 index e5fa22505ac45e9f28fe143f50f333342ec8268f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17756 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 JN@0Vq{|{}u^JM@4 diff --git a/flatfileexporter/docs/assets/ffe_02.png b/flatfileexporter/docs/assets/ffe_02.png deleted file mode 100644 index adc9fe5185e15fbd025e8be96c545dae7db2363d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11579 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 BhUEYN diff --git a/flatfileexporter/docs/assets/ffe_03.png b/flatfileexporter/docs/assets/ffe_03.png deleted file mode 100644 index 83b064d38ed67f37ad60765cb9818f6e0110d502..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13959 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 v|2_R{>yUpxndW@;E;$L00Q Date: Sun, 4 May 2025 15:22:14 -0700 Subject: [PATCH 07/16] chore: adding docs from old app --- docs/assets/ffe_01.png | Bin 0 -> 17756 bytes docs/assets/ffe_02.png | Bin 0 -> 11579 bytes docs/assets/ffe_03.png | Bin 0 -> 13959 bytes docs/assets/ffe_04.png | Bin 0 -> 17750 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/assets/ffe_01.png create mode 100644 docs/assets/ffe_02.png create mode 100644 docs/assets/ffe_03.png create mode 100644 docs/assets/ffe_04.png diff --git a/docs/assets/ffe_01.png b/docs/assets/ffe_01.png new file mode 100644 index 0000000000000000000000000000000000000000..e5fa22505ac45e9f28fe143f50f333342ec8268f GIT binary patch literal 17756 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 JN@0Vq{|{}u^JM@4 literal 0 HcmV?d00001 diff --git a/docs/assets/ffe_02.png b/docs/assets/ffe_02.png new file mode 100644 index 0000000000000000000000000000000000000000..adc9fe5185e15fbd025e8be96c545dae7db2363d GIT binary patch literal 11579 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 BhUEYN literal 0 HcmV?d00001 diff --git a/docs/assets/ffe_03.png b/docs/assets/ffe_03.png new file mode 100644 index 0000000000000000000000000000000000000000..83b064d38ed67f37ad60765cb9818f6e0110d502 GIT binary patch literal 13959 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 v|2_R{>yUpxndW@;E;$L00Q Date: Mon, 5 May 2025 21:44:50 -0700 Subject: [PATCH 08/16] wip: set up menu, etc --- src/flatfileexporter/app.py | 18 ++++++++++++++++++ .../views/components/script_dialog.py | 0 src/flatfileexporter/views/connection.py | 5 ++++- src/flatfileexporter/views/main_window.py | 3 ++- 4 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 src/flatfileexporter/views/components/script_dialog.py diff --git a/src/flatfileexporter/app.py b/src/flatfileexporter/app.py index bb07ef0..2ea8fed 100644 --- a/src/flatfileexporter/app.py +++ b/src/flatfileexporter/app.py @@ -9,6 +9,24 @@ class FlatFileExporter(toga.App): def startup(self): """Create and show the main window.""" + # self.cmd_quit = toga.Command( + # lambda widget: self.exit(), + # text='Quit', + # shortcut=toga.Key.MOD_1 + 'q', # Cmd-Q on macOS, Ctrl-Q on others + # group=toga.Group.FILE + # ) + + # Add to startup() method + # self.cmd_about = toga.Command( + # self.show_about, + # label="About", + # group=toga.Group.HELP, + # order=1 + # ) + + # self.commands.add(self.cmd_about) + # self.commands.add(self.cmd_quit) + self.main_window = create_main_window(app=self) self.main_window.show() diff --git a/src/flatfileexporter/views/components/script_dialog.py b/src/flatfileexporter/views/components/script_dialog.py new file mode 100644 index 0000000..e69de29 diff --git a/src/flatfileexporter/views/connection.py b/src/flatfileexporter/views/connection.py index e79d5f6..90e9448 100644 --- a/src/flatfileexporter/views/connection.py +++ b/src/flatfileexporter/views/connection.py @@ -50,7 +50,10 @@ def test_connection(self, widget): # TODO Call backend (uncomment when ready) # from py.db.connector import test_connection # test_connection(**params) - + # self.app.main_window.dialog(toga.InfoDialog( + # 'Success', + # f"Connected to {params['db_type']} at {params['server']}" + # )) self.app.main_window.info_dialog( 'Success', f"Connected to {params['db_type']} at {params['server']}" diff --git a/src/flatfileexporter/views/main_window.py b/src/flatfileexporter/views/main_window.py index 3ff1b59..94599d4 100644 --- a/src/flatfileexporter/views/main_window.py +++ b/src/flatfileexporter/views/main_window.py @@ -7,7 +7,8 @@ def create_main_window(app): """Factory function to create the main window.""" - window = toga.Window(title=app.formal_name) + window = toga.MainWindow(title=app.formal_name) + print(window.toolbar.__dict__) # Main container main_box = toga.Box( From 57edda317e25875dd0b627ee3692bfac032a48ec Mon Sep 17 00:00:00 2001 From: eddyizm Date: Mon, 5 May 2025 22:24:04 -0700 Subject: [PATCH 09/16] wip: working but blocking the ui --- src/flatfileexporter/app.py | 83 +++++++++++----- src/flatfileexporter/storage.py | 51 ++++++++++ .../views/components/console_output.py | 22 +++++ .../views/components/script_dialog.py | 94 ++++++++++++++++++ src/flatfileexporter/views/config_view.py | 99 +++++++++++++++++++ src/flatfileexporter/views/main_view.py | 44 +++++++++ src/flatfileexporter/views/main_window.py | 26 ----- 7 files changed, 368 insertions(+), 51 deletions(-) create mode 100644 src/flatfileexporter/storage.py create mode 100644 src/flatfileexporter/views/components/console_output.py create mode 100644 src/flatfileexporter/views/config_view.py create mode 100644 src/flatfileexporter/views/main_view.py delete mode 100644 src/flatfileexporter/views/main_window.py diff --git a/src/flatfileexporter/app.py b/src/flatfileexporter/app.py index 2ea8fed..8f02cbd 100644 --- a/src/flatfileexporter/app.py +++ b/src/flatfileexporter/app.py @@ -1,35 +1,68 @@ -""" -Using a sql script to export flat files. -""" - import toga -from .views.main_window import create_main_window +import asyncio +from .storage import AppConfig +from .views.main_view import MainView +from .views.config_view import ConfigView class FlatFileExporter(toga.App): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Initialize persistent configuration + self.config_store = AppConfig("FlatFileExporter") + + # Load saved configuration + self.saved_config = { + "db_type": self.config_store.get("db_type", "SQL Server"), + "server": self.config_store.get("server", ""), + "database": self.config_store.get("database", ""), + "username": self.config_store.get("username", ""), + } + def startup(self): """Create and show the main window.""" - # self.cmd_quit = toga.Command( - # lambda widget: self.exit(), - # text='Quit', - # shortcut=toga.Key.MOD_1 + 'q', # Cmd-Q on macOS, Ctrl-Q on others - # group=toga.Group.FILE - # ) - - # Add to startup() method - # self.cmd_about = toga.Command( - # self.show_about, - # label="About", - # group=toga.Group.HELP, - # order=1 - # ) - - # self.commands.add(self.cmd_about) - # self.commands.add(self.cmd_quit) - - self.main_window = create_main_window(app=self) + # Initialize views + self.main_view = MainView(app=self) + self.config_view = ConfigView(app=self, initial_config=self.saved_config) + + # Create main window + self.main_window = toga.MainWindow(title=self.name) + + # Show main view initially + self._show_main_view() + self.main_window.show() + def save_configuration(self, config): + """Save configuration to persistent storage""" + for key, value in config.items(): + self.config_store.set(key, value) + self.saved_config = config + + def _show_main_view(self): + """Switch to main view (private method)""" + self.main_window.content = self.main_view + self.main_view.refresh_data() + + def _show_config_view(self): + """Switch to config view (private method)""" + self.main_window.content = self.config_view + + def execute_sql(self): + """Simple synchronous execution with progress feedback""" + self.main_view.console.show_working() + self.main_view.console.log("Starting execution...") + + import time + + for i in range(5): + time.sleep(1) # Blocking sleep + self.main_view.console.log(f"Completed step {i+1}") + + self.main_view.console.log("Execution finished") + self.main_view.console.show_ready() + def main(): - return FlatFileExporter('FlatFileExporter', 'org.eddyizm.flatfileexporter') + return FlatFileExporter("FlatFileExporter", "org.eddyizm.flatfileexporter") diff --git a/src/flatfileexporter/storage.py b/src/flatfileexporter/storage.py new file mode 100644 index 0000000..e92dace --- /dev/null +++ b/src/flatfileexporter/storage.py @@ -0,0 +1,51 @@ +import json +import os +from pathlib import Path +import appdirs + + +class AppConfig: + def __init__(self, app_name): + self.app_name = app_name + self.config_dir = Path(appdirs.user_config_dir(app_name)) + self.config_file = self.config_dir / "config.json" + self._config = {} + + # Create config directory if it doesn't exist + self.config_dir.mkdir(parents=True, exist_ok=True) + + # Load existing config + self.load() + + def load(self): + """Load configuration from file""" + try: + if self.config_file.exists(): + with open(self.config_file, "r") as f: + self._config = json.load(f) + except Exception as e: + print(f"Error loading config: {e}") + self._config = {} + + def save(self): + """Save configuration to file""" + try: + with open(self.config_file, "w") as f: + json.dump(self._config, f, indent=2) + except Exception as e: + print(f"Error saving config: {e}") + + def get(self, key, default=None): + """Get a config value""" + return self._config.get(key, default) + + def set(self, key, value): + """Set a config value""" + self._config[key] = value + self.save() + + def delete(self, key): + """Delete a config value""" + if key in self._config: + del self._config[key] + self.save() diff --git a/src/flatfileexporter/views/components/console_output.py b/src/flatfileexporter/views/components/console_output.py new file mode 100644 index 0000000..7786d51 --- /dev/null +++ b/src/flatfileexporter/views/components/console_output.py @@ -0,0 +1,22 @@ +import toga +from toga.style import Pack +from toga.style.pack import COLUMN + +class ConsoleOutput(toga.Box): + def __init__(self): + super().__init__(style=Pack(direction=COLUMN, flex=1)) + self.output = toga.MultilineTextInput(readonly=True) + # Replace ProgressBar with simple activity indicator + self.status_label = toga.Label("Ready", style=Pack(padding_top=5)) + self.add(self.output) + self.add(self.status_label) + + def log(self, message): + self.output.value += f"{message}\n" + self.output.scroll_to_bottom() + + def show_working(self): + self.status_label.text = "Working..." + + def show_ready(self): + self.status_label.text = "Ready" diff --git a/src/flatfileexporter/views/components/script_dialog.py b/src/flatfileexporter/views/components/script_dialog.py index e69de29..c546525 100644 --- a/src/flatfileexporter/views/components/script_dialog.py +++ b/src/flatfileexporter/views/components/script_dialog.py @@ -0,0 +1,94 @@ +import toga +from toga.style import Pack +from toga.style.pack import COLUMN, ROW +from pathlib import Path + + +class ScriptDialog(toga.Window): + def __init__(self, app): + super().__init__(title="Select SQL Script", size=(400, 300)) + self.app = app + + # Main container + main_box = toga.Box(style=Pack(direction=COLUMN, padding=10)) + + # File selection controls + self.file_path = toga.TextInput(placeholder="Script path", readonly=True) + browse_btn = toga.Button( + "Browse...", on_press=self.browse_script, style=Pack(padding=5) + ) + + # Preview area + self.preview = toga.MultilineTextInput(readonly=True, style=Pack(flex=1)) + + # Action buttons + btn_box = toga.Box(style=Pack(direction=ROW, padding_top=10)) + load_btn = toga.Button( + "Load Script", on_press=self.load_script, style=Pack(flex=1, padding=5) + ) + cancel_btn = toga.Button( + "Cancel", + on_press=lambda widget: self.close(), + style=Pack(flex=1, padding=5), + ) + btn_box.add(load_btn) + btn_box.add(cancel_btn) + + # Add components to dialog + main_box.add(toga.Label("Select SQL script file:")) + main_box.add(self.file_path) + main_box.add(browse_btn) + main_box.add(toga.Label("Preview:")) + main_box.add(self.preview) + main_box.add(btn_box) + + self.content = main_box + + def browse_script(self, widget): + """Open file selection dialog""" + try: + # Show file selection dialog + script_path = self.app.main_window.open_file_dialog( + title="Select SQL Script", file_types=["sql", "txt"] + ) + + if script_path: + self.file_path.value = script_path + self.preview_script(script_path) + except Exception as e: + self.app.main_window.error_dialog("Error", str(e)) + + def preview_script(self, path): + """Show a preview of the script""" + try: + with open(path, "r") as f: + content = f.read() + # Show first 500 chars for preview + self.preview.value = content[:500] + ( + "..." if len(content) > 500 else "" + ) + except Exception as e: + self.app.main_window.error_dialog("Error", f"Could not read file: {e}") + + def load_script(self, widget): + """Handle script loading""" + if not self.file_path.value: + self.app.main_window.error_dialog("Error", "No script selected") + return + + try: + # Here you would implement what happens when a script is loaded + print(f"Loading script: {self.file_path.value}") + + # For example, you might want to: + # 1. Store the script path in your app + # 2. Parse the script + # 3. Show it in the main view + + self.app.main_window.info_dialog( + "Success", + f"Script loaded successfully: {Path(self.file_path.value).name}", + ) + self.close() + except Exception as e: + self.app.main_window.error_dialog("Error", str(e)) diff --git a/src/flatfileexporter/views/config_view.py b/src/flatfileexporter/views/config_view.py new file mode 100644 index 0000000..3228895 --- /dev/null +++ b/src/flatfileexporter/views/config_view.py @@ -0,0 +1,99 @@ +import toga +from toga.style import Pack +from toga.style.pack import COLUMN, ROW + + +class ConfigView(toga.Box): + def __init__(self, app, initial_config): + super().__init__(style=Pack(direction=COLUMN, padding=10, flex=1)) + self.app = app + + # Navigation toolbar + toolbar = toga.Box(style=Pack(direction=ROW, padding=5)) + back_btn = toga.Button( + "Back to Main", on_press=self._save_and_exit, style=Pack(padding=5) + ) + toolbar.add(back_btn) + + # Configuration content + content_box = toga.Box(style=Pack(direction=COLUMN, padding=10, flex=1)) + + # Database connection fields + self.db_type = toga.Selection( + items=["SQL Server", "PostgreSQL", "MySQL"], + value=initial_config.get("db_type", "SQL Server"), + style=Pack(padding=5), + ) + + self.server_input = toga.TextInput( + value=initial_config.get("server", ""), + placeholder="Server", + style=Pack(padding=5), + ) + + self.db_input = toga.TextInput( + value=initial_config.get("database", ""), + placeholder="Database", + style=Pack(padding=5), + ) + + self.username_input = toga.TextInput( + value=initial_config.get("username", ""), + placeholder="Username", + style=Pack(padding=5), + ) + + self.password_input = toga.PasswordInput( + placeholder="Password", style=Pack(padding=5) + ) + + # Add fields to content box + content_box.add(toga.Label("Database Type:")) + content_box.add(self.db_type) + content_box.add(toga.Label("Server:")) + content_box.add(self.server_input) + content_box.add(toga.Label("Database:")) + content_box.add(self.db_input) + content_box.add(toga.Label("Username:")) + content_box.add(self.username_input) + content_box.add(toga.Label("Password:")) + content_box.add(self.password_input) + + # Add test connection button + test_btn = toga.Button( + "Test Connection", on_press=self._test_connection, style=Pack(padding=10) + ) + content_box.add(test_btn) + + # Add all to main view + self.add(toolbar) + self.add(content_box) + + def _test_connection(self, widget): + """Test database connection""" + try: + params = { + "db_type": self.db_type.value, + "server": self.server_input.value, + "database": self.db_input.value, + "username": self.username_input.value, + "password": self.password_input.value, + } + # Call your backend connection test here + print(f"Testing connection: {params}") + self.app.main_window.info_dialog( + "Success", f"Connected to {params['db_type']} at {params['server']}" + ) + except Exception as e: + self.app.main_window.error_dialog("Error", str(e)) + + def _save_and_exit(self, widget): + """Save configuration and return to main view""" + config = { + "db_type": self.db_type.value, + "server": self.server_input.value, + "database": self.db_input.value, + "username": self.username_input.value, + } + self.app.save_configuration(config) + self.app._show_main_view() diff --git a/src/flatfileexporter/views/main_view.py b/src/flatfileexporter/views/main_view.py new file mode 100644 index 0000000..2545187 --- /dev/null +++ b/src/flatfileexporter/views/main_view.py @@ -0,0 +1,44 @@ +import toga +import asyncio +from toga.style import Pack +from toga.style.pack import COLUMN, ROW +from .components.console_output import ConsoleOutput + + +class MainView(toga.Box): + def __init__(self, app): + super().__init__(style=Pack(direction=COLUMN, padding=10, flex=1)) + self.app = app + + # Navigation toolbar + toolbar = toga.Box(style=Pack(direction=ROW, padding=5)) + config_btn = toga.Button( + "Configuration", + on_press=lambda widget: self.app._show_config_view(), + style=Pack(padding=5), + ) + toolbar.add(config_btn) + + # Main content + content_box = toga.Box(style=Pack(direction=COLUMN, flex=1)) + + # Add console output + self.console = ConsoleOutput() + + # Add SQL script button + script_btn = toga.Button( + "Execute SQL", on_press=self.execute_script, style=Pack(padding=10) + ) + + content_box.add(script_btn) + content_box.add(self.console) + + self.add(toolbar) + self.add(content_box) + + def execute_script(self, widget): + """Button handler - just delegates to app""" + self.app.execute_sql() + + def refresh_data(self): + self.console.log("Application ready") diff --git a/src/flatfileexporter/views/main_window.py b/src/flatfileexporter/views/main_window.py deleted file mode 100644 index 94599d4..0000000 --- a/src/flatfileexporter/views/main_window.py +++ /dev/null @@ -1,26 +0,0 @@ -import toga -from toga.style import Pack -from toga.style.pack import COLUMN -from .connection import ConnectionSection -from .export import ExportSection - - -def create_main_window(app): - """Factory function to create the main window.""" - window = toga.MainWindow(title=app.formal_name) - print(window.toolbar.__dict__) - - # Main container - main_box = toga.Box( - style=Pack(direction=COLUMN, padding=10, flex=1) - ) - # TODO Move these to their own pages --Create sections - connection_section = ConnectionSection(app) - export_section = ExportSection(app) - - # Add sections to main container - main_box.add(connection_section) - main_box.add(export_section) - - window.content = main_box - return window From 2298c2f83326029d7e163b96bba7551cedff5e0a Mon Sep 17 00:00:00 2001 From: eddyizm Date: Tue, 6 May 2025 21:53:50 -0700 Subject: [PATCH 10/16] wip: updated window title, fixed console output and button label --- src/flatfileexporter/app.py | 2 +- src/flatfileexporter/views/components/console_output.py | 5 ++++- src/flatfileexporter/views/main_view.py | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/flatfileexporter/app.py b/src/flatfileexporter/app.py index 8f02cbd..324f703 100644 --- a/src/flatfileexporter/app.py +++ b/src/flatfileexporter/app.py @@ -27,7 +27,7 @@ def startup(self): self.config_view = ConfigView(app=self, initial_config=self.saved_config) # Create main window - self.main_window = toga.MainWindow(title=self.name) + self.main_window = toga.MainWindow(title=self.formal_name) # Show main view initially self._show_main_view() diff --git a/src/flatfileexporter/views/components/console_output.py b/src/flatfileexporter/views/components/console_output.py index 7786d51..92bbd82 100644 --- a/src/flatfileexporter/views/components/console_output.py +++ b/src/flatfileexporter/views/components/console_output.py @@ -2,10 +2,13 @@ from toga.style import Pack from toga.style.pack import COLUMN + class ConsoleOutput(toga.Box): def __init__(self): super().__init__(style=Pack(direction=COLUMN, flex=1)) - self.output = toga.MultilineTextInput(readonly=True) + self.output = toga.MultilineTextInput( + readonly=True, style=Pack(flex=1, font_family="monospace", padding_bottom=5) + ) # Replace ProgressBar with simple activity indicator self.status_label = toga.Label("Ready", style=Pack(padding_top=5)) self.add(self.output) diff --git a/src/flatfileexporter/views/main_view.py b/src/flatfileexporter/views/main_view.py index 2545187..e9a07e3 100644 --- a/src/flatfileexporter/views/main_view.py +++ b/src/flatfileexporter/views/main_view.py @@ -27,7 +27,7 @@ def __init__(self, app): # Add SQL script button script_btn = toga.Button( - "Execute SQL", on_press=self.execute_script, style=Pack(padding=10) + "Generate file", on_press=self.execute_script, style=Pack(padding=10) ) content_box.add(script_btn) From f99e7d1173f75e9dafa8aa1ce7433ab0993f2213 Mon Sep 17 00:00:00 2001 From: eddyizm Date: Wed, 7 May 2025 07:52:57 -0700 Subject: [PATCH 11/16] chore: updated padding to margin to remove depreciation warnings, started tying in dialog picker --- .../views/components/console_output.py | 4 +-- .../views/components/script_dialog.py | 2 +- src/flatfileexporter/views/config_view.py | 20 ++++++------- src/flatfileexporter/views/main_view.py | 28 +++++++++++++++---- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/flatfileexporter/views/components/console_output.py b/src/flatfileexporter/views/components/console_output.py index 92bbd82..ff892ea 100644 --- a/src/flatfileexporter/views/components/console_output.py +++ b/src/flatfileexporter/views/components/console_output.py @@ -7,10 +7,10 @@ class ConsoleOutput(toga.Box): def __init__(self): super().__init__(style=Pack(direction=COLUMN, flex=1)) self.output = toga.MultilineTextInput( - readonly=True, style=Pack(flex=1, font_family="monospace", padding_bottom=5) + readonly=True, style=Pack(flex=1, font_family="monospace", margin_bottom=5) ) # Replace ProgressBar with simple activity indicator - self.status_label = toga.Label("Ready", style=Pack(padding_top=5)) + self.status_label = toga.Label("Ready", style=Pack(margin_top=5)) self.add(self.output) self.add(self.status_label) diff --git a/src/flatfileexporter/views/components/script_dialog.py b/src/flatfileexporter/views/components/script_dialog.py index c546525..cd10e52 100644 --- a/src/flatfileexporter/views/components/script_dialog.py +++ b/src/flatfileexporter/views/components/script_dialog.py @@ -22,7 +22,7 @@ def __init__(self, app): self.preview = toga.MultilineTextInput(readonly=True, style=Pack(flex=1)) # Action buttons - btn_box = toga.Box(style=Pack(direction=ROW, padding_top=10)) + btn_box = toga.Box(style=Pack(direction=ROW, margin_top=10)) load_btn = toga.Button( "Load Script", on_press=self.load_script, style=Pack(flex=1, padding=5) ) diff --git a/src/flatfileexporter/views/config_view.py b/src/flatfileexporter/views/config_view.py index 3228895..bb275f2 100644 --- a/src/flatfileexporter/views/config_view.py +++ b/src/flatfileexporter/views/config_view.py @@ -5,46 +5,46 @@ class ConfigView(toga.Box): def __init__(self, app, initial_config): - super().__init__(style=Pack(direction=COLUMN, padding=10, flex=1)) + super().__init__(style=Pack(direction=COLUMN, margin=10, flex=1)) self.app = app # Navigation toolbar - toolbar = toga.Box(style=Pack(direction=ROW, padding=5)) + toolbar = toga.Box(style=Pack(direction=ROW, margin=5)) back_btn = toga.Button( - "Back to Main", on_press=self._save_and_exit, style=Pack(padding=5) + "Back to Main", on_press=self._save_and_exit, style=Pack(margin=5) ) toolbar.add(back_btn) # Configuration content - content_box = toga.Box(style=Pack(direction=COLUMN, padding=10, flex=1)) + content_box = toga.Box(style=Pack(direction=COLUMN, margin=10, flex=1)) # Database connection fields self.db_type = toga.Selection( items=["SQL Server", "PostgreSQL", "MySQL"], value=initial_config.get("db_type", "SQL Server"), - style=Pack(padding=5), + style=Pack(margin=5), ) self.server_input = toga.TextInput( value=initial_config.get("server", ""), placeholder="Server", - style=Pack(padding=5), + style=Pack(margin=5), ) self.db_input = toga.TextInput( value=initial_config.get("database", ""), placeholder="Database", - style=Pack(padding=5), + style=Pack(margin=5), ) self.username_input = toga.TextInput( value=initial_config.get("username", ""), placeholder="Username", - style=Pack(padding=5), + style=Pack(margin=5), ) self.password_input = toga.PasswordInput( - placeholder="Password", style=Pack(padding=5) + placeholder="Password", style=Pack(margin=5) ) # Add fields to content box @@ -61,7 +61,7 @@ def __init__(self, app, initial_config): # Add test connection button test_btn = toga.Button( - "Test Connection", on_press=self._test_connection, style=Pack(padding=10) + "Test Connection", on_press=self._test_connection, style=Pack(margin=10) ) content_box.add(test_btn) diff --git a/src/flatfileexporter/views/main_view.py b/src/flatfileexporter/views/main_view.py index e9a07e3..05413b4 100644 --- a/src/flatfileexporter/views/main_view.py +++ b/src/flatfileexporter/views/main_view.py @@ -1,39 +1,50 @@ import toga +import os import asyncio from toga.style import Pack from toga.style.pack import COLUMN, ROW from .components.console_output import ConsoleOutput - +from .components.script_dialog import ScriptDialog class MainView(toga.Box): def __init__(self, app): - super().__init__(style=Pack(direction=COLUMN, padding=10, flex=1)) + super().__init__(style=Pack(direction=COLUMN, margin=10, flex=1)) self.app = app # Navigation toolbar - toolbar = toga.Box(style=Pack(direction=ROW, padding=5)) + toolbar = toga.Box(style=Pack(direction=ROW, margin=5)) config_btn = toga.Button( "Configuration", on_press=lambda widget: self.app._show_config_view(), - style=Pack(padding=5), + style=Pack(margin=5), + ) + add_script_btn = toga.Button( + "Select SQL Script", + on_press=self._add_script, + # icon='resources/file-upload-line.svg', + style=Pack(margin=5), ) toolbar.add(config_btn) + # toolbar.add(add_script_btn) + file_toolbar = toga.Box(style=Pack(direction=ROW, margin=5)) + file_toolbar.add(add_script_btn) + # Main content content_box = toga.Box(style=Pack(direction=COLUMN, flex=1)) # Add console output self.console = ConsoleOutput() - # Add SQL script button script_btn = toga.Button( - "Generate file", on_press=self.execute_script, style=Pack(padding=10) + "Generate file", on_press=self.execute_script, style=Pack(margin=10) ) content_box.add(script_btn) content_box.add(self.console) self.add(toolbar) + self.add(file_toolbar) self.add(content_box) def execute_script(self, widget): @@ -42,3 +53,8 @@ def execute_script(self, widget): def refresh_data(self): self.console.log("Application ready") + + def _add_script(self, widget): + self.console.log("adding sql script") + ScriptDialog.browse_script(self, widget) + # self.console.log(os.getcwd()) From 5d1af193e646f34d8daf745cfff476cc58570a32 Mon Sep 17 00:00:00 2001 From: eddyizm Date: Sat, 10 May 2025 21:36:44 -0700 Subject: [PATCH 12/16] wip: selecting and reading sql script from file system --- .../views/components/script_dialog.py | 8 +- src/flatfileexporter/views/main_view.py | 80 ++++++++++++++++--- 2 files changed, 71 insertions(+), 17 deletions(-) diff --git a/src/flatfileexporter/views/components/script_dialog.py b/src/flatfileexporter/views/components/script_dialog.py index cd10e52..d0fa5f3 100644 --- a/src/flatfileexporter/views/components/script_dialog.py +++ b/src/flatfileexporter/views/components/script_dialog.py @@ -63,7 +63,6 @@ def preview_script(self, path): try: with open(path, "r") as f: content = f.read() - # Show first 500 chars for preview self.preview.value = content[:500] + ( "..." if len(content) > 500 else "" ) @@ -77,13 +76,8 @@ def load_script(self, widget): return try: - # Here you would implement what happens when a script is loaded - print(f"Loading script: {self.file_path.value}") - # For example, you might want to: - # 1. Store the script path in your app - # 2. Parse the script - # 3. Show it in the main view + print(f"Loading script: {self.file_path.value}") self.app.main_window.info_dialog( "Success", diff --git a/src/flatfileexporter/views/main_view.py b/src/flatfileexporter/views/main_view.py index 05413b4..8cc9f5f 100644 --- a/src/flatfileexporter/views/main_view.py +++ b/src/flatfileexporter/views/main_view.py @@ -1,10 +1,11 @@ import toga import os -import asyncio from toga.style import Pack from toga.style.pack import COLUMN, ROW from .components.console_output import ConsoleOutput -from .components.script_dialog import ScriptDialog + +# from .components.script_dialog import ScriptDialog + class MainView(toga.Box): def __init__(self, app): @@ -18,17 +19,36 @@ def __init__(self, app): on_press=lambda widget: self.app._show_config_view(), style=Pack(margin=5), ) + add_script_btn = toga.Button( "Select SQL Script", - on_press=self._add_script, - # icon='resources/file-upload-line.svg', + on_press=self.open_file_dialog, + # on_press=self._add_script, + # icon='resources/file-upload-line', style=Pack(margin=5), ) toolbar.add(config_btn) - + self.file_label = toga.Label( + "_", id="filelabel", style=Pack(direction=ROW, margin_top=15) + ) # toolbar.add(add_script_btn) - file_toolbar = toga.Box(style=Pack(direction=ROW, margin=5)) - file_toolbar.add(add_script_btn) + self.file_toolbar = toga.Box(style=Pack(direction=ROW, margin=5)) + + self.file_toolbar.add(self.file_label) + self.file_toolbar.add(add_script_btn) + + # # Create UI elements + # self.open_button = toga.Button( + # "Open SQL File", + # on_press=self.open_file_dialog, + # style=Pack(padding=10) + # ) + + # # Add a label to show selected file info + # self.file_label = toga.Label( + # "No file selected", + # style=Pack(padding=(0, 5, 10, 5)) + # ) # Main content content_box = toga.Box(style=Pack(direction=COLUMN, flex=1)) @@ -44,7 +64,7 @@ def __init__(self, app): content_box.add(self.console) self.add(toolbar) - self.add(file_toolbar) + self.add(self.file_toolbar) self.add(content_box) def execute_script(self, widget): @@ -56,5 +76,45 @@ def refresh_data(self): def _add_script(self, widget): self.console.log("adding sql script") - ScriptDialog.browse_script(self, widget) - # self.console.log(os.getcwd()) + # ScriptDialog.browse_script(self, widget) + script_path = toga.OpenFileDialog( + title="Select SQL Script", file_types=["sql", "txt"] + ) + self.console.log(type(script_path)) + self.console.log(script_path) + self.console.log(script_path.__dict__) + + # breakpoint() + + async def open_file_dialog(self, widget): + try: + file_path = await self.app.main_window.open_file_dialog( + title="Select SQL File", + file_types=["sql", "txt"], + multiple_select=False, + ) + + if file_path: + await self.handle_file_selection(file_path) + + except ValueError as e: + self.file_label.text = "File selection canceled" + print(f"No file selected: {e}") + + async def handle_file_selection(self, file_path): + # Update the UI + self.file_label.text = f"Selected: {file_path.name}" + + # Show confirmation dialog + await self.app.main_window.info_dialog( + "File Selected", f"Successfully selected:\n{file_path}" + ) + with open(str(file_path), "r") as f: + content = f.readlines() + + for line in content: + self.console.log(line.replace("\n", "")) + # Here you would typically: + # 1. Read the SQL file (file_path.read()) + # 2. Process the content + # 3. Update other parts of your UI From 0d77c0ae3b267c1d10c5fe30b9f4ff815327f0be Mon Sep 17 00:00:00 2001 From: eddyizm Date: Sun, 11 May 2025 12:01:39 -0700 Subject: [PATCH 13/16] wip: added file selection, model to hold file export info, checks on query existence. --- pyproject.toml | 4 +- src/PythonCodeBase/DAL.py | 4 +- src/flatfileexporter/app.py | 9 +- src/flatfileexporter/models/__init__.py | 0 src/flatfileexporter/models/files.py | 32 +++++++ .../resources/file-upload-line.ico | Bin 0 -> 23462 bytes .../resources/file-upload-line.svg | 1 + .../views/components/console_output.py | 22 +++-- .../views/components/script_dialog.py | 88 ------------------ src/flatfileexporter/views/main_view.py | 61 ++++++------ 10 files changed, 84 insertions(+), 137 deletions(-) create mode 100644 src/flatfileexporter/models/__init__.py create mode 100644 src/flatfileexporter/models/files.py create mode 100644 src/flatfileexporter/resources/file-upload-line.ico create mode 100644 src/flatfileexporter/resources/file-upload-line.svg delete mode 100644 src/flatfileexporter/views/components/script_dialog.py diff --git a/pyproject.toml b/pyproject.toml index 6375242..a09586b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ [tool.briefcase] project_name = "Flat File Exporter" bundle = "com.eddyizm" -version = "0.0.1" +version = "0.1.5" url = "https://eddyizm.com/flatfileexporter" license.file = "LICENSE" author = "eduardo cervantes" @@ -35,7 +35,7 @@ requires = [ [tool.briefcase.app.flatfileexporter.linux] requires = [ - "toga-gtk~=0.4.7", + "toga-gtk~=0.5.0", ] [tool.briefcase.app.flatfileexporter.linux.system.debian] diff --git a/src/PythonCodeBase/DAL.py b/src/PythonCodeBase/DAL.py index 12f2762..39233f9 100644 --- a/src/PythonCodeBase/DAL.py +++ b/src/PythonCodeBase/DAL.py @@ -34,10 +34,10 @@ def check_odbc() -> bool: log.info(f'Driver found - {driver}') return True else: - log.warn(ODBC_MISSING_MSG) + log.warning(ODBC_MISSING_MSG) return False else: - log.warn(ODBC_MISSING_MSG) + log.warning(ODBC_MISSING_MSG) return False except Exception as ex: log.error(f'Error in check_odbc(): {ex}') diff --git a/src/flatfileexporter/app.py b/src/flatfileexporter/app.py index 324f703..2988229 100644 --- a/src/flatfileexporter/app.py +++ b/src/flatfileexporter/app.py @@ -3,7 +3,7 @@ from .storage import AppConfig from .views.main_view import MainView from .views.config_view import ConfigView - +from .models.files import FileExport class FlatFileExporter(toga.App): def __init__(self, *args, **kwargs): @@ -49,19 +49,16 @@ def _show_config_view(self): """Switch to config view (private method)""" self.main_window.content = self.config_view - def execute_sql(self): + def execute_sql(self, file_info: FileExport): """Simple synchronous execution with progress feedback""" - self.main_view.console.show_working() self.main_view.console.log("Starting execution...") - + self.main_view.console.log(file_info.as_json) import time for i in range(5): time.sleep(1) # Blocking sleep self.main_view.console.log(f"Completed step {i+1}") - self.main_view.console.log("Execution finished") - self.main_view.console.show_ready() def main(): diff --git a/src/flatfileexporter/models/__init__.py b/src/flatfileexporter/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/flatfileexporter/models/files.py b/src/flatfileexporter/models/files.py new file mode 100644 index 0000000..83008c6 --- /dev/null +++ b/src/flatfileexporter/models/files.py @@ -0,0 +1,32 @@ +from dataclasses import dataclass, asdict +from enum import Enum +from typing import Optional + +# class FileType(Enum): +# CSV = "csv" +# TXT = "txt" +# XLSX = "xlsx" + + +@dataclass +class FileExport: + """Single model for file export configuration""" + filename: str + filetype: str + sql_query: str + # db_host: str + # db_name: str + # db_user: str + # db_password: str + # db_port: int = 5432 + # query_params: Optional[dict] = None + # overwrite_existing: bool = False + + @property + def full_filename(self) -> str: + """Returns filename with proper extension""" + return f"{self.filename}.{self.filetype.value}" + + @property + def as_json(self) -> dict: + return asdict(self) diff --git a/src/flatfileexporter/resources/file-upload-line.ico b/src/flatfileexporter/resources/file-upload-line.ico new file mode 100644 index 0000000000000000000000000000000000000000..4b26050dba02c8a5dc576cdd98306360b57bf8ec GIT binary patch literal 23462 zcmeI4v2GJV5Qf)EkqUwUQ4$LpDqg`ILW-yA%D z02Lrb9RKgro=+CG$9r>g-eo45%b&a5otbavl(TF(gb{vDP6CeO@ajzJQ;@KNR4s<2n2)T+=RNu`lqdK>v)I zF2S|z2^K1mQ>^K+7AUOqu5eaw6ZeKEn>(kP9JGeheeEZ0`i#_by&oZSDZf5q7I7~ z=>+66E$Xm{A+I=nq(vPTG13XhXIj)@5kp>a`bdj9EMlY+kk7QJ!(;e}ya+1!|{Rl$Qpl5?F2HrNyZfR_l0aaw>_{ zDlctLrLl^h1(mGSd+;~-JWyg3mno4|ADqamG**3aDved2oJwNVH>XlqS#T!b0VuN@Ht$X^AO|qffEod>^hDw;pgyu{GKA~|91;0esBU#zzH}3C-A=z zsG`mB@DwdY&uU^*&ONL3q_bWmG_Tj$4?UY!O+I^|L3$GNY3o@C(|De{5cT&kYV@yg z(CCYB*yumusL@q8Uh1>SQnzcLSIhH1TDrPmcI*f2olw{PQ;)BfbCGqk4zg}EvTii8 qZZxuPG_s}+v}sEt(|CT=(g!Vl*wRO}o^hGuS}$4>2d)z>LH`Ayb4f)2 literal 0 HcmV?d00001 diff --git a/src/flatfileexporter/resources/file-upload-line.svg b/src/flatfileexporter/resources/file-upload-line.svg new file mode 100644 index 0000000..13fcb04 --- /dev/null +++ b/src/flatfileexporter/resources/file-upload-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/flatfileexporter/views/components/console_output.py b/src/flatfileexporter/views/components/console_output.py index ff892ea..deb111f 100644 --- a/src/flatfileexporter/views/components/console_output.py +++ b/src/flatfileexporter/views/components/console_output.py @@ -9,17 +9,23 @@ def __init__(self): self.output = toga.MultilineTextInput( readonly=True, style=Pack(flex=1, font_family="monospace", margin_bottom=5) ) - # Replace ProgressBar with simple activity indicator - self.status_label = toga.Label("Ready", style=Pack(margin_top=5)) - self.add(self.output) + self.status_label = toga.Label( + "Application Log", style=Pack(margin_top=5, margin_bottom=5) + ) self.add(self.status_label) + self.add(self.output) + + self.clear_output_btn = toga.Button( + "Clear output", + on_press=self._clear_console, + style=Pack(margin=5), + ) + self.add(self.clear_output_btn) def log(self, message): self.output.value += f"{message}\n" self.output.scroll_to_bottom() - def show_working(self): - self.status_label.text = "Working..." - - def show_ready(self): - self.status_label.text = "Ready" + def _clear_console(self, widget): + self.output.value = "" + self.log("Ready") diff --git a/src/flatfileexporter/views/components/script_dialog.py b/src/flatfileexporter/views/components/script_dialog.py deleted file mode 100644 index d0fa5f3..0000000 --- a/src/flatfileexporter/views/components/script_dialog.py +++ /dev/null @@ -1,88 +0,0 @@ -import toga -from toga.style import Pack -from toga.style.pack import COLUMN, ROW -from pathlib import Path - - -class ScriptDialog(toga.Window): - def __init__(self, app): - super().__init__(title="Select SQL Script", size=(400, 300)) - self.app = app - - # Main container - main_box = toga.Box(style=Pack(direction=COLUMN, padding=10)) - - # File selection controls - self.file_path = toga.TextInput(placeholder="Script path", readonly=True) - browse_btn = toga.Button( - "Browse...", on_press=self.browse_script, style=Pack(padding=5) - ) - - # Preview area - self.preview = toga.MultilineTextInput(readonly=True, style=Pack(flex=1)) - - # Action buttons - btn_box = toga.Box(style=Pack(direction=ROW, margin_top=10)) - load_btn = toga.Button( - "Load Script", on_press=self.load_script, style=Pack(flex=1, padding=5) - ) - cancel_btn = toga.Button( - "Cancel", - on_press=lambda widget: self.close(), - style=Pack(flex=1, padding=5), - ) - btn_box.add(load_btn) - btn_box.add(cancel_btn) - - # Add components to dialog - main_box.add(toga.Label("Select SQL script file:")) - main_box.add(self.file_path) - main_box.add(browse_btn) - main_box.add(toga.Label("Preview:")) - main_box.add(self.preview) - main_box.add(btn_box) - - self.content = main_box - - def browse_script(self, widget): - """Open file selection dialog""" - try: - # Show file selection dialog - script_path = self.app.main_window.open_file_dialog( - title="Select SQL Script", file_types=["sql", "txt"] - ) - - if script_path: - self.file_path.value = script_path - self.preview_script(script_path) - except Exception as e: - self.app.main_window.error_dialog("Error", str(e)) - - def preview_script(self, path): - """Show a preview of the script""" - try: - with open(path, "r") as f: - content = f.read() - self.preview.value = content[:500] + ( - "..." if len(content) > 500 else "" - ) - except Exception as e: - self.app.main_window.error_dialog("Error", f"Could not read file: {e}") - - def load_script(self, widget): - """Handle script loading""" - if not self.file_path.value: - self.app.main_window.error_dialog("Error", "No script selected") - return - - try: - - print(f"Loading script: {self.file_path.value}") - - self.app.main_window.info_dialog( - "Success", - f"Script loaded successfully: {Path(self.file_path.value).name}", - ) - self.close() - except Exception as e: - self.app.main_window.error_dialog("Error", str(e)) diff --git a/src/flatfileexporter/views/main_view.py b/src/flatfileexporter/views/main_view.py index 8cc9f5f..fe7e7f9 100644 --- a/src/flatfileexporter/views/main_view.py +++ b/src/flatfileexporter/views/main_view.py @@ -1,10 +1,8 @@ import toga -import os from toga.style import Pack from toga.style.pack import COLUMN, ROW from .components.console_output import ConsoleOutput - -# from .components.script_dialog import ScriptDialog +from ..models.files import FileExport class MainView(toga.Box): @@ -19,36 +17,33 @@ def __init__(self, app): on_press=lambda widget: self.app._show_config_view(), style=Pack(margin=5), ) + toolbar.add(config_btn) + # script selection add_script_btn = toga.Button( "Select SQL Script", on_press=self.open_file_dialog, - # on_press=self._add_script, - # icon='resources/file-upload-line', style=Pack(margin=5), ) - toolbar.add(config_btn) + self.file_label = toga.Label( "_", id="filelabel", style=Pack(direction=ROW, margin_top=15) ) - # toolbar.add(add_script_btn) self.file_toolbar = toga.Box(style=Pack(direction=ROW, margin=5)) - self.file_toolbar.add(self.file_label) - self.file_toolbar.add(add_script_btn) + # Create file extension selector component + self.selection = toga.Selection( + items=["csv", "txt", "xlsx"], + on_change=self._file_selection, + style=Pack(margin=5), + ) - # # Create UI elements - # self.open_button = toga.Button( - # "Open SQL File", - # on_press=self.open_file_dialog, - # style=Pack(padding=10) - # ) + # Change the selection to "Charlie" + self.selection.value = "csv" - # # Add a label to show selected file info - # self.file_label = toga.Label( - # "No file selected", - # style=Pack(padding=(0, 5, 10, 5)) - # ) + self.file_toolbar.add(self.selection) + self.file_toolbar.add(self.file_label) + self.file_toolbar.add(add_script_btn) # Main content content_box = toga.Box(style=Pack(direction=COLUMN, flex=1)) @@ -67,12 +62,24 @@ def __init__(self, app): self.add(self.file_toolbar) self.add(content_box) + # create file export object to hold values + self._file_export = FileExport(filename="", filetype="", sql_query="") + def execute_script(self, widget): """Button handler - just delegates to app""" - self.app.execute_sql() + + if self._file_export.sql_query == "": + self.console.log("Error, please select a valid sql script.") + return + self._file_export.filetype = self.selection.value + self._file_export.filename = "test" + self.app.execute_sql(self._file_export) def refresh_data(self): - self.console.log("Application ready") + self.console.log("Ready") + + def _file_selection(self, widget): + self.console.log(f"file selected: {self.selection.value}") def _add_script(self, widget): self.console.log("adding sql script") @@ -84,8 +91,6 @@ def _add_script(self, widget): self.console.log(script_path) self.console.log(script_path.__dict__) - # breakpoint() - async def open_file_dialog(self, widget): try: file_path = await self.app.main_window.open_file_dialog( @@ -102,19 +107,13 @@ async def open_file_dialog(self, widget): print(f"No file selected: {e}") async def handle_file_selection(self, file_path): - # Update the UI self.file_label.text = f"Selected: {file_path.name}" - - # Show confirmation dialog await self.app.main_window.info_dialog( "File Selected", f"Successfully selected:\n{file_path}" ) with open(str(file_path), "r") as f: content = f.readlines() + self._file_export.sql_query = content for line in content: self.console.log(line.replace("\n", "")) - # Here you would typically: - # 1. Read the SQL file (file_path.read()) - # 2. Process the content - # 3. Update other parts of your UI From 42797100e6b8a07f63b9266b81da52918b50890f Mon Sep 17 00:00:00 2001 From: eddyizm Date: Mon, 12 May 2025 22:04:15 -0700 Subject: [PATCH 14/16] wip: fixed alignment and margines --- src/flatfileexporter/app.py | 1 - .../views/components/console_output.py | 4 +-- src/flatfileexporter/views/main_view.py | 35 ++++++++++++------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/flatfileexporter/app.py b/src/flatfileexporter/app.py index 2988229..7536fe7 100644 --- a/src/flatfileexporter/app.py +++ b/src/flatfileexporter/app.py @@ -1,5 +1,4 @@ import toga -import asyncio from .storage import AppConfig from .views.main_view import MainView from .views.config_view import ConfigView diff --git a/src/flatfileexporter/views/components/console_output.py b/src/flatfileexporter/views/components/console_output.py index deb111f..a05de09 100644 --- a/src/flatfileexporter/views/components/console_output.py +++ b/src/flatfileexporter/views/components/console_output.py @@ -7,10 +7,10 @@ class ConsoleOutput(toga.Box): def __init__(self): super().__init__(style=Pack(direction=COLUMN, flex=1)) self.output = toga.MultilineTextInput( - readonly=True, style=Pack(flex=1, font_family="monospace", margin_bottom=5) + readonly=True, style=Pack(flex=1, font_family="monospace", margin_bottom=5, margin_left=10, margin_right=10) ) self.status_label = toga.Label( - "Application Log", style=Pack(margin_top=5, margin_bottom=5) + "Application Log", style=Pack(margin_top=5, margin_bottom=5, margin_left=10, margin_right=10) ) self.add(self.status_label) self.add(self.output) diff --git a/src/flatfileexporter/views/main_view.py b/src/flatfileexporter/views/main_view.py index fe7e7f9..549a16b 100644 --- a/src/flatfileexporter/views/main_view.py +++ b/src/flatfileexporter/views/main_view.py @@ -13,9 +13,9 @@ def __init__(self, app): # Navigation toolbar toolbar = toga.Box(style=Pack(direction=ROW, margin=5)) config_btn = toga.Button( - "Configuration", + "db configuration", on_press=lambda widget: self.app._show_config_view(), - style=Pack(margin=5), + style=Pack(margin=5, width=180), ) toolbar.add(config_btn) @@ -23,27 +23,37 @@ def __init__(self, app): add_script_btn = toga.Button( "Select SQL Script", on_press=self.open_file_dialog, - style=Pack(margin=5), + style=Pack(margin=5, width=180), ) self.file_label = toga.Label( - "_", id="filelabel", style=Pack(direction=ROW, margin_top=15) + "Sql script", + id="filelabel", + style=Pack(direction=ROW, margin_top=15, margin_left=10, flex=0.5), ) self.file_toolbar = toga.Box(style=Pack(direction=ROW, margin=5)) + self.extension_block = toga.Box(style=Pack(direction=ROW, margin=5)) # Create file extension selector component - self.selection = toga.Selection( + self.extension_label = toga.Label( + "Output (file type)", + id="ext_lb", + style=Pack(direction=ROW, margin_top=15, margin_left=10, flex=0.5), + ) + self.extension = toga.Selection( items=["csv", "txt", "xlsx"], on_change=self._file_selection, - style=Pack(margin=5), + style=Pack(margin=5, width=180), ) - # Change the selection to "Charlie" - self.selection.value = "csv" + # Set selection to default (config option?) + self.extension.value = "csv" + + self.extension_block.add(self.extension) + self.extension_block.add(self.extension_label) - self.file_toolbar.add(self.selection) - self.file_toolbar.add(self.file_label) self.file_toolbar.add(add_script_btn) + self.file_toolbar.add(self.file_label) # Main content content_box = toga.Box(style=Pack(direction=COLUMN, flex=1)) @@ -57,8 +67,9 @@ def __init__(self, app): content_box.add(script_btn) content_box.add(self.console) - + # add to main view self.add(toolbar) + self.add(self.extension_block) self.add(self.file_toolbar) self.add(content_box) @@ -79,7 +90,7 @@ def refresh_data(self): self.console.log("Ready") def _file_selection(self, widget): - self.console.log(f"file selected: {self.selection.value}") + self.console.log(f"file selected: {self.extension.value}") def _add_script(self, widget): self.console.log("adding sql script") From 3d853f0c29c833018473eaf42a68b49d996e285c Mon Sep 17 00:00:00 2001 From: eddyizm Date: Tue, 13 May 2025 07:41:46 -0700 Subject: [PATCH 15/16] chore: moving old python code to current project, cleaning up files and testing connection/config --- pyproject.toml | 5 ++ .../data}/DAL.py | 6 +- .../data}/__init__.py | 0 .../flatfile_cli.py | 6 +- .../flatfile_cli.txt | 0 .../requirements.txt | 0 .../views/components/console_output.py | 12 ++-- src/flatfileexporter/views/config_view.py | 2 + src/flatfileexporter/views/connection.py | 62 ------------------- 9 files changed, 18 insertions(+), 75 deletions(-) rename src/{PythonCodeBase => flatfileexporter/data}/DAL.py (94%) rename src/{PythonCodeBase => flatfileexporter/data}/__init__.py (100%) rename src/{PythonCodeBase => flatfileexporter}/flatfile_cli.py (97%) rename src/{PythonCodeBase => flatfileexporter}/flatfile_cli.txt (100%) rename src/{PythonCodeBase => flatfileexporter}/requirements.txt (100%) delete mode 100644 src/flatfileexporter/views/connection.py diff --git a/pyproject.toml b/pyproject.toml index a09586b..88df295 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,11 @@ test_sources = [ ] requires = [ + "appdirs==1.4.4", + "pandas==2.2.3", + "pyodbc==5.2.0", + "openpyxl==3.1.5", + "XlsxWriter==3.2.3" ] test_requires = [ "pytest", diff --git a/src/PythonCodeBase/DAL.py b/src/flatfileexporter/data/DAL.py similarity index 94% rename from src/PythonCodeBase/DAL.py rename to src/flatfileexporter/data/DAL.py index 39233f9..7995d28 100644 --- a/src/PythonCodeBase/DAL.py +++ b/src/flatfileexporter/data/DAL.py @@ -11,11 +11,9 @@ log = logging.getLogger('root') date_var = datetime.now().strftime("%Y%m%d") -# When called from UI the directory will default to the l`ocation of the SQL script. -# TODO figure out how to auto increment this and pass it/share it with c# codebase -__version__= '0.1.5' +# When called from UI the directory will default to the location of the SQL script. -DB_STRING = 'DRIVER={{{}}};SERVER={};Trusted_Connection=yes;DATABASE={}' +DB_STRING = 'DRIVER={{{}}};SERVER={};Trusted_Connection=yes;DATABASE={}' USERPASS_DBSTRING = 'DRIVER={{{}}};SERVER={};DATABASE={};UID={};PWD={}' ODBC_MISSING_MSG = '''ODBC driver not found please install ODBC Driver 17 for SQL Server diff --git a/src/PythonCodeBase/__init__.py b/src/flatfileexporter/data/__init__.py similarity index 100% rename from src/PythonCodeBase/__init__.py rename to src/flatfileexporter/data/__init__.py diff --git a/src/PythonCodeBase/flatfile_cli.py b/src/flatfileexporter/flatfile_cli.py similarity index 97% rename from src/PythonCodeBase/flatfile_cli.py rename to src/flatfileexporter/flatfile_cli.py index 2e01600..0009f3d 100644 --- a/src/PythonCodeBase/flatfile_cli.py +++ b/src/flatfileexporter/flatfile_cli.py @@ -8,7 +8,7 @@ ''' import os -import DAL as db # local library +import flatfileexporter.data.DAL as db # local library import argparse import logging from logging.handlers import RotatingFileHandler @@ -33,7 +33,7 @@ # argument parser parser = argparse.ArgumentParser(description="Reads a sql script or calls a stored procedure and exports results to a csv, txt or excel file.") -parser.add_argument("-V", "--version", action='version', version =f'{db.__version__}') +# parser.add_argument("-V", "--version", action='version') parser.add_argument("server", help="server to run query against.") parser.add_argument("db", help="database to use on server.") parser.add_argument("directory", help="location to export flat file. do not specify extension.") @@ -148,7 +148,7 @@ def main(): if __name__ == '__main__': - log.info(f'Startiing FFE CLI version: {db.__version__}') + log.info('Startiing FFE CLI') # log_args() main() log.info(FINISH_MSG) diff --git a/src/PythonCodeBase/flatfile_cli.txt b/src/flatfileexporter/flatfile_cli.txt similarity index 100% rename from src/PythonCodeBase/flatfile_cli.txt rename to src/flatfileexporter/flatfile_cli.txt diff --git a/src/PythonCodeBase/requirements.txt b/src/flatfileexporter/requirements.txt similarity index 100% rename from src/PythonCodeBase/requirements.txt rename to src/flatfileexporter/requirements.txt diff --git a/src/flatfileexporter/views/components/console_output.py b/src/flatfileexporter/views/components/console_output.py index a05de09..7204069 100644 --- a/src/flatfileexporter/views/components/console_output.py +++ b/src/flatfileexporter/views/components/console_output.py @@ -7,25 +7,25 @@ class ConsoleOutput(toga.Box): def __init__(self): super().__init__(style=Pack(direction=COLUMN, flex=1)) self.output = toga.MultilineTextInput( - readonly=True, style=Pack(flex=1, font_family="monospace", margin_bottom=5, margin_left=10, margin_right=10) + readonly=True, style=Pack(flex=1, font_family='monospace', margin_bottom=5, margin_left=10, margin_right=10) ) self.status_label = toga.Label( - "Application Log", style=Pack(margin_top=5, margin_bottom=5, margin_left=10, margin_right=10) + 'Application Log', style=Pack(margin_top=5, margin_bottom=5, margin_left=10, margin_right=10) ) self.add(self.status_label) self.add(self.output) self.clear_output_btn = toga.Button( - "Clear output", + 'Clear output', on_press=self._clear_console, style=Pack(margin=5), ) self.add(self.clear_output_btn) def log(self, message): - self.output.value += f"{message}\n" + self.output.value += f'{message}\n' self.output.scroll_to_bottom() def _clear_console(self, widget): - self.output.value = "" - self.log("Ready") + self.output.value = '' + self.log('Ready') diff --git a/src/flatfileexporter/views/config_view.py b/src/flatfileexporter/views/config_view.py index bb275f2..6868ca5 100644 --- a/src/flatfileexporter/views/config_view.py +++ b/src/flatfileexporter/views/config_view.py @@ -1,6 +1,7 @@ import toga from toga.style import Pack from toga.style.pack import COLUMN, ROW +from flatfileexporter.data.DAL import check_odbc class ConfigView(toga.Box): @@ -80,6 +81,7 @@ def _test_connection(self, widget): "password": self.password_input.value, } # Call your backend connection test here + check_odbc() print(f"Testing connection: {params}") self.app.main_window.info_dialog( "Success", f"Connected to {params['db_type']} at {params['server']}" diff --git a/src/flatfileexporter/views/connection.py b/src/flatfileexporter/views/connection.py deleted file mode 100644 index 90e9448..0000000 --- a/src/flatfileexporter/views/connection.py +++ /dev/null @@ -1,62 +0,0 @@ -import toga -from toga.style import Pack -from toga.style.pack import COLUMN - - -class ConnectionSection(toga.Box): - def __init__(self, app): - super().__init__(style=Pack(direction=COLUMN, padding=5)) - self.app = app - - self.db_type = toga.Selection( - items=['SQL Server', 'PostgreSQL', 'MySQL'], - style=Pack(padding=(0, 0, 5, 0)) - ) - - self.server_input = toga.TextInput(placeholder='Server') - self.db_input = toga.TextInput(placeholder='Database') - self.username_input = toga.TextInput(placeholder='Username') - self.password_input = toga.PasswordInput(placeholder='Password') - - self.test_btn = toga.Button( - 'Test Connection', - on_press=self.test_connection, - style=Pack(padding=(5, 0)) - ) - - self.add(toga.Label('Database Type:')) - self.add(self.db_type) - self.add(toga.Label('Server:')) - self.add(self.server_input) - self.add(toga.Label('Database:')) - self.add(self.db_input) - self.add(toga.Label('Username:')) - self.add(self.username_input) - self.add(toga.Label('Password:')) - self.add(self.password_input) - self.add(self.test_btn) - - def test_connection(self, widget): - try: - - params = { - 'db_type': self.db_type.value, - 'server': self.server_input.value, - 'database': self.db_input.value, - 'username': self.username_input.value, - 'password': self.password_input.value - } - - # TODO Call backend (uncomment when ready) - # from py.db.connector import test_connection - # test_connection(**params) - # self.app.main_window.dialog(toga.InfoDialog( - # 'Success', - # f"Connected to {params['db_type']} at {params['server']}" - # )) - self.app.main_window.info_dialog( - 'Success', - f"Connected to {params['db_type']} at {params['server']}" - ) - except Exception as e: - self.app.main_window.error_dialog('Error', str(e)) From 987be5686f98dda205a83a556c5efed9ca535883 Mon Sep 17 00:00:00 2001 From: eddyizm Date: Wed, 14 May 2025 22:05:09 -0700 Subject: [PATCH 16/16] wip: starting integration to db backend initial connection --- src/flatfileexporter/data/DAL.py | 101 ++++++++++++-------- src/flatfileexporter/models/data_service.py | 12 +++ src/flatfileexporter/models/files.py | 5 - src/flatfileexporter/views/config_view.py | 43 +++++---- src/flatfileexporter/views/main_view.py | 2 +- 5 files changed, 103 insertions(+), 60 deletions(-) create mode 100644 src/flatfileexporter/models/data_service.py diff --git a/src/flatfileexporter/data/DAL.py b/src/flatfileexporter/data/DAL.py index 7995d28..e94884b 100644 --- a/src/flatfileexporter/data/DAL.py +++ b/src/flatfileexporter/data/DAL.py @@ -5,9 +5,11 @@ import pyodbc as db import pandas as pd import os +import sys from datetime import datetime import xlsxwriter import logging +from flatfileexporter.models.data_service import DatabaseConnectionTest log = logging.getLogger('root') date_var = datetime.now().strftime("%Y%m%d") @@ -16,46 +18,72 @@ DB_STRING = 'DRIVER={{{}}};SERVER={};Trusted_Connection=yes;DATABASE={}' USERPASS_DBSTRING = 'DRIVER={{{}}};SERVER={};DATABASE={};UID={};PWD={}' ODBC_MISSING_MSG = '''ODBC driver not found -please install ODBC Driver 17 for SQL Server -https://www.microsoft.com/en-us/download/details.aspx?id=56567''' -MSDRIVERS = ['ODBC Driver 17 for SQL Server', 'SQL Server Native Client 11.0', 'SQL Server'] +please install ODBC Driver for SQL Server +https://learn.microsoft.com/en-us/sql/connect/odbc/download-odbc-driver-for-sql-server?view=sql-server-ver16''' +MSDRIVERS = ['ODBC Driver 18 for SQL Server', 'ODBC Driver 17 for SQL Server', 'SQL Server Native Client 11.0', 'SQL Server'] -def check_odbc() -> bool: - ''' check for odbc driver ''' - try: - drivers = db.drivers() - if drivers: - log.info('Checking for a compatible driver') - for driver in MSDRIVERS: - if driver in drivers: - log.info(f'Driver found - {driver}') - return True - else: - log.warning(ODBC_MISSING_MSG) - return False - else: - log.warning(ODBC_MISSING_MSG) - return False - except Exception as ex: - log.error(f'Error in check_odbc(): {ex}') +def get_os_and_platform() -> tuple: + return (sys.platform, os.system('uname -a')) -def get_odbc() -> str: - ''' get odbc driver - the check should have already been done and pass - however maybe I need to combine this later to specify which one to use. - ''' - try: - drivers = db.drivers() - if drivers: - log.info('Loading driver') - for driver in MSDRIVERS: - if driver in drivers: - log.info(f'Driver found - {driver}') - return driver +def test_db_connect(params) -> DatabaseConnectionTest: + params.get('db_type') + params.get('database') + params.get('server') + db_connection = DB_STRING.format(MSDRIVERS[0], params.get('server'), params.get('database')) + db_result = DatabaseConnectionTest( + message='', + success_response=False + ) + try: + conn = db.connect(db_connection) + conn.close() + db_result.message = f"Connected to {params.get('server')}" + return db_result + except db.Error as e: + # print("Error connecting to SQL Server:", e) + db_result.message = f"Unable connect to Server: {e}" + return db_result - except Exception as ex: - log.error(f'Error in get_odbc(): {ex}') + +def check_odbc(params, platform) -> bool: + ''' check for odbc driver on nt systems.''' + try: + drivers = db.drivers() + if platform == 'linux': + return test_db_connect(params, drivers) + log.info(os.system('uname -a')) + if drivers: + log.info('Checking for a compatible driver') + for driver in MSDRIVERS: + if driver in drivers: + log.info(f'Driver found - {driver}') + return True + else: + log.warning(ODBC_MISSING_MSG) + return False + else: + log.warning(ODBC_MISSING_MSG) + return False + except Exception as ex: + log.error(f'Error in check_odbc(): {ex}') + + +def get_odbc() -> str: + ''' get odbc driver - the check should have already been done and pass + however maybe I need to combine this later to specify which one to use. + ''' + try: + drivers = db.drivers() + if drivers: + log.info('Loading driver') + for driver in MSDRIVERS: + if driver in drivers: + log.info(f'Driver found - {driver}') + return driver + except Exception as ex: + log.error(f'Error in get_odbc(): {ex}') def read_file(file): @@ -108,6 +136,3 @@ def generate_file(query, db_name, server, separator, directory, extension, file_ pass # TODO Add version - - - diff --git a/src/flatfileexporter/models/data_service.py b/src/flatfileexporter/models/data_service.py new file mode 100644 index 0000000..4999ef7 --- /dev/null +++ b/src/flatfileexporter/models/data_service.py @@ -0,0 +1,12 @@ +from dataclasses import dataclass, asdict + + +@dataclass +class DatabaseConnectionTest: + """Store database connection test response""" + message: str + success_response: bool + + @property + def as_json(self) -> dict: + return asdict(self) diff --git a/src/flatfileexporter/models/files.py b/src/flatfileexporter/models/files.py index 83008c6..2763099 100644 --- a/src/flatfileexporter/models/files.py +++ b/src/flatfileexporter/models/files.py @@ -2,11 +2,6 @@ from enum import Enum from typing import Optional -# class FileType(Enum): -# CSV = "csv" -# TXT = "txt" -# XLSX = "xlsx" - @dataclass class FileExport: diff --git a/src/flatfileexporter/views/config_view.py b/src/flatfileexporter/views/config_view.py index 6868ca5..109bd21 100644 --- a/src/flatfileexporter/views/config_view.py +++ b/src/flatfileexporter/views/config_view.py @@ -1,7 +1,13 @@ import toga from toga.style import Pack from toga.style.pack import COLUMN, ROW -from flatfileexporter.data.DAL import check_odbc +from flatfileexporter.data.DAL import ( + check_odbc, + test_db_connect, + ODBC_MISSING_MSG +) +import logging +log = logging.getLogger('root') class ConfigView(toga.Box): @@ -62,7 +68,7 @@ def __init__(self, app, initial_config): # Add test connection button test_btn = toga.Button( - "Test Connection", on_press=self._test_connection, style=Pack(margin=10) + "Test Connection", on_press=self._test_btn_click, style=Pack(margin=10) ) content_box.add(test_btn) @@ -70,22 +76,27 @@ def __init__(self, app, initial_config): self.add(toolbar) self.add(content_box) - def _test_connection(self, widget): + def get_params(self) -> dict: + return { + "db_type": self.db_type.value, + "server": self.server_input.value, + "database": self.db_input.value, + "username": self.username_input.value, + "password": self.password_input.value, + } + + def _test_btn_click(self, widget): """Test database connection""" try: - params = { - "db_type": self.db_type.value, - "server": self.server_input.value, - "database": self.db_input.value, - "username": self.username_input.value, - "password": self.password_input.value, - } - # Call your backend connection test here - check_odbc() - print(f"Testing connection: {params}") - self.app.main_window.info_dialog( - "Success", f"Connected to {params['db_type']} at {params['server']}" - ) + params = self.get_params() + server = f'{params['db_type']} at {params['server']}' + db_result = test_db_connect(params) + if db_result.success_response: + self.app.main_window.info_dialog( + "Success", f"{db_result.message}" + ) + else: + self.app.main_window.error_dialog("Error", db_result.message) except Exception as e: self.app.main_window.error_dialog("Error", str(e)) diff --git a/src/flatfileexporter/views/main_view.py b/src/flatfileexporter/views/main_view.py index 549a16b..7e8294b 100644 --- a/src/flatfileexporter/views/main_view.py +++ b/src/flatfileexporter/views/main_view.py @@ -82,7 +82,7 @@ def execute_script(self, widget): if self._file_export.sql_query == "": self.console.log("Error, please select a valid sql script.") return - self._file_export.filetype = self.selection.value + self._file_export.filetype = self.extension.value self._file_export.filename = "test" self.app.execute_sql(self._file_export)