From 0928a9793a8d01900c0eed50fd8a0acb2ec8ad4f Mon Sep 17 00:00:00 2001 From: "dries.k" Date: Thu, 4 Aug 2022 00:04:00 +0200 Subject: [PATCH 01/11] simplify image cache --- gotify_tray/database/cache.py | 36 +++++++++++-------------- gotify_tray/database/downloader.py | 42 +++--------------------------- 2 files changed, 19 insertions(+), 59 deletions(-) diff --git a/gotify_tray/database/cache.py b/gotify_tray/database/cache.py index bb321bf..e7ebcd5 100644 --- a/gotify_tray/database/cache.py +++ b/gotify_tray/database/cache.py @@ -1,7 +1,7 @@ import glob import logging import os -import time +import uuid import requests @@ -35,40 +35,34 @@ class Cache(object): def clear(self): self.cursor.execute("DELETE FROM cache") self.database.commit() + + if not self.cache_dir: + logger.error("Cache directory is empty.") + return + for filename in glob.glob(self.cache_dir + "/*"): os.remove(filename) def lookup(self, key: str) -> str: - q = self.cursor.execute( - "SELECT filename, cached_on FROM cache WHERE url=?", (key,) - ).fetchone() - if q: + if q := self.cursor.execute( + "SELECT filename FROM cache WHERE url=?", (key,) + ).fetchone(): # Cache hit - filename, cached_on = q - return filename if os.path.exists(filename) else "" + return q["filename"] if os.path.exists(q["filename"]) else "" else: # Cache miss return "" - def store( - self, key: str, response: requests.Response, add_time: bool = True - ) -> str: + def store(self, key: str, response: requests.Response) -> str: # Create a file and store the response contents - filename = str(time.time()).replace(".", "") if add_time else "" - if "Content-Disposition" in response.headers.keys(): - filename += response.headers["Content-Disposition"] - else: - filename += response.url.split("/")[-1] + filepath = os.path.join(self.cache_dir, uuid.uuid4().hex) - filename = "".join([c for c in filename if c.isalpha() or c.isdigit()]).rstrip() - filename = os.path.join(self.cache_dir, filename) - - with open(filename, "wb") as f: + with open(filepath, "wb") as f: f.write(response.content) self.cursor.execute( "INSERT INTO cache (url, filename, cached_on) VALUES(?, ?, datetime('now', 'localtime'))", - (key, filename), + (key, filepath), ) self.database.commit() - return filename + return filepath diff --git a/gotify_tray/database/downloader.py b/gotify_tray/database/downloader.py index 9e3f959..e3f0147 100644 --- a/gotify_tray/database/downloader.py +++ b/gotify_tray/database/downloader.py @@ -22,43 +22,12 @@ class Downloader(object): """ return self.session.get(url) - def get_bytes(self, url: str, cached: bool = True, add_time: bool = True) -> bytes: - """ - Get the content of an http get request, as bytes. - Optionally use the cache. - """ - if cached: - # Retrieve from cache - filename = self.cache.lookup(url) - if filename: - with open(filename, "rb") as f: - return f.read() - - try: - response = self.get(url) - except Exception as e: - logger.error(f"get_bytes: downloading {url} failed.: {e}") - return b"" - - if not response.ok: - return b"" - - if cached: - # Store in cache - self.cache.store(url, response, add_time=add_time) - - return response.content - - def get_filename( - self, url: str, retrieve_from_cache: bool = True, add_time: bool = True - ) -> str: + def get_filename(self, url: str) -> str: """ Get the content of an http get request, as a filename. """ - if retrieve_from_cache: - filename = self.cache.lookup(url) - if filename: - return filename + if filename := self.cache.lookup(url): + return filename try: response = self.get(url) @@ -66,7 +35,4 @@ class Downloader(object): logger.error(f"get_filename: downloading {url} failed.: {e}") return "" - if not response.ok: - return "" - - return self.cache.store(url, response, add_time=add_time) + return self.cache.store(url, response) if response.ok else "" From a0f6530ff5c73a50712944c2a2a85e062b40c60a Mon Sep 17 00:00:00 2001 From: "dries.k" Date: Thu, 4 Aug 2022 00:04:00 +0200 Subject: [PATCH 02/11] simplify image cache --- gotify_tray/database/cache.py | 36 +++++++++++-------------- gotify_tray/database/downloader.py | 42 +++--------------------------- 2 files changed, 19 insertions(+), 59 deletions(-) diff --git a/gotify_tray/database/cache.py b/gotify_tray/database/cache.py index bb321bf..e7ebcd5 100644 --- a/gotify_tray/database/cache.py +++ b/gotify_tray/database/cache.py @@ -1,7 +1,7 @@ import glob import logging import os -import time +import uuid import requests @@ -35,40 +35,34 @@ class Cache(object): def clear(self): self.cursor.execute("DELETE FROM cache") self.database.commit() + + if not self.cache_dir: + logger.error("Cache directory is empty.") + return + for filename in glob.glob(self.cache_dir + "/*"): os.remove(filename) def lookup(self, key: str) -> str: - q = self.cursor.execute( - "SELECT filename, cached_on FROM cache WHERE url=?", (key,) - ).fetchone() - if q: + if q := self.cursor.execute( + "SELECT filename FROM cache WHERE url=?", (key,) + ).fetchone(): # Cache hit - filename, cached_on = q - return filename if os.path.exists(filename) else "" + return q["filename"] if os.path.exists(q["filename"]) else "" else: # Cache miss return "" - def store( - self, key: str, response: requests.Response, add_time: bool = True - ) -> str: + def store(self, key: str, response: requests.Response) -> str: # Create a file and store the response contents - filename = str(time.time()).replace(".", "") if add_time else "" - if "Content-Disposition" in response.headers.keys(): - filename += response.headers["Content-Disposition"] - else: - filename += response.url.split("/")[-1] + filepath = os.path.join(self.cache_dir, uuid.uuid4().hex) - filename = "".join([c for c in filename if c.isalpha() or c.isdigit()]).rstrip() - filename = os.path.join(self.cache_dir, filename) - - with open(filename, "wb") as f: + with open(filepath, "wb") as f: f.write(response.content) self.cursor.execute( "INSERT INTO cache (url, filename, cached_on) VALUES(?, ?, datetime('now', 'localtime'))", - (key, filename), + (key, filepath), ) self.database.commit() - return filename + return filepath diff --git a/gotify_tray/database/downloader.py b/gotify_tray/database/downloader.py index 9e3f959..e3f0147 100644 --- a/gotify_tray/database/downloader.py +++ b/gotify_tray/database/downloader.py @@ -22,43 +22,12 @@ class Downloader(object): """ return self.session.get(url) - def get_bytes(self, url: str, cached: bool = True, add_time: bool = True) -> bytes: - """ - Get the content of an http get request, as bytes. - Optionally use the cache. - """ - if cached: - # Retrieve from cache - filename = self.cache.lookup(url) - if filename: - with open(filename, "rb") as f: - return f.read() - - try: - response = self.get(url) - except Exception as e: - logger.error(f"get_bytes: downloading {url} failed.: {e}") - return b"" - - if not response.ok: - return b"" - - if cached: - # Store in cache - self.cache.store(url, response, add_time=add_time) - - return response.content - - def get_filename( - self, url: str, retrieve_from_cache: bool = True, add_time: bool = True - ) -> str: + def get_filename(self, url: str) -> str: """ Get the content of an http get request, as a filename. """ - if retrieve_from_cache: - filename = self.cache.lookup(url) - if filename: - return filename + if filename := self.cache.lookup(url): + return filename try: response = self.get(url) @@ -66,7 +35,4 @@ class Downloader(object): logger.error(f"get_filename: downloading {url} failed.: {e}") return "" - if not response.ok: - return "" - - return self.cache.store(url, response, add_time=add_time) + return self.cache.store(url, response) if response.ok else "" From ef747145ac54c3833f4e094705cf01c4bdefcc1d Mon Sep 17 00:00:00 2001 From: "dries.k" Date: Thu, 4 Aug 2022 00:14:33 +0200 Subject: [PATCH 03/11] manual refresh also redownloads the application images --- gotify_tray/gui/MainApplication.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gotify_tray/gui/MainApplication.py b/gotify_tray/gui/MainApplication.py index c01e2ee..d494fa7 100644 --- a/gotify_tray/gui/MainApplication.py +++ b/gotify_tray/gui/MainApplication.py @@ -7,7 +7,7 @@ from typing import List, Union from gotify_tray import gotify from gotify_tray.__version__ import __title__ -from gotify_tray.database import Downloader, Settings +from gotify_tray.database import Cache, Downloader, Settings from gotify_tray.tasks import ( DeleteApplicationMessagesTask, DeleteAllMessagesTask, @@ -308,6 +308,11 @@ class MainApplication(QtWidgets.QApplication): self.messages_model.clear() + def refresh_callback(self): + # Manual refresh -> also reset the image cache + Cache().clear() + self.refresh_applications() + def settings_callback(self): settings_dialog = SettingsDialog() settings_dialog.quit_requested.connect(self.quit) @@ -343,7 +348,7 @@ class MainApplication(QtWidgets.QApplication): self.tray.messageClicked.connect(self.main_window.bring_to_front) self.tray.activated.connect(self.tray_activated_callback) - self.main_window.refresh.connect(self.refresh_applications) + self.main_window.refresh.connect(self.refresh_callback) self.main_window.delete_all.connect(self.delete_all_messages_callback) self.main_window.application_selection_changed.connect( self.application_selection_changed_callback From 54b45fa33c7c41336f4cf4f2d4b29da71745e2e2 Mon Sep 17 00:00:00 2001 From: "dries.k" Date: Thu, 4 Aug 2022 00:04:00 +0200 Subject: [PATCH 04/11] simplify image cache --- gotify_tray/database/cache.py | 36 +++++++++++-------------- gotify_tray/database/downloader.py | 42 +++--------------------------- 2 files changed, 19 insertions(+), 59 deletions(-) diff --git a/gotify_tray/database/cache.py b/gotify_tray/database/cache.py index bb321bf..e7ebcd5 100644 --- a/gotify_tray/database/cache.py +++ b/gotify_tray/database/cache.py @@ -1,7 +1,7 @@ import glob import logging import os -import time +import uuid import requests @@ -35,40 +35,34 @@ class Cache(object): def clear(self): self.cursor.execute("DELETE FROM cache") self.database.commit() + + if not self.cache_dir: + logger.error("Cache directory is empty.") + return + for filename in glob.glob(self.cache_dir + "/*"): os.remove(filename) def lookup(self, key: str) -> str: - q = self.cursor.execute( - "SELECT filename, cached_on FROM cache WHERE url=?", (key,) - ).fetchone() - if q: + if q := self.cursor.execute( + "SELECT filename FROM cache WHERE url=?", (key,) + ).fetchone(): # Cache hit - filename, cached_on = q - return filename if os.path.exists(filename) else "" + return q["filename"] if os.path.exists(q["filename"]) else "" else: # Cache miss return "" - def store( - self, key: str, response: requests.Response, add_time: bool = True - ) -> str: + def store(self, key: str, response: requests.Response) -> str: # Create a file and store the response contents - filename = str(time.time()).replace(".", "") if add_time else "" - if "Content-Disposition" in response.headers.keys(): - filename += response.headers["Content-Disposition"] - else: - filename += response.url.split("/")[-1] + filepath = os.path.join(self.cache_dir, uuid.uuid4().hex) - filename = "".join([c for c in filename if c.isalpha() or c.isdigit()]).rstrip() - filename = os.path.join(self.cache_dir, filename) - - with open(filename, "wb") as f: + with open(filepath, "wb") as f: f.write(response.content) self.cursor.execute( "INSERT INTO cache (url, filename, cached_on) VALUES(?, ?, datetime('now', 'localtime'))", - (key, filename), + (key, filepath), ) self.database.commit() - return filename + return filepath diff --git a/gotify_tray/database/downloader.py b/gotify_tray/database/downloader.py index 9e3f959..e3f0147 100644 --- a/gotify_tray/database/downloader.py +++ b/gotify_tray/database/downloader.py @@ -22,43 +22,12 @@ class Downloader(object): """ return self.session.get(url) - def get_bytes(self, url: str, cached: bool = True, add_time: bool = True) -> bytes: - """ - Get the content of an http get request, as bytes. - Optionally use the cache. - """ - if cached: - # Retrieve from cache - filename = self.cache.lookup(url) - if filename: - with open(filename, "rb") as f: - return f.read() - - try: - response = self.get(url) - except Exception as e: - logger.error(f"get_bytes: downloading {url} failed.: {e}") - return b"" - - if not response.ok: - return b"" - - if cached: - # Store in cache - self.cache.store(url, response, add_time=add_time) - - return response.content - - def get_filename( - self, url: str, retrieve_from_cache: bool = True, add_time: bool = True - ) -> str: + def get_filename(self, url: str) -> str: """ Get the content of an http get request, as a filename. """ - if retrieve_from_cache: - filename = self.cache.lookup(url) - if filename: - return filename + if filename := self.cache.lookup(url): + return filename try: response = self.get(url) @@ -66,7 +35,4 @@ class Downloader(object): logger.error(f"get_filename: downloading {url} failed.: {e}") return "" - if not response.ok: - return "" - - return self.cache.store(url, response, add_time=add_time) + return self.cache.store(url, response) if response.ok else "" From 9ec3ca401d2ca65dcaf5be68830eb0ae143e91ac Mon Sep 17 00:00:00 2001 From: "dries.k" Date: Thu, 4 Aug 2022 00:14:33 +0200 Subject: [PATCH 05/11] manual refresh also redownloads the application images --- gotify_tray/gui/MainApplication.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gotify_tray/gui/MainApplication.py b/gotify_tray/gui/MainApplication.py index c01e2ee..d494fa7 100644 --- a/gotify_tray/gui/MainApplication.py +++ b/gotify_tray/gui/MainApplication.py @@ -7,7 +7,7 @@ from typing import List, Union from gotify_tray import gotify from gotify_tray.__version__ import __title__ -from gotify_tray.database import Downloader, Settings +from gotify_tray.database import Cache, Downloader, Settings from gotify_tray.tasks import ( DeleteApplicationMessagesTask, DeleteAllMessagesTask, @@ -308,6 +308,11 @@ class MainApplication(QtWidgets.QApplication): self.messages_model.clear() + def refresh_callback(self): + # Manual refresh -> also reset the image cache + Cache().clear() + self.refresh_applications() + def settings_callback(self): settings_dialog = SettingsDialog() settings_dialog.quit_requested.connect(self.quit) @@ -343,7 +348,7 @@ class MainApplication(QtWidgets.QApplication): self.tray.messageClicked.connect(self.main_window.bring_to_front) self.tray.activated.connect(self.tray_activated_callback) - self.main_window.refresh.connect(self.refresh_applications) + self.main_window.refresh.connect(self.refresh_callback) self.main_window.delete_all.connect(self.delete_all_messages_callback) self.main_window.application_selection_changed.connect( self.application_selection_changed_callback From d18bcbbdeddf417df251fce8d0c5723502fdb052 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Aug 2022 22:34:46 +0000 Subject: [PATCH 06/11] Bump websocket-client from 1.3.2 to 1.3.3 Bumps [websocket-client](https://github.com/websocket-client/websocket-client) from 1.3.2 to 1.3.3. - [Release notes](https://github.com/websocket-client/websocket-client/releases) - [Changelog](https://github.com/websocket-client/websocket-client/blob/master/ChangeLog) - [Commits](https://github.com/websocket-client/websocket-client/compare/v1.3.2...v1.3.3) --- updated-dependencies: - dependency-name: websocket-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f054c43..eed2fe6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ requests==2.27.1 -websocket-client==1.3.2 +websocket-client==1.3.3 pyqt6==6.3.0 python-dateutil==2.8.2 From 22df401d9b9d9ad63c9c3e5cff4f08474cfd6bfd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Aug 2022 22:36:10 +0000 Subject: [PATCH 07/11] Bump pyqt6 from 6.3.0 to 6.3.1 Bumps [pyqt6](https://www.riverbankcomputing.com/software/pyqt/) from 6.3.0 to 6.3.1. --- updated-dependencies: - dependency-name: pyqt6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index eed2fe6..b36fb17 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ requests==2.27.1 websocket-client==1.3.3 -pyqt6==6.3.0 +pyqt6==6.3.1 python-dateutil==2.8.2 From 6f29acc405e05cb825ddfa6be60c3413a06538fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Aug 2022 22:36:38 +0000 Subject: [PATCH 08/11] Bump requests from 2.27.1 to 2.28.1 Bumps [requests](https://github.com/psf/requests) from 2.27.1 to 2.28.1. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.27.1...v2.28.1) --- updated-dependencies: - dependency-name: requests dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index eed2fe6..22b558c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -requests==2.27.1 +requests==2.28.1 websocket-client==1.3.3 pyqt6==6.3.0 python-dateutil==2.8.2 From e7918b59c0212ef30e404df7909f300a1445e0eb Mon Sep 17 00:00:00 2001 From: "dries.k" Date: Thu, 4 Aug 2022 00:48:47 +0200 Subject: [PATCH 09/11] do not always create all builds --- .github/workflows/develop.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 5623d74..9430872 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -4,7 +4,6 @@ on: push: branches-ignore: - master - - "dependabot/**" tags-ignore: - '*' @@ -37,6 +36,7 @@ jobs: path: gotify-tray-installer-win.exe build-ubuntu: + if: ${{ github.actor != 'dependabot[bot]' }} strategy: matrix: tag: [focal] @@ -60,6 +60,7 @@ jobs: path: gotify-tray_amd64_ubuntu_${{ matrix.tag }}.deb build-macos: + if: ${{ github.actor != 'dependabot[bot]' }} runs-on: macos-12 steps: - uses: actions/checkout@v2 From 64b89c16e99ffa83674c39f9fc2d3e1ce309ff31 Mon Sep 17 00:00:00 2001 From: "dries.k" Date: Thu, 4 Aug 2022 18:14:55 +0200 Subject: [PATCH 10/11] retain wheel name for artifact --- .github/workflows/develop.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 9430872..3f03028 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -100,5 +100,4 @@ jobs: - name: Upload artifact uses: actions/upload-artifact@v2 with: - name: gotify_tray.whl path: dist/gotify_tray-*.whl From 873f8005158f02b74226ba8b22f5a1d0ee12ce94 Mon Sep 17 00:00:00 2001 From: "dries.k" Date: Thu, 4 Aug 2022 18:21:07 +0200 Subject: [PATCH 11/11] upload wheel as release artifact --- .github/workflows/build.yml | 47 ++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 235da2e..07acb93 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -100,27 +100,6 @@ jobs: name: gotify-tray.dmg path: gotify-tray.dmg - release: - runs-on: ubuntu-latest - needs: [build-win64, build-ubuntu, build-debian, build-macos] - steps: - - uses: actions/checkout@v2 - - uses: actions/download-artifact@master - - name: Release - uses: marvinpinto/action-automatic-releases@latest - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - prerelease: false - files: | - gotify-tray-installer-win.exe - gotify-tray_amd64_ubuntu_focal.deb - gotify-tray_amd64_ubuntu_hirsute.deb - gotify-tray_amd64_ubuntu_impish.deb - gotify-tray_amd64_ubuntu_jammy.deb - gotify-tray_amd64_debian_bullseye.deb - gotify-tray_amd64_debian_bookworm.deb - gotify-tray.dmg - pypi: runs-on: ubuntu-latest steps: @@ -141,3 +120,29 @@ jobs: uses: pypa/gh-action-pypi-publish@release/v1 with: password: ${{ secrets.PYPI_API_TOKEN }} + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + path: dist/gotify_tray-*.whl + + release: + runs-on: ubuntu-latest + needs: [build-win64, build-ubuntu, build-debian, build-macos, pypi] + steps: + - uses: actions/checkout@v2 + - uses: actions/download-artifact@master + - name: Release + uses: marvinpinto/action-automatic-releases@latest + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + prerelease: false + files: | + gotify-tray-installer-win.exe + gotify-tray_amd64_ubuntu_focal.deb + gotify-tray_amd64_ubuntu_hirsute.deb + gotify-tray_amd64_ubuntu_impish.deb + gotify-tray_amd64_ubuntu_jammy.deb + gotify-tray_amd64_debian_bullseye.deb + gotify-tray_amd64_debian_bookworm.deb + gotify-tray.dmg + gotify_tray-*.whl