From 361f14425a9a4e0c080dbe6b3a2b2db6258283ef Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 30 Nov 2023 12:19:28 +1000 Subject: [PATCH 1/6] Use backend api to retrieve country listings --- koordinates/api/client.py | 39 +++++++++++++++++++++++++++ koordinates/gui/koordinates.py | 49 +++++++++++++++++++++++++++------- 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/koordinates/api/client.py b/koordinates/api/client.py index 707206c..0d30b5e 100644 --- a/koordinates/api/client.py +++ b/koordinates/api/client.py @@ -48,6 +48,7 @@ class KoordinatesClient(QObject): loginChanged = pyqtSignal(bool) error_occurred = pyqtSignal(str) explore_sections_retrieved = pyqtSignal(object) + data_options_retrieved = pyqtSignal(dict) BASE64_ENCODED_SVG_HEADER = 'data:image/svg+xml;base64,' @@ -69,6 +70,7 @@ def __init__(self): KoordinatesClient.__instance = self self._explore_sections_reply: Optional[QNetworkReply] = None + self._data_options_reply: Optional[QNetworkReply] = None self.layers = {} self._dataset_details = {} @@ -103,6 +105,10 @@ def login(self, apiKey): self._explore_sections_reply.finished.connect( partial(self._sections_reply_finished, self._explore_sections_reply)) + self._data_options_reply = self.data_options_async() + self._data_options_reply.finished.connect( + partial(self._data_options_reply_finished, self._data_options_reply)) + def logout(self): oldKey = self.apiKey @@ -139,6 +145,25 @@ def _sections_reply_finished(self, reply: QNetworkReply): self.explore_sections_retrieved.emit(sections) + def _data_options_reply_finished(self, reply: QNetworkReply): + if sip.isdeleted(self): + return + + if reply != self._data_options_reply: + # an old reply we don't care about anymore + return + + self._data_options_reply = None + if reply.error() == QNetworkReply.OperationCanceledError: + return + + if reply.error() != QNetworkReply.NoError: + print('error occurred :(') + return + + content = json.loads(reply.readAll().data().decode()) + self.data_options_retrieved.emit(content) + def _build_datasets_request(self, page=1, query: Optional[DataBrowserQuery] = None, @@ -235,6 +260,20 @@ def _build_publishers_request(self, return endpoint, headers, params + def data_options_async(self) -> QNetworkReply: + """ + Retrieve data OPTIONS request asynchronously + """ + headers = {} + params = {} + endpoint = "data/" + + network_request = self._build_request(endpoint, headers, params) + + return QgsNetworkAccessManager.instance().sendCustomRequest( + network_request, b"OPTIONS", None + ) + def datasets_async(self, page=1, query: Optional[DataBrowserQuery] = None, diff --git a/koordinates/gui/koordinates.py b/koordinates/gui/koordinates.py index 67ce853..4e15625 100644 --- a/koordinates/gui/koordinates.py +++ b/koordinates/gui/koordinates.py @@ -2,7 +2,11 @@ import locale import os from functools import partial -from typing import Optional, List +from typing import ( + Dict, + Optional, + List +) from qgis.PyQt import sip from qgis.PyQt import uic @@ -646,6 +650,10 @@ def __init__(self, parent): self.context_tab_container.setFixedWidth(self.context_container.width()) KoordinatesClient.instance().loginChanged.connect(self._loginChanged) + self._data_options: Optional[Dict] = None + KoordinatesClient.instance().data_options_retrieved.connect( + self._data_options_retrieved + ) self.setMinimumWidth(430) @@ -971,7 +979,7 @@ def _loginChanged(self, logged_in: bool): user = KoordinatesClient.instance().user_details() self.user_country_action.set_country_code(user['country']) - self._build_sort_menu(user) + self._build_sort_menu() self._create_context_tabs(user.get('contexts', [])) @@ -981,14 +989,30 @@ def _loginChanged(self, logged_in: bool): else: self.stackedWidget.setCurrentWidget(self.pageAuth) - def _build_sort_menu(self, user_details: dict): + def _data_options_retrieved(self, options: dict): + """ + Called when the data options request is finished + """ + self._data_options = options + self._build_sort_menu() + + def _build_sort_menu(self): """ Builds the dataset sorting options menu. This can only be done after a login event """ + user_details = KoordinatesClient.instance().user_details() + if not user_details or not self._data_options: + return + self.sort_menu.clear() + country_choices = (self._data_options + .get('filters', {}) + .get('country', {}) + .get('choices', [])) + user_country_code = user_details['country'] sort_by_action = CustomLabelWidgetAction( @@ -1001,14 +1025,19 @@ def _build_sort_menu(self, user_details: dict): checkable=True, parent=self.sort_menu) self.sort_menu.addAction(self.sort_by_popular_action) - for country, sub_text, code in ( - (self.tr('For New Zealand'), None, 'NZ'), - (self.tr('For Australia'), None, 'AU'), - (self.tr('For United Kingdom'), None, 'GB'), - (self.tr('For United States'), None, 'US'), - (self.tr('Anywhere'), self.tr("Don't bias results by location"), '')): - if user_country_code == code: + for country_choice in country_choices: + country = country_choice['display_name'] + code = country_choice['value'] + + if code == 'global': + code = '' + + if not code: + sub_text = self.tr("Don't bias results by location") + elif user_country_code == code: sub_text = "Your Koordinates ID country" + else: + sub_text = None if code: icon = EmojiToIconRenderer.render_flag_to_icon(code) From 3e45d55c405f61c4d5d8e454cfec208be1c32ae0 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Dec 2023 08:21:40 +1000 Subject: [PATCH 2/6] Remove redundant sort by text from some buttons --- koordinates/api/enums.py | 13 ++++++++++++- koordinates/gui/koordinates.py | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/koordinates/api/enums.py b/koordinates/api/enums.py index ab7f028..b335e00 100644 --- a/koordinates/api/enums.py +++ b/koordinates/api/enums.py @@ -186,7 +186,7 @@ class SortOrder(Enum): Oldest = 6 @staticmethod - def to_text(order: 'SortOrder'): + def to_text(order: 'SortOrder') -> str: """ Converts sort order to user-friendly text """ @@ -197,6 +197,17 @@ def to_text(order: 'SortOrder'): SortOrder.AlphabeticalZA: 'Alphabetical (Z-A)', SortOrder.Oldest: 'Oldest'}[order] + def to_button_text(self) -> str: + """ + Converts sort order to user-friendly text for buttons + """ + return {SortOrder.Popularity: 'Popular', + SortOrder.RecentlyAdded: 'Recently Added', + SortOrder.RecentlyUpdated: 'Recently Updated', + SortOrder.AlphabeticalAZ: 'Sort by Alphabetical (A-Z)', + SortOrder.AlphabeticalZA: 'Sort by Alphabetical (Z-A)', + SortOrder.Oldest: 'Oldest'}[self] + class PublisherType(Enum): """ diff --git a/koordinates/gui/koordinates.py b/koordinates/gui/koordinates.py index 4e15625..f340144 100644 --- a/koordinates/gui/koordinates.py +++ b/koordinates/gui/koordinates.py @@ -739,7 +739,7 @@ def _set_sort_order_button_text(self): ) else: self.button_sort_order.setText( - 'Sort by {}'.format(SortOrder.to_text(self.filter_widget.sort_order)) + self.filter_widget.sort_order.to_button_text() ) def _show_context_switcher_menu(self): From 199d04c4803146ab0d14a3660036d7a06f4689d6 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Dec 2023 08:27:18 +1000 Subject: [PATCH 3/6] Ensure menu state is always correct --- koordinates/gui/koordinates.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/koordinates/gui/koordinates.py b/koordinates/gui/koordinates.py index f340144..3c0ad25 100644 --- a/koordinates/gui/koordinates.py +++ b/koordinates/gui/koordinates.py @@ -688,9 +688,12 @@ def _sort_order_menu_about_to_show(self): if action == self.sort_by_popular_action: action.set_widget_checked( isinstance(self.filter_widget.sort_order, str) + or self.filter_widget.sort_order == SortOrder.Popularity ) else: - is_checked = action.data() == self.filter_widget.sort_order + is_checked = (action.data() == self.filter_widget.sort_order or + (self.filter_widget.sort_order == SortOrder.Popularity and + not action.data())) if isinstance(action, CustomLabelWidgetAction): action.set_widget_checked(is_checked) From c20191a9fdccb6df45b6d6fa327759954a6be8db Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Dec 2023 08:33:59 +1000 Subject: [PATCH 4/6] Tweak sort button appearance, add margin --- koordinates/gui/koordinates.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/koordinates/gui/koordinates.py b/koordinates/gui/koordinates.py index 3c0ad25..7c1c75c 100644 --- a/koordinates/gui/koordinates.py +++ b/koordinates/gui/koordinates.py @@ -593,10 +593,11 @@ def __init__(self, parent): # requires Qt 5.14+ device_pixel_ratio = 1 - if os.name == 'nt' or device_pixel_ratio > 1: - self.button_sort_order.setStyleSheet( - 'QToolButton { padding-right: 30px; padding-left: 0px; }' - ) + self.button_sort_order.setStyleSheet( + """QToolButton::menu-indicator { image: none } + QToolButton { margin-top: 2px; } + """ + ) self.sort_menu = QMenu(self.button_sort_order) self.sort_by_popular_action: Optional[QAction] = None self._sort_menu_event_filter = WidgetActionMenuHoverEventFilter(self.sort_menu) From 48d792573a9dbdbf5755462c4047f85942149a0f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Dec 2023 09:05:27 +1000 Subject: [PATCH 5/6] Fix initial visibility of sort button --- koordinates/gui/koordinates.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/koordinates/gui/koordinates.py b/koordinates/gui/koordinates.py index 7c1c75c..33a4dec 100644 --- a/koordinates/gui/koordinates.py +++ b/koordinates/gui/koordinates.py @@ -988,8 +988,6 @@ def _loginChanged(self, logged_in: bool): self._create_context_tabs(user.get('contexts', [])) self.filter_widget.set_logged_in(True) - - self.explore() else: self.stackedWidget.setCurrentWidget(self.pageAuth) @@ -1073,7 +1071,8 @@ def _build_sort_menu(self): sort_by_action.selected.connect(partial(self._set_sort_order, order)) sort_by_action.setData(order) - self._set_popular_sort_order('') + self.filter_widget.sort_order = '' + self._set_sort_order_button_text() def _create_context_tabs(self, contexts: List): """ From ab48d0743383d3584815f9e35c6701ec332da1a9 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 1 Dec 2023 09:07:08 +1000 Subject: [PATCH 6/6] Remove dead code --- koordinates/gui/koordinates.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/koordinates/gui/koordinates.py b/koordinates/gui/koordinates.py index 33a4dec..263904b 100644 --- a/koordinates/gui/koordinates.py +++ b/koordinates/gui/koordinates.py @@ -587,12 +587,6 @@ def __init__(self, parent): self.results_panel.publisher_cleared.connect( self.filter_widget.remove_publisher_filter) - try: - device_pixel_ratio = self.window().screen().devicePixelRatio() - except AttributeError: - # requires Qt 5.14+ - device_pixel_ratio = 1 - self.button_sort_order.setStyleSheet( """QToolButton::menu-indicator { image: none } QToolButton { margin-top: 2px; }