From 94b36c6ab60c890ba8d3eda4e00b1fd717e721e3 Mon Sep 17 00:00:00 2001 From: Julien M Date: Tue, 12 Mar 2024 22:12:27 +0100 Subject: [PATCH 1/8] =?UTF-8?q?feature:=20ajoute=20une=20option=20pour=20a?= =?UTF-8?q?ctiver/d=C3=A9sactiver=20l'int=C3=A9gration=20au=20fil=20d'actu?= =?UTF-8?q?alit=C3=A9=20de=20QGIS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qtribu/gui/dlg_settings.py | 4 +- qtribu/gui/dlg_settings.ui | 138 ++++++++++++++++++++------------- qtribu/toolbelt/preferences.py | 3 +- 3 files changed, 91 insertions(+), 54 deletions(-) diff --git a/qtribu/gui/dlg_settings.py b/qtribu/gui/dlg_settings.py index 1f6ccff3..d5ba3ed1 100644 --- a/qtribu/gui/dlg_settings.py +++ b/qtribu/gui/dlg_settings.py @@ -93,6 +93,7 @@ def apply(self): settings.browser = self.opt_browser_group.checkedId() settings.notify_push_info = self.opt_notif_push_msg.isChecked() settings.notify_push_duration = self.sbx_notif_duration.value() + settings.integration_qgis_news_feed = self.chb_integration_news_feed.isChecked() settings.license_global_accept = self.chb_license_global_accept.isChecked() # misc @@ -119,6 +120,7 @@ def load_settings(self) -> dict: self.opt_browser_group.button(settings.browser).setChecked(True) self.opt_notif_push_msg.setChecked(settings.notify_push_info) self.sbx_notif_duration.setValue(settings.notify_push_duration) + self.chb_integration_news_feed.setChecked(settings.integration_qgis_news_feed) self.chb_license_global_accept.setChecked(settings.license_global_accept) # misc @@ -128,7 +130,7 @@ def load_settings(self) -> dict: def reset_read_history(self): """Set latest_content_guid to None.""" new_settings = PlgSettingsStructure( - latest_content_guid=None, + latest_content_guid="", ) # dump new settings into QgsSettings diff --git a/qtribu/gui/dlg_settings.ui b/qtribu/gui/dlg_settings.ui index c0313b93..ca62a866 100644 --- a/qtribu/gui/dlg_settings.ui +++ b/qtribu/gui/dlg_settings.ui @@ -6,7 +6,7 @@ 0 0 - 697 + 809 468 @@ -88,8 +88,8 @@ Features - - + + 175 @@ -102,49 +102,58 @@ 30 - - Which web browser to use to display articles. - - Open in web browser: + Notify for new content: - - + + - 175 + 0 25 - 250 + 16777215 30 - Notify for new content: + Embedded browser + + + true - - + + + + + 0 + 0 + + 0 25 - - - 16777215 - 30 - + + The only 'Show QGIS news feed on welcome page' must be checked in General Settings - Reset read history + Insert latest item in QGIS news feed + + + + + + true @@ -176,6 +185,28 @@ + + + + + 175 + 25 + + + + + 250 + 30 + + + + Which web browser to use to display articles. + + + Open in web browser: + + + @@ -195,8 +226,21 @@ - - + + + + + 0 + 25 + + + + Accept publication license globally + + + + + 0 @@ -210,10 +254,7 @@ - Embedded browser - - - true + Reset read history @@ -256,13 +297,6 @@ - - - - Accept publication license globally - - - @@ -300,8 +334,8 @@ false - - + + 0 @@ -314,16 +348,25 @@ 30 + + Enable debug mode. + + + true + + + + - X.X.x + Debug mode (degraded performances) - - Qt::NoTextInteraction + + false - - + + 0 @@ -336,20 +379,11 @@ 30 - - Enable debug mode. - - - true - - - - - Debug mode (degraded performances) + X.X.x - - false + + Qt::NoTextInteraction diff --git a/qtribu/toolbelt/preferences.py b/qtribu/toolbelt/preferences.py index 91079d2f..dccf1750 100644 --- a/qtribu/toolbelt/preferences.py +++ b/qtribu/toolbelt/preferences.py @@ -34,9 +34,10 @@ class PlgSettingsStructure: browser: int = 1 notify_push_info: bool = True notify_push_duration: int = 10 - latest_content_guid: str = None + latest_content_guid: str = "" splash_screen_enabled: bool = False license_global_accept: bool = False + integration_qgis_news_feed: bool = True # network network_http_user_agent: str = f"{__title__}/{__version__}" From d6ba60cba2dbea2eabafa4eca0e08caf145615fc Mon Sep 17 00:00:00 2001 From: Julien M Date: Tue, 12 Mar 2024 22:12:47 +0100 Subject: [PATCH 2/8] feature: add category and multiple authors supprot --- qtribu/logic/custom_datatypes.py | 1 + qtribu/logic/rss_reader.py | 97 ++++++++++++++++++++++++++++++-- 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/qtribu/logic/custom_datatypes.py b/qtribu/logic/custom_datatypes.py index 2a6d6095..e0020e59 100644 --- a/qtribu/logic/custom_datatypes.py +++ b/qtribu/logic/custom_datatypes.py @@ -9,6 +9,7 @@ field_names=[ "abstract", "author", + "categories", "date_pub", "guid", "image_length", diff --git a/qtribu/logic/rss_reader.py b/qtribu/logic/rss_reader.py index 55dd5a3d..f2cdea3c 100644 --- a/qtribu/logic/rss_reader.py +++ b/qtribu/logic/rss_reader.py @@ -13,6 +13,12 @@ import logging import xml.etree.ElementTree as ET from email.utils import parsedate +from typing import Optional + +from qgis.core import QgsSettings + +# QGIS +from qgis.PyQt.QtCore import QCoreApplication # project from qtribu.__about__ import __title__, __version__ @@ -33,7 +39,7 @@ class RssMiniReader: """Minimalist RSS feed parser.""" - FEED_ITEMS: tuple = None + FEED_ITEMS: Optional[tuple] = None HEADERS: dict = { b"Accept": b"application/xml", b"User-Agent": bytes(f"{__title__}/{__version__}", "utf8"), @@ -72,20 +78,23 @@ def read_feed(self, in_xml: str) -> tuple[RssItem]: feed_items.append( RssItem( abstract=item.find("description").text, - author=item.find("author").text or None, + author=[author.text for author in item.findall("author")] + or None, + categories=[ + category.text for category in item.findall("category") + ] + or None, date_pub=parsedate(item.find("pubDate").text), + guid=item.find("guid").text, image_length=item.find("enclosure").attrib.get("length"), image_type=item.find("enclosure").attrib.get("type"), image_url=item.find("enclosure").attrib.get("url"), - guid=item.find("guid").text, title=item.find("title").text, url=item.find("link").text, ) ) except Exception as err: - err_msg = "Feed item (index = {}) triggers an error. Trace: {}".format( - feed_items.index(item), err - ) + err_msg = f"Feed item triggers an error. Trace: {err}" logger.error(err_msg) self.log(message=err_msg, log_level=2) @@ -122,3 +131,79 @@ def has_new_content(self) -> bool: return True else: return False + + def add_latest_item_to_news_feed(self) -> bool: + """Check if the news feed integration is enabled. If so, insert the latest RSS + item at the top of the news feed. + + :return: True if it worked. False if disabled or something wen wrong. + :rtype: bool + """ + + # sample stucture: + # news-feed\items\httpsfeedqgisorg\entries\items\64\title=It\x2019s OSM\x2019s... + # news-feed\items\httpsfeedqgisorg\entries\items\65\content="

