From a643f0c8bb0a784d4efba21d99d8b29d08ff1c61 Mon Sep 17 00:00:00 2001 From: neo Date: Thu, 7 Nov 2024 13:13:48 +0100 Subject: [PATCH 1/5] ADD: Optional choice to add a limit to requests --- lizmap_server/expression_service.py | 11 ++++++ test/test_expression_service_virtualfields.py | 36 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/lizmap_server/expression_service.py b/lizmap_server/expression_service.py index ed84b5c3..2c806238 100755 --- a/lizmap_server/expression_service.py +++ b/lizmap_server/expression_service.py @@ -782,6 +782,7 @@ def virtualFields(params: Dict[str, str], response: QgsServerResponse, project: FILTER=An expression to filter layer FIELDS=list of requested field separated by comma WITH_GEOMETRY=False + LIMIT=number of features to return or nothing to return all """ logger = Logger() layer_name = params.get('LAYER', '') @@ -865,6 +866,16 @@ def virtualFields(params: Dict[str, str], response: QgsServerResponse, project: req = QgsFeatureRequest() + # set limit + req_limit = params.get('LIMIT', '-1') + try: + req.setLimit(int(req_limit)) + except ValueError: + raise ExpressionServiceError( + "Bad request error", + f"Invalid LIMIT for 'VirtualFields': \"{req_limit}\"", + 400) + # get filter req_filter = params.get('FILTER', '') if req_filter: diff --git a/test/test_expression_service_virtualfields.py b/test/test_expression_service_virtualfields.py index 8581b8d5..3ca2bf3f 100644 --- a/test/test_expression_service_virtualfields.py +++ b/test/test_expression_service_virtualfields.py @@ -204,3 +204,39 @@ def test_request_with_filter_fields_geometry(client): assert b['features'][0]['properties']['a'] == 1 assert 'b' in b['features'][0]['properties'] assert b['features'][0]['properties']['b'] == 2 + +def test_request_limit(client): + """ Test Expression VirtualFields request + """ + projectfile = "france_parts.qgs" + + # Make a request + qs = "?SERVICE=EXPRESSION&REQUEST=VirtualFields&MAP=france_parts.qgs&LAYER=france_parts" + qs += "&VIRTUALS={\"a\":\"%s\", \"b\":\"%s\"}" % ( + quote('1', safe=''), quote('1 + 1', safe='')) + qs += "&LIMIT=2" + rv = client.get(qs, projectfile) + assert rv.status_code == 200 + assert rv.headers.get('Content-Type', '').find('application/json') == 0 + + b = json.loads(rv.content.decode('utf-8')) + assert 'type' in b + assert b['type'] == 'FeatureCollection' + + assert 'features' in b + assert len(b['features']) == 2 + + assert 'type' in b['features'][0] + assert b['features'][0]['type'] == 'Feature' + + assert 'geometry' in b['features'][0] + assert b['features'][0]['geometry'] is None + + assert 'properties' in b['features'][0] + assert 'NAME_1' in b['features'][0]['properties'] + assert 'Region' in b['features'][0]['properties'] + + assert 'a' in b['features'][0]['properties'] + assert b['features'][0]['properties']['a'] == 1 + assert 'b' in b['features'][0]['properties'] + assert b['features'][0]['properties']['b'] == 2 From deaa0eca21cd983b56093dc0c32d2198af5dd5ce Mon Sep 17 00:00:00 2001 From: neo Date: Thu, 7 Nov 2024 13:30:01 +0100 Subject: [PATCH 2/5] ADD: Optional choice to set the order of requests --- lizmap_server/expression_service.py | 18 ++++++++ test/test_expression_service_virtualfields.py | 41 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/lizmap_server/expression_service.py b/lizmap_server/expression_service.py index 2c806238..67a90d49 100755 --- a/lizmap_server/expression_service.py +++ b/lizmap_server/expression_service.py @@ -783,6 +783,8 @@ def virtualFields(params: Dict[str, str], response: QgsServerResponse, project: FIELDS=list of requested field separated by comma WITH_GEOMETRY=False LIMIT=number of features to return or nothing to return all + SORTING_ORDER=asc or desc, default = asc + SORTING_FIELD=field name to sort by """ logger = Logger() layer_name = params.get('LAYER', '') @@ -876,6 +878,22 @@ def virtualFields(params: Dict[str, str], response: QgsServerResponse, project: f"Invalid LIMIT for 'VirtualFields': \"{req_limit}\"", 400) + # set orderby + req_sorting_order = params.get('SORTING_ORDER', '') + + if req_sorting_order in ['asc', 'desc']: + req_sorting_order = req_sorting_order == 'asc' + elif req_sorting_order != '' : + raise ExpressionServiceError( + "Bad request error", + f"Invalid SORTING_ORDER for 'VirtualFields': \"{req_sorting_order}\"", + 400) + + req_sorting_field = params.get('SORTING_FIELD', '') + + if type(req_sorting_order) is bool : + req.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause(req_sorting_field, req_sorting_order)])) + # get filter req_filter = params.get('FILTER', '') if req_filter: diff --git a/test/test_expression_service_virtualfields.py b/test/test_expression_service_virtualfields.py index 3ca2bf3f..a4d6f483 100644 --- a/test/test_expression_service_virtualfields.py +++ b/test/test_expression_service_virtualfields.py @@ -240,3 +240,44 @@ def test_request_limit(client): assert b['features'][0]['properties']['a'] == 1 assert 'b' in b['features'][0]['properties'] assert b['features'][0]['properties']['b'] == 2 + + +def test_request_order(client): + """ Test Expression VirtualFields request + """ + projectfile = "france_parts.qgs" + + # Make a request + qs = "?SERVICE=EXPRESSION&REQUEST=VirtualFields&MAP=france_parts.qgs&LAYER=france_parts" + qs += "&VIRTUALS={\"a\":\"%s\", \"b\":\"%s\"}" % ( + quote('1', safe=''), quote('1 + 1', safe='')) + qs += "&SORTING_ORDER=desc" + qs += "&SORTING_FIELD=NAME_1" + rv = client.get(qs, projectfile) + assert rv.status_code == 200 + assert rv.headers.get('Content-Type', '').find('application/json') == 0 + + b = json.loads(rv.content.decode('utf-8')) + assert 'type' in b + assert b['type'] == 'FeatureCollection' + + assert 'features' in b + assert len(b['features']) == 4 + + assert 'type' in b['features'][0] + assert b['features'][0]['type'] == 'Feature' + + assert 'geometry' in b['features'][0] + assert b['features'][0]['geometry'] is None + + assert 'properties' in b['features'][0] + assert 'NAME_1' in b['features'][0]['properties'] + assert 'Region' in b['features'][0]['properties'] + + assert 'a' in b['features'][0]['properties'] + assert b['features'][0]['properties']['a'] == 1 + assert 'b' in b['features'][0]['properties'] + assert b['features'][0]['properties']['b'] == 2 + + assert b['features'][0]['id'] == 'france_parts.2' + assert b['features'][3]['id'] == 'france_parts.0' From de890bd85ae5b05524f1b3218b53bd15a32427c7 Mon Sep 17 00:00:00 2001 From: neo Date: Thu, 7 Nov 2024 15:09:18 +0100 Subject: [PATCH 3/5] FIX: syntax --- lizmap_server/expression_service.py | 3 +- test/test_expression_service_virtualfields.py | 43 +++++++++++-------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/lizmap_server/expression_service.py b/lizmap_server/expression_service.py index 67a90d49..ad6a1b42 100755 --- a/lizmap_server/expression_service.py +++ b/lizmap_server/expression_service.py @@ -892,7 +892,8 @@ def virtualFields(params: Dict[str, str], response: QgsServerResponse, project: req_sorting_field = params.get('SORTING_FIELD', '') if type(req_sorting_order) is bool : - req.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause(req_sorting_field, req_sorting_order)])) + order_by_clause = QgsFeatureRequest.OrderByClause(req_sorting_field, req_sorting_order) + req.setOrderBy(QgsFeatureRequest.OrderBy([order_by_clause])) # get filter req_filter = params.get('FILTER', '') diff --git a/test/test_expression_service_virtualfields.py b/test/test_expression_service_virtualfields.py index a4d6f483..b1605683 100644 --- a/test/test_expression_service_virtualfields.py +++ b/test/test_expression_service_virtualfields.py @@ -1,6 +1,7 @@ import json from urllib.parse import quote +from test.utils import _build_query_string, _check_request __copyright__ = 'Copyright 2019, 3Liz' __license__ = 'GPL version 3' @@ -211,15 +212,19 @@ def test_request_limit(client): projectfile = "france_parts.qgs" # Make a request - qs = "?SERVICE=EXPRESSION&REQUEST=VirtualFields&MAP=france_parts.qgs&LAYER=france_parts" - qs += "&VIRTUALS={\"a\":\"%s\", \"b\":\"%s\"}" % ( - quote('1', safe=''), quote('1 + 1', safe='')) - qs += "&LIMIT=2" - rv = client.get(qs, projectfile) - assert rv.status_code == 200 - assert rv.headers.get('Content-Type', '').find('application/json') == 0 + qs = { + "SERVICE": "EXPRESSION", + "REQUEST": "VirtualFields", + "MAP": "france_parts.qgs", + "LAYER": "france_parts", + "VIRTUALS": "{\"a\":\"%s\", \"b\":\"%s\"}" % ( + quote('1', safe=''), quote('1 + 1', safe='')), + "LIMIT": "2", + } + + rv = client.get(_build_query_string(qs), projectfile) + b = _check_request(rv, http_code=200) - b = json.loads(rv.content.decode('utf-8')) assert 'type' in b assert b['type'] == 'FeatureCollection' @@ -248,16 +253,20 @@ def test_request_order(client): projectfile = "france_parts.qgs" # Make a request - qs = "?SERVICE=EXPRESSION&REQUEST=VirtualFields&MAP=france_parts.qgs&LAYER=france_parts" - qs += "&VIRTUALS={\"a\":\"%s\", \"b\":\"%s\"}" % ( - quote('1', safe=''), quote('1 + 1', safe='')) - qs += "&SORTING_ORDER=desc" - qs += "&SORTING_FIELD=NAME_1" - rv = client.get(qs, projectfile) - assert rv.status_code == 200 - assert rv.headers.get('Content-Type', '').find('application/json') == 0 + qs = { + "SERVICE": "EXPRESSION", + "REQUEST": "VirtualFields", + "MAP": "france_parts.qgs", + "LAYER": "france_parts", + "VIRTUALS": "{\"a\":\"%s\", \"b\":\"%s\"}" % ( + quote('1', safe=''), quote('1 + 1', safe='')), + "SORTING_ORDER": "desc", + "SORTING_FIELD": "NAME_1", + } + + rv = client.get(_build_query_string(qs), projectfile) + b = _check_request(rv, http_code=200) - b = json.loads(rv.content.decode('utf-8')) assert 'type' in b assert b['type'] == 'FeatureCollection' From 8ba3bc0bfa1a2580b0d53c6b6b37e887583a7c13 Mon Sep 17 00:00:00 2001 From: neo Date: Thu, 7 Nov 2024 16:48:17 +0100 Subject: [PATCH 4/5] FIX : code clarity --- lizmap_server/expression_service.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lizmap_server/expression_service.py b/lizmap_server/expression_service.py index ad6a1b42..5c086fa1 100755 --- a/lizmap_server/expression_service.py +++ b/lizmap_server/expression_service.py @@ -879,14 +879,17 @@ def virtualFields(params: Dict[str, str], response: QgsServerResponse, project: 400) # set orderby - req_sorting_order = params.get('SORTING_ORDER', '') - - if req_sorting_order in ['asc', 'desc']: - req_sorting_order = req_sorting_order == 'asc' - elif req_sorting_order != '' : + req_sorting_order_param = params.get('SORTING_ORDER', '') + # Initialize to a default value + req_sorting_order = '' + + if req_sorting_order_param in ['asc', 'desc']: + # QGIS expects a boolean to know how to sort + req_sorting_order = (req_sorting_order_param == 'asc') + elif req_sorting_order_param != '' : raise ExpressionServiceError( "Bad request error", - f"Invalid SORTING_ORDER for 'VirtualFields': \"{req_sorting_order}\"", + f"Invalid SORTING_ORDER for 'VirtualFields': \"{req_sorting_order_param}\"", 400) req_sorting_field = params.get('SORTING_FIELD', '') From b47ceadfc578f41dbcc8e79841019a9ecefb0933 Mon Sep 17 00:00:00 2001 From: neo Date: Fri, 8 Nov 2024 10:09:21 +0100 Subject: [PATCH 5/5] FIX : code logic --- lizmap_server/expression_service.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lizmap_server/expression_service.py b/lizmap_server/expression_service.py index 5c086fa1..db4d4194 100755 --- a/lizmap_server/expression_service.py +++ b/lizmap_server/expression_service.py @@ -880,24 +880,18 @@ def virtualFields(params: Dict[str, str], response: QgsServerResponse, project: # set orderby req_sorting_order_param = params.get('SORTING_ORDER', '') - # Initialize to a default value - req_sorting_order = '' - if req_sorting_order_param in ['asc', 'desc']: + if req_sorting_order_param in ('asc', 'desc'): # QGIS expects a boolean to know how to sort - req_sorting_order = (req_sorting_order_param == 'asc') + req_sorting_field = params.get('SORTING_FIELD', '') + order_by_clause = QgsFeatureRequest.OrderByClause(req_sorting_field, req_sorting_order_param == 'asc') + req.setOrderBy(QgsFeatureRequest.OrderBy([order_by_clause])) elif req_sorting_order_param != '' : raise ExpressionServiceError( "Bad request error", f"Invalid SORTING_ORDER for 'VirtualFields': \"{req_sorting_order_param}\"", 400) - req_sorting_field = params.get('SORTING_FIELD', '') - - if type(req_sorting_order) is bool : - order_by_clause = QgsFeatureRequest.OrderByClause(req_sorting_field, req_sorting_order) - req.setOrderBy(QgsFeatureRequest.OrderBy([order_by_clause])) - # get filter req_filter = params.get('FILTER', '') if req_filter: