From a6f0339d5bad13594de2ebedc82b612f9142b3f8 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 30 Dec 2021 13:09:17 +0100 Subject: [PATCH 1/5] Added logical operator param --- QuickOSM/core/query_factory.py | 38 ++++++++++++++++++- .../advanced/build_query.py | 1 + QuickOSM/quick_osm_processing/build_input.py | 14 ++++++- .../quick_osm_processing/quickosm_process.py | 4 ++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/QuickOSM/core/query_factory.py b/QuickOSM/core/query_factory.py index ca5494fb..8111177d 100644 --- a/QuickOSM/core/query_factory.py +++ b/QuickOSM/core/query_factory.py @@ -3,7 +3,7 @@ import logging import re -from typing import List +from typing import List, Union from xml.dom.minidom import parseString from QuickOSM.core.exceptions import QueryFactoryException @@ -98,6 +98,8 @@ def __init__( """ if isinstance(type_multi_request, list): self._type_multi_request = [x for x in type_multi_request if x] + elif isinstance(type_multi_request, str): + self._type_multi_request = self._convert_to_multitypes(type_multi_request) else: self._type_multi_request = [] @@ -154,6 +156,30 @@ def area(self) -> list: """ return self._area + @staticmethod + def _convert_to_multitypes(type_multi_request: str) -> List[MultiType]: + """Converts a string of comma separated 'AND'/'OR's to a list of MultiType equivalents. + + :param type_multi_request: A string of comma separated 'AND'/'OR's provided from input of processing algorithms + :type type_multi_request: str + + :return: list of MultiType + :rtype: list + + :raise QueryFactoryException: + """ + types = [] + if type_multi_request: + for type_ in type_multi_request.split(','): + type_ = type_.strip() + if type_ == 'AND': + types.append(MultiType.AND) + elif type_ == 'OR': + types.append(MultiType.OR) + else: + raise QueryFactoryException(tr('Only values "AND"/"OR" are allowed as logical operators.')) + return types + def _check_parameters(self) -> bool: """Internal function to check that the query can be built. @@ -214,6 +240,16 @@ def _check_parameters(self) -> bool: raise QueryFactoryException( tr(f'Key "{key}" contains leading or trailing whitespace.')) + if len(self._key) != 0: + if len(self._type_multi_request) > len(self._key) - 1: + raise QueryFactoryException( + tr('Too many logical operators were provided.') + ) + elif len(self._type_multi_request) < len(self._key) - 1: + raise QueryFactoryException( + tr('Not enough logical operators were provided.') + ) + self._checked = True return True diff --git a/QuickOSM/quick_osm_processing/advanced/build_query.py b/QuickOSM/quick_osm_processing/advanced/build_query.py index 05258f79..1478f518 100644 --- a/QuickOSM/quick_osm_processing/advanced/build_query.py +++ b/QuickOSM/quick_osm_processing/advanced/build_query.py @@ -68,6 +68,7 @@ def initAlgorithm(self, config=None): def build_query(self) -> Dict[str, str]: """Build the query requested.""" query_factory = QueryFactory( + type_multi_request=self.type_multi_request, query_type=self.QUERY_TYPE, key=self.key, value=self.value, diff --git a/QuickOSM/quick_osm_processing/build_input.py b/QuickOSM/quick_osm_processing/build_input.py index de96205a..2c572ab8 100644 --- a/QuickOSM/quick_osm_processing/build_input.py +++ b/QuickOSM/quick_osm_processing/build_input.py @@ -15,7 +15,7 @@ ) from QuickOSM.core.utilities.tools import get_setting -from QuickOSM.definitions.osm import QueryType +from QuickOSM.definitions.osm import QueryType, MultiType from QuickOSM.definitions.overpass import OVERPASS_SERVERS from QuickOSM.qgis_plugin_tools.tools.i18n import tr @@ -141,11 +141,13 @@ class BuildBasedQuery(BuildBased): KEY = 'KEY' VALUE = 'VALUE' + TYPE_MULTI_REQUEST = 'TYPE_MULTI_REQUEST' def __init__(self): super().__init__() self.key = None self.value = None + self.type_multi_request = None self.distance = None def fetch_based_parameters(self, parameters, context): @@ -153,6 +155,7 @@ def fetch_based_parameters(self, parameters, context): super().fetch_based_parameters(parameters, context) self.key = self.parameterAsString(parameters, self.KEY, context) self.value = self.parameterAsString(parameters, self.VALUE, context) + self.type_multi_request = self.parameterAsString(parameters, self.TYPE_MULTI_REQUEST, context) def add_top_parameters(self): """Set up the parameters.""" @@ -180,6 +183,15 @@ def add_top_parameters(self): param.setHelp(help_string) self.addParameter(param) + param = QgsProcessingParameterString( + self.TYPE_MULTI_REQUEST, tr('Operator types to combine multiple keys and values with'), optional=True + ) + help_string = tr( + 'The logical operators used to combine keys and values, if there are multiple.' # TODO: expand help string + ) + param.setHelp(help_string) + self.addParameter(param) + class BuildBasedNotSpatialQuery(BuildBasedQuery): """Set up the parameters for a not spatial query.""" diff --git a/QuickOSM/quick_osm_processing/quickosm_process.py b/QuickOSM/quick_osm_processing/quickosm_process.py index b190a3e2..ff40e110 100644 --- a/QuickOSM/quick_osm_processing/quickosm_process.py +++ b/QuickOSM/quick_osm_processing/quickosm_process.py @@ -296,6 +296,7 @@ def processAlgorithm(self, parameters, context, feedback): 'KEY': self.key, 'SERVER': self.server, 'TIMEOUT': self.timeout, + 'TYPE_MULTI_REQUEST': self.type_multi_request, 'VALUE': self.value }, feedback=self.feedback @@ -342,6 +343,7 @@ def processAlgorithm(self, parameters, context, feedback): 'KEY': self.key, 'SERVER': self.server, 'TIMEOUT': self.timeout, + 'TYPE_MULTI_REQUEST': self.type_multi_request, 'VALUE': self.value }, feedback=self.feedback @@ -389,6 +391,7 @@ def processAlgorithm(self, parameters, context, feedback): 'KEY': self.key, 'SERVER': self.server, 'TIMEOUT': self.timeout, + 'TYPE_MULTI_REQUEST': self.type_multi_request, 'VALUE': self.value }, feedback=self.feedback @@ -435,6 +438,7 @@ def processAlgorithm(self, parameters, context, feedback): 'KEY': self.key, 'SERVER': self.server, 'TIMEOUT': self.timeout, + 'TYPE_MULTI_REQUEST': self.type_multi_request, 'VALUE': self.value }, feedback=self.feedback From c78e63314fcc9be2911811a22aeb10e435d4b7f1 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 30 Dec 2021 13:29:31 +0100 Subject: [PATCH 2/5] fixed misplaced argument in test_impossible_queries --- QuickOSM/test/test_query_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QuickOSM/test/test_query_factory.py b/QuickOSM/test/test_query_factory.py index 74efa9e7..7d2b02a7 100644 --- a/QuickOSM/test/test_query_factory.py +++ b/QuickOSM/test/test_query_factory.py @@ -27,7 +27,7 @@ def test_impossible_queries(self): """Test queries which are not possible and must raise an exception.""" # Query type # noinspection PyTypeChecker - query = QueryFactory('fake_query_type', area='foo') + query = QueryFactory(query_type='fake_query_type', area='foo') msg = 'Wrong query type.' with self.assertRaisesRegex(QueryFactoryException, msg): query._check_parameters() From 03b7dfaa7804f4fb77de9a0a7038a2216dbd6d9c Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 30 Dec 2021 13:58:26 +0100 Subject: [PATCH 3/5] added small test for logical operators in extent_query --- QuickOSM/test/test_processing.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/QuickOSM/test/test_processing.py b/QuickOSM/test/test_processing.py index dd82d7fa..a7a69e8a 100644 --- a/QuickOSM/test/test_processing.py +++ b/QuickOSM/test/test_processing.py @@ -143,6 +143,35 @@ def test_build_in_extent_query(self): self.assertEqual(result_expected, result) + def test_build_in_extent_query_with_multitypes(self): + """Test for the build of an in extent algorithm that uses the multitypes parameter.""" + result = processing.run( + 'quickosm:buildqueryextent', + { + 'EXTENT': '8.71679,8.79689,51.70687,51.72602 [EPSG:4326]', + 'KEY': 'highway,footway', + 'SERVER': 'https://lz4.overpass-api.de/api/interpreter', + 'TIMEOUT': 25, + 'TYPE_MULTI_REQUEST': 'AND', + 'VALUE': 'footway,sidewalk' + } + ) + result_expected = { + 'OUTPUT_OQL_QUERY': + '[out:xml] [timeout:25];\n(\n node["highway"="footway"]["footway"="sidewalk"]' + '( 51.70687,8.71679,51.72602,8.79689);\n way["highway"="footway"]["footway"="sidewalk"]' + '( 51.70687,8.71679,51.72602,8.79689);\n relation["highway"="footway"]' + '["footway"="sidewalk"]( 51.70687,8.71679,51.72602,8.79689);\n);\n(._;>;);\nout body;', + 'OUTPUT_URL': + 'https://lz4.overpass-api.de/api/interpreter?data=[out:xml] [timeout:25];%0A(%0A' + ' node[%22highway%22%3D%22footway%22][%22footway%22%3D%22sidewalk%22]' + '( 51.70687,8.71679,51.72602,8.79689);%0A way[%22highway%22%3D%22footway%22]' + '[%22footway%22%3D%22sidewalk%22]( 51.70687,8.71679,51.72602,8.79689);%0A ' + 'relation[%22highway%22%3D%22footway%22][%22footway%22%3D%22sidewalk%22]' + '( 51.70687,8.71679,51.72602,8.79689);%0A);%0A(._;%3E;);%0Aout body;&info=QgisQuickOSMPlugin' + } + self.assertEqual(result_expected, result) + def test_build_raw_query(self): """Test for the build of a raw query algorithm.""" result = processing.run( From 0745837358f0288ccbe785d02a3c4ad7bbb4354a Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 30 Dec 2021 14:03:00 +0100 Subject: [PATCH 4/5] removed unused import --- QuickOSM/core/query_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QuickOSM/core/query_factory.py b/QuickOSM/core/query_factory.py index 8111177d..60d1d3ec 100644 --- a/QuickOSM/core/query_factory.py +++ b/QuickOSM/core/query_factory.py @@ -3,7 +3,7 @@ import logging import re -from typing import List, Union +from typing import List from xml.dom.minidom import parseString from QuickOSM.core.exceptions import QueryFactoryException From 2878a6c41310940dad86c76062a45376db7328b0 Mon Sep 17 00:00:00 2001 From: Etienne Trimaille Date: Tue, 1 Feb 2022 20:03:39 +0100 Subject: [PATCH 5/5] Fix lint issues about the multikey in processing --- QuickOSM/core/query_factory.py | 9 ++++++--- QuickOSM/quick_osm_processing/build_input.py | 10 +++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/QuickOSM/core/query_factory.py b/QuickOSM/core/query_factory.py index 60d1d3ec..f97fe279 100644 --- a/QuickOSM/core/query_factory.py +++ b/QuickOSM/core/query_factory.py @@ -160,7 +160,8 @@ def area(self) -> list: def _convert_to_multitypes(type_multi_request: str) -> List[MultiType]: """Converts a string of comma separated 'AND'/'OR's to a list of MultiType equivalents. - :param type_multi_request: A string of comma separated 'AND'/'OR's provided from input of processing algorithms + :param type_multi_request: A string of comma separated 'AND'/'OR's provided from input of processing + algorithms :type type_multi_request: str :return: list of MultiType @@ -177,7 +178,8 @@ def _convert_to_multitypes(type_multi_request: str) -> List[MultiType]: elif type_ == 'OR': types.append(MultiType.OR) else: - raise QueryFactoryException(tr('Only values "AND"/"OR" are allowed as logical operators.')) + raise QueryFactoryException( + tr('Only values "AND"/"OR" are allowed as logical operators.')) return types def _check_parameters(self) -> bool: @@ -245,7 +247,8 @@ def _check_parameters(self) -> bool: raise QueryFactoryException( tr('Too many logical operators were provided.') ) - elif len(self._type_multi_request) < len(self._key) - 1: + + if len(self._type_multi_request) < len(self._key) - 1: raise QueryFactoryException( tr('Not enough logical operators were provided.') ) diff --git a/QuickOSM/quick_osm_processing/build_input.py b/QuickOSM/quick_osm_processing/build_input.py index 2c572ab8..8bb9a014 100644 --- a/QuickOSM/quick_osm_processing/build_input.py +++ b/QuickOSM/quick_osm_processing/build_input.py @@ -15,7 +15,7 @@ ) from QuickOSM.core.utilities.tools import get_setting -from QuickOSM.definitions.osm import QueryType, MultiType +from QuickOSM.definitions.osm import QueryType from QuickOSM.definitions.overpass import OVERPASS_SERVERS from QuickOSM.qgis_plugin_tools.tools.i18n import tr @@ -184,10 +184,14 @@ def add_top_parameters(self): self.addParameter(param) param = QgsProcessingParameterString( - self.TYPE_MULTI_REQUEST, tr('Operator types to combine multiple keys and values with'), optional=True + self.TYPE_MULTI_REQUEST, + tr('Operator types to combine multiple keys and values with'), + optional=True ) + + # TODO: expand help string help_string = tr( - 'The logical operators used to combine keys and values, if there are multiple.' # TODO: expand help string + 'The logical operators used to combine keys and values, if there are multiple.' ) param.setHelp(help_string) self.addParameter(param)