The Cyber/..

" + # news-feed\items\httpsfeedqgisorg\entries\items\65\expiry=@DateTime(\0\0\0\x10\0\0\0\0\0\0%\x8b\xd1\x3\xba\xe1\x38\0) + # news-feed\items\httpsfeedqgisorg\entries\items\65\image-url=https://feed.qgis.org/media/feedimages/2023/11/09/europe.jpg + # news-feed\items\httpsfeedqgisorg\entries\items\65\link=https://www.osgeo.org/foundation-news/eu-cyber-resilience-act/ + # news-feed\items\httpsfeedqgisorg\entries\items\65\sticky=false + + plg_settings = PlgOptionsManager.get_plg_settings() + if not plg_settings.integration_qgis_news_feed: + self.log( + message="The QGIS news feed integration is disabled. Abort!", + log_level=4, + ) + return False + + qsettings = QgsSettings() + + # get latest QGIS item id + latest_geotribu_article = self.latest_item + item_id = 99 + + qsettings.setValue( + key=f"news-feed/items/httpsfeedqgisorg/entries/items/{item_id}/title", + value=f"[Geotribu] {latest_geotribu_article.title}", + section=QgsSettings.App, + ) + qsettings.setValue( + key=f"news-feed/items/httpsfeedqgisorg/entries/items/{item_id}/content", + value=f"

{latest_geotribu_article.abstract}

" + + self.tr("Author(s): ") + + f"{', '.join(latest_geotribu_article.author)}

" + + self.tr("Keywords: ") + + f"{', '.join(latest_geotribu_article.categories)}

", + section=QgsSettings.App, + ) + qsettings.setValue( + key=f"news-feed/items/httpsfeedqgisorg/entries/items/{item_id}/image-url", + value=latest_geotribu_article.image_url, + section=QgsSettings.App, + ) + qsettings.setValue( + key=f"news-feed/items/httpsfeedqgisorg/entries/items/{item_id}/link", + value=latest_geotribu_article.url, + section=QgsSettings.App, + ) + + qsettings.sync() + + self.log( + message=f"Latest Geotribu content inserted in QGIS news feed: " + f"{latest_geotribu_article.title}", + log_level=0, + ) + + return True + + def tr(self, message: str) -> str: + """Get the translation for a string using Qt translation API. + + :param message: string to be translated. + :type message: str + + :returns: Translated version of message. + :rtype: str + """ + return QCoreApplication.translate(self.__class__.__name__, message) From 29a13b37dd2da70ab365c1577eac7219f72ba747 Mon Sep 17 00:00:00 2001 From: Julien M Date: Tue, 12 Mar 2024 22:13:14 +0100 Subject: [PATCH 3/8] refacto: split method parsing/unparsing the URL --- qtribu/toolbelt/network_manager.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/qtribu/toolbelt/network_manager.py b/qtribu/toolbelt/network_manager.py index d69c5027..1eb87da7 100644 --- a/qtribu/toolbelt/network_manager.py +++ b/qtribu/toolbelt/network_manager.py @@ -11,6 +11,7 @@ # Standard library import logging from functools import lru_cache +from typing import Optional from urllib.parse import urlparse, urlunparse # PyQGIS @@ -58,7 +59,7 @@ def tr(self, message: str) -> str: return QCoreApplication.translate(self.__class__.__name__, message) @lru_cache(maxsize=128) - def build_url(self, url: str) -> QUrl: + def add_utm_to_url(self, url: str) -> str: """Returns the URL using the plugin settings. :param url: input URL to complete @@ -71,9 +72,21 @@ def build_url(self, url: str) -> QUrl: clean_url = parsed_url._replace( query=PlgOptionsManager.get_plg_settings().request_path ) - return QUrl(urlunparse(clean_url)) + return urlunparse(clean_url) + + @lru_cache(maxsize=128) + def build_url(self, url: str) -> QUrl: + """Returns the URL using the plugin settings. + + :param url: input URL to complete + :type url: str + + :return: Qt URL object with full parameters + :rtype: QUrl + """ + return QUrl(self.add_utm_to_url(url)) - def build_request(self, url: QUrl = None) -> QNetworkRequest: + def build_request(self, url: Optional[QUrl] = None) -> QNetworkRequest: """Build request object using plugin settings. :return: network request object. From e4da5a71fe4dd44c98f24b93a0049e40f44571f9 Mon Sep 17 00:00:00 2001 From: Julien M Date: Tue, 12 Mar 2024 22:14:39 +0100 Subject: [PATCH 4/8] feature: trigger the news feed integration --- qtribu/plugin_main.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/qtribu/plugin_main.py b/qtribu/plugin_main.py index 0558efd5..a4fb243d 100644 --- a/qtribu/plugin_main.py +++ b/qtribu/plugin_main.py @@ -229,6 +229,20 @@ def post_ui_init(self): log_level=2, push=True, ) + return + + # insert latest item within news feed + try: + self.rss_rdr.add_latest_item_to_news_feed() + except Exception as err: + self.log( + message=self.tr( + f"Unable to insert latest item within QGIS news feed. Trace: {err}" + ), + log_level=2, + push=True, + ) + return def tr(self, message: str) -> str: """Get the translation for a string using Qt translation API. From 98d4ed07fe6daf71804d5681a54344c56ff342ae Mon Sep 17 00:00:00 2001 From: Julien Date: Wed, 13 Mar 2024 11:13:24 +0100 Subject: [PATCH 5/8] Update qtribu/logic/rss_reader.py Co-authored-by: Guilhem Allaman <40383801+gounux@users.noreply.github.com> Signed-off-by: Julien --- qtribu/logic/rss_reader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtribu/logic/rss_reader.py b/qtribu/logic/rss_reader.py index f2cdea3c..cc9f8930 100644 --- a/qtribu/logic/rss_reader.py +++ b/qtribu/logic/rss_reader.py @@ -192,7 +192,7 @@ def add_latest_item_to_news_feed(self) -> bool: self.log( message=f"Latest Geotribu content inserted in QGIS news feed: " f"{latest_geotribu_article.title}", - log_level=0, + log_level=Qgis.Info, ) return True From b6f26e28153dbb98169d24dd7b82a9db1577830b Mon Sep 17 00:00:00 2001 From: Julien Date: Wed, 13 Mar 2024 11:13:32 +0100 Subject: [PATCH 6/8] Update qtribu/plugin_main.py Co-authored-by: Guilhem Allaman <40383801+gounux@users.noreply.github.com> Signed-off-by: Julien --- qtribu/plugin_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtribu/plugin_main.py b/qtribu/plugin_main.py index a4fb243d..23c8990d 100644 --- a/qtribu/plugin_main.py +++ b/qtribu/plugin_main.py @@ -239,7 +239,7 @@ def post_ui_init(self): message=self.tr( f"Unable to insert latest item within QGIS news feed. Trace: {err}" ), - log_level=2, + log_level=Qgis.Critical, push=True, ) return From ba7a33ccc7c02b762bf7d8f65f3c086fb896c75f Mon Sep 17 00:00:00 2001 From: "Julien M." Date: Wed, 13 Mar 2024 11:17:05 +0100 Subject: [PATCH 7/8] import qgis.core.Qgis for log levels --- qtribu/logic/rss_reader.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qtribu/logic/rss_reader.py b/qtribu/logic/rss_reader.py index cc9f8930..985fcd4f 100644 --- a/qtribu/logic/rss_reader.py +++ b/qtribu/logic/rss_reader.py @@ -15,9 +15,8 @@ from email.utils import parsedate from typing import Optional -from qgis.core import QgsSettings - # QGIS +from qgis.core import Qgis, QgsSettings from qgis.PyQt.QtCore import QCoreApplication # project From 0c34de026db3336120fafc2b34ae383fb19ae7f2 Mon Sep 17 00:00:00 2001 From: "Julien M." Date: Wed, 13 Mar 2024 11:20:43 +0100 Subject: [PATCH 8/8] fix missing import --- qtribu/plugin_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtribu/plugin_main.py b/qtribu/plugin_main.py index 23c8990d..85646e03 100644 --- a/qtribu/plugin_main.py +++ b/qtribu/plugin_main.py @@ -9,7 +9,7 @@ from pathlib import Path # PyQGIS -from qgis.core import QgsApplication, QgsSettings +from qgis.core import Qgis, QgsApplication, QgsSettings from qgis.gui import QgisInterface from qgis.PyQt.QtCore import QCoreApplication, QLocale, QTranslator, QUrl from qgis.PyQt.QtGui import QDesktopServices, QIcon