From a1267a411c4519d9f620501811d4ede3644a0594 Mon Sep 17 00:00:00 2001 From: Carlos Date: Tue, 23 Aug 2022 14:29:34 -0500 Subject: [PATCH 01/33] Ant Design Table changes --- pharus/component_interface.py | 75 ++++++++++++++++++++++++----------- pharus/dynamic_api_gen.py | 3 +- 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index 25aed44..8224050 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -12,6 +12,7 @@ import types import io import numpy as np +from uuid import UUID class NumpyEncoder(json.JSONEncoder): @@ -37,11 +38,13 @@ def default(self, o): return self.npmap[type(o)](o) if type(o) in (datetime, date): return o.isoformat() + if type(o) is UUID: + return str(o) return json.JSONEncoder.default(self, o) @classmethod def dumps(cls, obj): - return json.dumps(obj, cls=cls) + return json.dumps(obj, cls=cls).replace("NaN", "null") class QueryComponent: @@ -77,7 +80,7 @@ def __init__(self, name, component_config, static_config, jwt_payload: dict): self.vm_list = [ dj.VirtualModule( s, - s, + s.replace("__DASH__", "-"), connection=dj.conn( host=jwt_payload["databaseAddress"], user=jwt_payload["username"], @@ -117,6 +120,7 @@ def restriction(self): class TableComponent(QueryComponent): attributes_route_format = "{route}/attributes" + universal_set_route_format = "{route}/universalset" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -162,31 +166,42 @@ def __init__(self, *args, **kwargs): # Returns the result of djquery with paging, sorting, filtering def dj_query_route(self): fetch_metadata = self.fetch_metadata + order = [] + if "order" in request.args: + order=request.args["order"].split(",") record_header, table_records, total_count = _DJConnector._fetch_records( - query=fetch_metadata["query"] & self.restriction[0], + query=fetch_metadata["query"] & self.restriction, fetch_args=fetch_metadata["fetch_args"], - **{ - k: ( - int(v) - if k in ("limit", "page") - else ( - v.split(",") - if k == "order" - else json.loads(b64decode(v.encode("utf-8")).decode("utf-8")) - ) - ) - for k, v in request.args.items() - }, + limit=int(request.args["limit"]), + page=int(request.args["page"]), + order=order ) - return dict( - recordHeader=record_header, records=table_records, totalCount=total_count + + # print(f'\n\n\n\n{fetch_metadata["query"] & self.restriction[0]}\n\n\n\n') + + return NumpyEncoder.dumps( + dict( + recordHeader=record_header, + records=table_records, + totalCount=total_count, + ) ) def attributes_route(self): attributes_meta = _DJConnector._get_attributes(self.fetch_metadata["query"]) - return dict( - attributeHeaders=attributes_meta["attribute_headers"], - attributes=attributes_meta["attributes"], + query = self.fetch_metadata["query"] & self.restriction[0] + unique_values = [] + for key in query.heading.attributes.keys(): + result = (dj.U(key) & query).fetch() + unique_values.append( + [dict({"text": str(x[0]), "value": x[0]}) for x in result] + ) + return NumpyEncoder.dumps( + dict( + attributeHeaders=attributes_meta["attribute_headers"], + attributes=attributes_meta["attributes"], + unique_values=unique_values, + ) ) @@ -230,8 +245,12 @@ def dj_query_route(self): query=fetch_metadata["query"] & self.restriction, fetch_args=fetch_metadata["fetch_args"], ) - return dict( - recordHeader=record_header, records=table_records, totalCount=total_count + return NumpyEncoder.dumps( + dict( + recordHeader=record_header, + records=table_records, + totalCount=total_count, + ) ) @@ -287,12 +306,19 @@ def __init__(self, *args, **kwargs): def dj_query_route(self): fetch_metadata = self.fetch_metadata + print(request.args, flush=True) record_header, table_records, total_count = _DJConnector._fetch_records( query=fetch_metadata["query"] & self.restriction, fetch_args=fetch_metadata["fetch_args"], + page=int(request.args["page"]) if "page" in request.args else 1, + limit=int(request.args["limit"]) if "limit" in request.args else 1000, ) - return dict( - recordHeader=record_header, records=table_records, totalCount=total_count + return NumpyEncoder.dumps( + dict( + recordHeader=record_header, + records=table_records, + totalCount=total_count, + ) ) @@ -322,6 +348,7 @@ def dj_query_route(self): "basicquery": BasicQuery, "plot:plotly:stored_json": PlotPlotlyStoredjsonComponent, "table": TableComponent, + "djtable": TableComponent, "metadata": MetadataComponent, "file:image:attach": FileImageAttachComponent, "slider": BasicQuery, diff --git a/pharus/dynamic_api_gen.py b/pharus/dynamic_api_gen.py index a5db351..ab9566b 100644 --- a/pharus/dynamic_api_gen.py +++ b/pharus/dynamic_api_gen.py @@ -107,14 +107,13 @@ def {method_name}() -> dict: method_name_type="dj_query_route", ) ) - for comp_name, comp in ( grid["component_templates"] if "component_templates" in grid else grid["components"] ).items(): if re.match( - r"^(table|metadata|plot|file|slider|dropdown-query).*$", + r"^(table|djtable|metadata|plot|file|slider|dropdown-query).*$", comp["type"], ): f.write( From 7b62141a9bc7520ff4c66829ffbf6df7ba8661b4 Mon Sep 17 00:00:00 2001 From: Carlos Date: Tue, 23 Aug 2022 15:27:31 -0500 Subject: [PATCH 02/33] black formatting needs work --- pharus/component_interface.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index 8224050..ff03195 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -175,10 +175,18 @@ def dj_query_route(self): limit=int(request.args["limit"]), page=int(request.args["page"]), order=order + **({ + "order": request.args["order"].split(",") + } if "order" in request.args else {}, + { + "limit": request.args["limit"].split(",") + } if "limit" in request.args else {}, + { + "page": request.args["page"].split(",") + } if "page" in request.args else {} + ) ) - # print(f'\n\n\n\n{fetch_metadata["query"] & self.restriction[0]}\n\n\n\n') - return NumpyEncoder.dumps( dict( recordHeader=record_header, From 4d51b1d4f2727b4f2a2ffcb78243e021f4e776a5 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Wed, 24 Aug 2022 11:29:38 -0500 Subject: [PATCH 03/33] fix bug --- pharus/component_interface.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index ff03195..477ee6b 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -166,25 +166,12 @@ def __init__(self, *args, **kwargs): # Returns the result of djquery with paging, sorting, filtering def dj_query_route(self): fetch_metadata = self.fetch_metadata - order = [] - if "order" in request.args: - order=request.args["order"].split(",") record_header, table_records, total_count = _DJConnector._fetch_records( query=fetch_metadata["query"] & self.restriction, fetch_args=fetch_metadata["fetch_args"], - limit=int(request.args["limit"]), - page=int(request.args["page"]), - order=order - **({ - "order": request.args["order"].split(",") - } if "order" in request.args else {}, - { - "limit": request.args["limit"].split(",") - } if "limit" in request.args else {}, - { - "page": request.args["page"].split(",") - } if "page" in request.args else {} - ) + limit=int(request.args["limit"]) or None, + page=int(request.args["page"]) or 1, + order=request.args["order"].split(",") or [], ) return NumpyEncoder.dumps( From 23714ad61b580f81cbba58e30a9cb94193e34dec Mon Sep 17 00:00:00 2001 From: jverswijver Date: Mon, 29 Aug 2022 08:54:06 -0500 Subject: [PATCH 04/33] fix tests, apply suggestions from code review. --- docker-compose-build.yaml | 2 +- pharus/component_interface.py | 19 ++++++++++--------- pharus/dynamic_api_gen.py | 2 +- tests/test_api_gen.py | 19 ++++++++++++++++++- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/docker-compose-build.yaml b/docker-compose-build.yaml index 7f76c2f..07d7b32 100644 --- a/docker-compose-build.yaml +++ b/docker-compose-build.yaml @@ -11,7 +11,7 @@ services: - PY_VER - DISTRO - IMAGE - image: datajoint/pharus:${PHARUS_VERSION} + image: jverswijver/pharus:0.4.2-prerelease environment: - PHARUS_PRIVATE_KEY - PHARUS_PUBLIC_KEY diff --git a/pharus/component_interface.py b/pharus/component_interface.py index 477ee6b..9dbd9d1 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -36,15 +36,17 @@ class NumpyEncoder(json.JSONEncoder): def default(self, o): if type(o) in self.npmap: return self.npmap[type(o)](o) - if type(o) in (datetime, date): - return o.isoformat() if type(o) is UUID: return str(o) + if type(o) is str and o == "NaN": + return "null" + if type(o) in (datetime, date): + return o.isoformat() return json.JSONEncoder.default(self, o) @classmethod def dumps(cls, obj): - return json.dumps(obj, cls=cls).replace("NaN", "null") + return json.dumps(obj, cls=cls) class QueryComponent: @@ -80,7 +82,7 @@ def __init__(self, name, component_config, static_config, jwt_payload: dict): self.vm_list = [ dj.VirtualModule( s, - s.replace("__DASH__", "-"), + s.replace("__", "-"), connection=dj.conn( host=jwt_payload["databaseAddress"], user=jwt_payload["username"], @@ -169,9 +171,9 @@ def dj_query_route(self): record_header, table_records, total_count = _DJConnector._fetch_records( query=fetch_metadata["query"] & self.restriction, fetch_args=fetch_metadata["fetch_args"], - limit=int(request.args["limit"]) or None, - page=int(request.args["page"]) or 1, - order=request.args["order"].split(",") or [], + limit=int(request.args["limit"]) if "limit" in request.args else 1000, + page=int(request.args["page"]) if "page" in request.args else 1, + order=request.args["order"].split(",") if "order" in request.args else None, ) return NumpyEncoder.dumps( @@ -301,7 +303,6 @@ def __init__(self, *args, **kwargs): def dj_query_route(self): fetch_metadata = self.fetch_metadata - print(request.args, flush=True) record_header, table_records, total_count = _DJConnector._fetch_records( query=fetch_metadata["query"] & self.restriction, fetch_args=fetch_metadata["fetch_args"], @@ -343,7 +344,7 @@ def dj_query_route(self): "basicquery": BasicQuery, "plot:plotly:stored_json": PlotPlotlyStoredjsonComponent, "table": TableComponent, - "djtable": TableComponent, + "antd-table": TableComponent, "metadata": MetadataComponent, "file:image:attach": FileImageAttachComponent, "slider": BasicQuery, diff --git a/pharus/dynamic_api_gen.py b/pharus/dynamic_api_gen.py index ab9566b..96addf7 100644 --- a/pharus/dynamic_api_gen.py +++ b/pharus/dynamic_api_gen.py @@ -113,7 +113,7 @@ def {method_name}() -> dict: else grid["components"] ).items(): if re.match( - r"^(table|djtable|metadata|plot|file|slider|dropdown-query).*$", + r"^(table|antd-table|metadata|plot|file|slider|dropdown-query).*$", comp["type"], ): f.write( diff --git a/tests/test_api_gen.py b/tests/test_api_gen.py index 32d0e7e..be82b2b 100644 --- a/tests/test_api_gen.py +++ b/tests/test_api_gen.py @@ -85,9 +85,26 @@ def test_get_attributes(token, client, schemas_simple): ["b_number", "float", False, None, False], ], }, + "unique_values": [ + [{"text": "0", "value": 0}, {"text": "1", "value": 1}], + [ + {"text": "10", "value": 10}, + {"text": "11", "value": 11}, + {"text": "21", "value": 21}, + ], + [ + {"text": "Raphael", "value": "Raphael"}, + {"text": "Bernie", "value": "Bernie"}, + ], + [ + {"text": "22.12", "value": 22.12}, + {"text": "-1.21", "value": -1.21}, + {"text": "7.77", "value": 7.77}, + ], + ], } - assert expected_json == REST_response.get_json() + assert expected_json == REST_response.get_json(force=True) def test_dynamic_restriction(token, client, schemas_simple): From 863fc84b46b621cadf8e72f6ee23db9f0b8849ad Mon Sep 17 00:00:00 2001 From: jverswijver Date: Mon, 29 Aug 2022 08:58:45 -0500 Subject: [PATCH 05/33] fix bug in docker compose --- docker-compose-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose-build.yaml b/docker-compose-build.yaml index 07d7b32..7f76c2f 100644 --- a/docker-compose-build.yaml +++ b/docker-compose-build.yaml @@ -11,7 +11,7 @@ services: - PY_VER - DISTRO - IMAGE - image: jverswijver/pharus:0.4.2-prerelease + image: datajoint/pharus:${PHARUS_VERSION} environment: - PHARUS_PRIVATE_KEY - PHARUS_PUBLIC_KEY From 0be8ddf3142a49bae0ac40d8a5590cb508db1062 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Mon, 29 Aug 2022 09:05:48 -0500 Subject: [PATCH 06/33] remove unused import --- pharus/component_interface.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index 9dbd9d1..1edd998 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -1,6 +1,5 @@ """This module is a GUI component library of various common interfaces.""" import json -from base64 import b64decode import datajoint as dj import re import inspect @@ -82,7 +81,7 @@ def __init__(self, name, component_config, static_config, jwt_payload: dict): self.vm_list = [ dj.VirtualModule( s, - s.replace("__", "-"), + s.replace("__DASH__", "-"), connection=dj.conn( host=jwt_payload["databaseAddress"], user=jwt_payload["username"], From 7dadea0f1558ef076a07ac86380097d9246730cc Mon Sep 17 00:00:00 2001 From: jverswijver Date: Mon, 29 Aug 2022 09:17:35 -0500 Subject: [PATCH 07/33] update changelog, version. --- CHANGELOG.md | 8 ++++++++ pharus/version.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed9211f..af4f9f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) convention. +## [0.4.2] - 2022-08-29 +### Fixed +- Bugs with UUID and NaN in component_interface.py JSON encoder PR #128 + +### Added +- Support for creating virtual modules with `-` in the schema name, use `__` PR #128 +- Support for new `antd-table`, old table filtering will no longer work and the entire table will be deprecated in a later release PR #128 + ## [0.4.1] - 2022-03-24 ### Fixed - Bug with otumat version not being tied to the latest PR #119 diff --git a/pharus/version.py b/pharus/version.py index e3ffbcb..93498cb 100644 --- a/pharus/version.py +++ b/pharus/version.py @@ -1,2 +1,2 @@ """Package metadata.""" -__version__ = "0.4.1" +__version__ = "0.4.2" From 194e585faf98062efb6374f1710e0436a81ccc34 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Mon, 29 Aug 2022 11:41:11 -0500 Subject: [PATCH 08/33] small tweak --- pharus/component_interface.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index 1edd998..cc4371e 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -189,9 +189,7 @@ def attributes_route(self): unique_values = [] for key in query.heading.attributes.keys(): result = (dj.U(key) & query).fetch() - unique_values.append( - [dict({"text": str(x[0]), "value": x[0]}) for x in result] - ) + unique_values.append([dict({"text": x[0], "value": x[0]}) for x in result]) return NumpyEncoder.dumps( dict( attributeHeaders=attributes_meta["attribute_headers"], From d60ffd888d27043be8df504abdd773c705d6d116 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Mon, 29 Aug 2022 14:41:06 -0500 Subject: [PATCH 09/33] fix bug --- pharus/component_interface.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index cc4371e..1edd998 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -189,7 +189,9 @@ def attributes_route(self): unique_values = [] for key in query.heading.attributes.keys(): result = (dj.U(key) & query).fetch() - unique_values.append([dict({"text": x[0], "value": x[0]}) for x in result]) + unique_values.append( + [dict({"text": str(x[0]), "value": x[0]}) for x in result] + ) return NumpyEncoder.dumps( dict( attributeHeaders=attributes_meta["attribute_headers"], From 6a427d2ab23c37962618e83d2fb58b424279b551 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Mon, 29 Aug 2022 16:44:22 -0500 Subject: [PATCH 10/33] update --- pharus/component_interface.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index 1edd998..e5a9e0f 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -81,7 +81,7 @@ def __init__(self, name, component_config, static_config, jwt_payload: dict): self.vm_list = [ dj.VirtualModule( s, - s.replace("__DASH__", "-"), + s.replace("__", "-"), connection=dj.conn( host=jwt_payload["databaseAddress"], user=jwt_payload["username"], @@ -121,7 +121,6 @@ def restriction(self): class TableComponent(QueryComponent): attributes_route_format = "{route}/attributes" - universal_set_route_format = "{route}/universalset" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) From ec2e344ec5144b47f6c10d5d575461988088f309 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Tue, 30 Aug 2022 16:35:13 -0500 Subject: [PATCH 11/33] apply suggestions from code review. --- pharus/component_interface.py | 4 ++-- pharus/version.py | 2 +- tests/test_api_gen.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index e5a9e0f..b8e3e58 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -38,7 +38,7 @@ def default(self, o): if type(o) is UUID: return str(o) if type(o) is str and o == "NaN": - return "null" + return None if type(o) in (datetime, date): return o.isoformat() return json.JSONEncoder.default(self, o) @@ -302,7 +302,7 @@ def __init__(self, *args, **kwargs): def dj_query_route(self): fetch_metadata = self.fetch_metadata record_header, table_records, total_count = _DJConnector._fetch_records( - query=fetch_metadata["query"] & self.restriction, + query=fetch_metadata["query"] & self.restriction[0], fetch_args=fetch_metadata["fetch_args"], page=int(request.args["page"]) if "page" in request.args else 1, limit=int(request.args["limit"]) if "limit" in request.args else 1000, diff --git a/pharus/version.py b/pharus/version.py index 93498cb..b74bea2 100644 --- a/pharus/version.py +++ b/pharus/version.py @@ -1,2 +1,2 @@ """Package metadata.""" -__version__ = "0.4.2" +__version__ = "0.5.0" diff --git a/tests/test_api_gen.py b/tests/test_api_gen.py index be82b2b..3c41b98 100644 --- a/tests/test_api_gen.py +++ b/tests/test_api_gen.py @@ -104,7 +104,7 @@ def test_get_attributes(token, client, schemas_simple): ], } - assert expected_json == REST_response.get_json(force=True) + assert expected_json == REST_response.get_json() def test_dynamic_restriction(token, client, schemas_simple): From 3a242d44bddd7e9e437c75e38e74b05668ad94de Mon Sep 17 00:00:00 2001 From: jverswijver Date: Tue, 30 Aug 2022 16:41:27 -0500 Subject: [PATCH 12/33] fix bug --- tests/test_api_gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_api_gen.py b/tests/test_api_gen.py index 3c41b98..be82b2b 100644 --- a/tests/test_api_gen.py +++ b/tests/test_api_gen.py @@ -104,7 +104,7 @@ def test_get_attributes(token, client, schemas_simple): ], } - assert expected_json == REST_response.get_json() + assert expected_json == REST_response.get_json(force=True) def test_dynamic_restriction(token, client, schemas_simple): From 962210f5e6d16f3e5182ed03d96664857d8be2c8 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Wed, 31 Aug 2022 13:47:02 -0500 Subject: [PATCH 13/33] apply suggestions from code review --- CHANGELOG.md | 2 +- pharus/component_interface.py | 1 - pharus/interface.py | 8 +++++ tests/test_api_gen.py | 64 +++++++++++++++++++++++------------ 4 files changed, 52 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af4f9f7..dddf214 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) convention. -## [0.4.2] - 2022-08-29 +## [0.5.0] - 2022-08-31 ### Fixed - Bugs with UUID and NaN in component_interface.py JSON encoder PR #128 diff --git a/pharus/component_interface.py b/pharus/component_interface.py index b8e3e58..ca48444 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -195,7 +195,6 @@ def attributes_route(self): dict( attributeHeaders=attributes_meta["attribute_headers"], attributes=attributes_meta["attributes"], - unique_values=unique_values, ) ) diff --git a/pharus/interface.py b/pharus/interface.py index 0217866..8d7336e 100644 --- a/pharus/interface.py +++ b/pharus/interface.py @@ -248,6 +248,10 @@ def _get_attributes(query) -> dict: attribute_info.nullable, attribute_info.default, attribute_info.autoincrement, + [ + dict({"text": str(x[0]), "value": x[0]}) + for x in (dj.U(attribute_name) & query).fetch() + ], ) ) else: @@ -258,6 +262,10 @@ def _get_attributes(query) -> dict: attribute_info.nullable, attribute_info.default, attribute_info.autoincrement, + [ + dict({"text": str(x[0]), "value": x[0]}) + for x in (dj.U(attribute_name) & query).fetch() + ], ) ) diff --git a/tests/test_api_gen.py b/tests/test_api_gen.py index be82b2b..1177387 100644 --- a/tests/test_api_gen.py +++ b/tests/test_api_gen.py @@ -77,31 +77,53 @@ def test_get_attributes(token, client, schemas_simple): "attributeHeaders": ["name", "type", "nullable", "default", "autoincrement"], "attributes": { "primary": [ - ["a_id", "int", False, None, False], - ["b_id", "int", False, None, False], + [ + "a_id", + "int", + False, + None, + False, + [{"text": "0", "value": 0}, {"text": "1", "value": 1}], + ], + [ + "b_id", + "int", + False, + None, + False, + [ + {"text": "10", "value": 10}, + {"text": "11", "value": 11}, + {"text": "21", "value": 21}, + ], + ], ], "secondary": [ - ["a_name", "varchar(30)", False, None, False], - ["b_number", "float", False, None, False], + [ + "a_name", + "varchar(30)", + False, + None, + False, + [ + {"text": "Raphael", "value": "Raphael"}, + {"text": "Bernie", "value": "Bernie"}, + ], + ], + [ + "b_number", + "float", + False, + None, + False, + [ + {"text": "22.12", "value": 22.12}, + {"text": "-1.21", "value": -1.21}, + {"text": "7.77", "value": 7.77}, + ], + ], ], }, - "unique_values": [ - [{"text": "0", "value": 0}, {"text": "1", "value": 1}], - [ - {"text": "10", "value": 10}, - {"text": "11", "value": 11}, - {"text": "21", "value": 21}, - ], - [ - {"text": "Raphael", "value": "Raphael"}, - {"text": "Bernie", "value": "Bernie"}, - ], - [ - {"text": "22.12", "value": 22.12}, - {"text": "-1.21", "value": -1.21}, - {"text": "7.77", "value": 7.77}, - ], - ], } assert expected_json == REST_response.get_json(force=True) From f1b0d2a0cd1dd9118f242b80a9f13d6a32d73f79 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Wed, 31 Aug 2022 13:55:33 -0500 Subject: [PATCH 14/33] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dddf214..647d445 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) convention. -## [0.5.0] - 2022-08-31 +## [0.5.0] - 2022-09-01 ### Fixed - Bugs with UUID and NaN in component_interface.py JSON encoder PR #128 From 5b25cf2e37dbdf4f11fb89fc5034b19931be62e1 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Wed, 31 Aug 2022 14:17:18 -0500 Subject: [PATCH 15/33] fix long regex --- pharus/dynamic_api_gen.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pharus/dynamic_api_gen.py b/pharus/dynamic_api_gen.py index 5abbe02..0d8ae8a 100644 --- a/pharus/dynamic_api_gen.py +++ b/pharus/dynamic_api_gen.py @@ -119,7 +119,16 @@ def {method_name}() -> dict: else grid["components"] ).items(): if re.match( - r"^(table|antd-table|metadata|plot|file|slider|dropdown-query|form).*$", + r"^(" + + r"table|" + + r"antd-table|" + + r"metadata|" + + r"plot|" + + r"file|" + + r"slider|" + + r"dropdown-query|" + + r"form" + + r").*$", comp["type"], ): f.write( From bf6849f7c21710cc9608ea670805b844524ac1e4 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Thu, 1 Sep 2022 10:08:28 -0500 Subject: [PATCH 16/33] apply suggestions from code review --- pharus/component_interface.py | 11 +++-------- pharus/dynamic_api_gen.py | 21 +++++++++++---------- pharus/interface.py | 21 ++++++++++++++------- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index 031d00a..0674800 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -334,14 +334,9 @@ def dj_query_route(self): ) def attributes_route(self): - attributes_meta = _DJConnector._get_attributes(self.fetch_metadata["query"]) - query = self.fetch_metadata["query"] & self.restriction[0] - unique_values = [] - for key in query.heading.attributes.keys(): - result = (dj.U(key) & query).fetch() - unique_values.append( - [dict({"text": str(x[0]), "value": x[0]}) for x in result] - ) + attributes_meta = _DJConnector._get_attributes( + self.fetch_metadata["query"], include_unique_values=True + ) return NumpyEncoder.dumps( dict( attributeHeaders=attributes_meta["attribute_headers"], diff --git a/pharus/dynamic_api_gen.py b/pharus/dynamic_api_gen.py index 0d8ae8a..1ee21b2 100644 --- a/pharus/dynamic_api_gen.py +++ b/pharus/dynamic_api_gen.py @@ -119,17 +119,18 @@ def {method_name}() -> dict: else grid["components"] ).items(): if re.match( - r"^(" - + r"table|" - + r"antd-table|" - + r"metadata|" - + r"plot|" - + r"file|" - + r"slider|" - + r"dropdown-query|" - + r"form" - + r").*$", + r"""^( + table| + antd-table| + metadata| + plot| + file| + slider| + dropdown-query| + form + ).*$""", comp["type"], + flags=re.VERBOSE, ): f.write( (active_route_template).format( diff --git a/pharus/interface.py b/pharus/interface.py index 8d7336e..13184b3 100644 --- a/pharus/interface.py +++ b/pharus/interface.py @@ -225,12 +225,15 @@ def _fetch_records( return list(attributes.keys()), rows, len(query_restricted) @staticmethod - def _get_attributes(query) -> dict: + def _get_attributes(query, include_unique_values=False) -> dict: """ Method to get primary and secondary attributes of a query. :param query: any datajoint object related to QueryExpression :type query: datajoint ``QueryExpression`` or related object + :param include_unique_values: boolean that determines if the unique values are + included as part of the returned attributes + :type include_unique_values: boolean, optional :return: Dict with keys ``attribute_headers`` and ``attributes`` containing ``primary``, ``secondary`` which each contain a ``list`` of ``tuples`` specifying: ``attribute_name``, ``type``, ``nullable``, @@ -249,9 +252,11 @@ def _get_attributes(query) -> dict: attribute_info.default, attribute_info.autoincrement, [ - dict({"text": str(x[0]), "value": x[0]}) - for x in (dj.U(attribute_name) & query).fetch() - ], + dict({"text": str(v), "value": v}) + for (v,) in (dj.U(attribute_name) & query) + ] + if include_unique_values + else None, ) ) else: @@ -263,9 +268,11 @@ def _get_attributes(query) -> dict: attribute_info.default, attribute_info.autoincrement, [ - dict({"text": str(x[0]), "value": x[0]}) - for x in (dj.U(attribute_name) & query).fetch() - ], + dict({"text": str(v), "value": v}) + for (v,) in (dj.U(attribute_name) & query) + ] + if include_unique_values + else None, ) ) From c22488646a34d2fcc4d8aec4ac93aeeb75d7335d Mon Sep 17 00:00:00 2001 From: jverswijver <49455164+jverswijver@users.noreply.github.com> Date: Thu, 1 Sep 2022 10:09:30 -0500 Subject: [PATCH 17/33] Apply suggestions from code review Co-authored-by: Raphael Guzman <38401847+guzman-raphael@users.noreply.github.com> --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 647d445..26599b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,11 @@ Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and ## [0.5.0] - 2022-09-01 ### Fixed -- Bugs with UUID and NaN in component_interface.py JSON encoder PR #128 +- Bugs with returning UUID and NaN values PR #128 ### Added -- Support for creating virtual modules with `-` in the schema name, use `__` PR #128 -- Support for new `antd-table`, old table filtering will no longer work and the entire table will be deprecated in a later release PR #128 +- Support schemas with a `-` by specifying instead with `__` in dynamic spec PR #128 +- Support for new `antd-table` component. Prior `table` component will be deprecated in the next minor release. PR #128 ## [0.4.1] - 2022-03-24 ### Fixed From 7673bc4f197bfc580ae0e6dc3140f1d2932d81e3 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Thu, 1 Sep 2022 10:15:51 -0500 Subject: [PATCH 18/33] fix bug --- pharus/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pharus/interface.py b/pharus/interface.py index 13184b3..48b33f4 100644 --- a/pharus/interface.py +++ b/pharus/interface.py @@ -269,7 +269,7 @@ def _get_attributes(query, include_unique_values=False) -> dict: attribute_info.autoincrement, [ dict({"text": str(v), "value": v}) - for (v,) in (dj.U(attribute_name) & query) + for (v,) in (dj.U(attribute_name) & query).fetch() ] if include_unique_values else None, From cd4b0e074f67a31f96a8f79565c28cba1efadf5b Mon Sep 17 00:00:00 2001 From: jverswijver Date: Thu, 1 Sep 2022 10:34:05 -0500 Subject: [PATCH 19/33] fix tests --- pharus/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pharus/interface.py b/pharus/interface.py index 48b33f4..e5a3be6 100644 --- a/pharus/interface.py +++ b/pharus/interface.py @@ -253,7 +253,7 @@ def _get_attributes(query, include_unique_values=False) -> dict: attribute_info.autoincrement, [ dict({"text": str(v), "value": v}) - for (v,) in (dj.U(attribute_name) & query) + for (v,) in (dj.U(attribute_name) & query).fetch() ] if include_unique_values else None, From 70035bde4a5d6c6566785b548ae3162a9f95f3e2 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Thu, 1 Sep 2022 11:43:11 -0500 Subject: [PATCH 20/33] fix mimetype issue --- pharus/component_interface.py | 89 +++++++++++++++++++++-------------- pharus/dynamic_api_gen.py | 10 ++-- tests/test_api_gen.py | 26 +++------- 3 files changed, 67 insertions(+), 58 deletions(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index 0674800..d5fe04c 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -53,6 +53,7 @@ class FetchComponent: def __init__(self, name, component_config, static_config, jwt_payload: dict): lcls = locals() self.name = name + self.response_mimetype = "application/json" if static_config: self.static_variables = types.MappingProxyType(static_config) if not all(k in component_config for k in ("x", "y", "height", "width")): @@ -91,6 +92,10 @@ def __init__(self, name, component_config, static_config, jwt_payload: dict): def fetch_metadata(self): return self.dj_query(*self.vm_list) + @property + def GET_mimetype(self): + return self.response_mimetype + @property def restriction(self): # first element includes the spec's restriction, @@ -132,6 +137,8 @@ def __init__( ): self.name = name self.payload = payload + self.get_response_mimetype = "application/json" + self.post_response_mimetype = "text/plain" if static_config: self.static_variables = types.MappingProxyType(static_config) if not all(k in component_config for k in ("x", "y", "height", "width")): @@ -174,6 +181,14 @@ def __init__( key=lambda p: p.full_table_name, ) + @property + def GET_mimetype(self): + return self.get_response_mimetype + + @property + def POST_mimetype(self): + return self.post_response_mimetype + def dj_query_route(self): with self.connection.transaction: destination_lookup = reduce( @@ -231,42 +246,44 @@ def fields_route(self): } if not self.fields_map: - return dict(fields=list(source_fields.values())) - return dict( - fields=[ - dict( - (field := source_fields.pop(m["destination"])), - name=m["input" if "input" in m else "destination"], - **( - { - "values": field["values"] - if "map" not in m - else [ - { - input_lookup[k]: v - for k, v in r.items() - if k - in ( - input_lookup := { - table_m["destination"]: table_m[ - "input" - if "input" in table_m - else "destination" - ] - for table_m in m["map"] - } - ) - } - for r in field["values"] - ] - } - if m["type"] == "table" - else {} - ), - ) - for m in self.fields_map - ] - + list(source_fields.values()) + return NumpyEncoder.dumps(dict(fields=list(source_fields.values()))) + return NumpyEncoder.dumps( + dict( + fields=[ + dict( + (field := source_fields.pop(m["destination"])), + name=m["input" if "input" in m else "destination"], + **( + { + "values": field["values"] + if "map" not in m + else [ + { + input_lookup[k]: v + for k, v in r.items() + if k + in ( + input_lookup := { + table_m["destination"]: table_m[ + "input" + if "input" in table_m + else "destination" + ] + for table_m in m["map"] + } + ) + } + for r in field["values"] + ] + } + if m["type"] == "table" + else {} + ), + ) + for m in self.fields_map + ] + + list(source_fields.values()) + ) ) diff --git a/pharus/dynamic_api_gen.py b/pharus/dynamic_api_gen.py index 1ee21b2..4721d7a 100644 --- a/pharus/dynamic_api_gen.py +++ b/pharus/dynamic_api_gen.py @@ -12,7 +12,7 @@ def populate_api(): header_template = """# Auto-generated rest api from .server import app, protected_route from .interface import _DJConnector, dj -from flask import request +from flask import request, Response from json import loads from base64 import b64decode from datetime import datetime @@ -37,7 +37,9 @@ def {method_name}(jwt_payload: dict) -> dict: static_config={static_config}, jwt_payload=jwt_payload, {payload}) - return component_instance.{method_name_type}() + return Response(response=component_instance.{method_name_type}(), + status=200, + mimetype=component_instance.{rest_verb}_mimetype) except Exception as e: return traceback.format_exc(), 500 """ @@ -57,7 +59,9 @@ def {method_name}() -> dict: static_config={static_config}, jwt_payload=jwt_payload, {payload}) - return component_instance.{method_name_type}() + return Response(response=component_instance.{method_name_type}(), + status=200, + mimetype=component_instance.{rest_verb}_mimetype) except Exception as e: return traceback.format_exc(), 500 """ diff --git a/tests/test_api_gen.py b/tests/test_api_gen.py index 1177387..fe1ea8b 100644 --- a/tests/test_api_gen.py +++ b/tests/test_api_gen.py @@ -31,18 +31,10 @@ def test_auto_generated_route(token, client, schemas_simple): } ) - assert expected_json == json.dumps( - REST_response1.get_json(force=True), sort_keys=True - ) - assert expected_json == json.dumps( - REST_response2.get_json(force=True), sort_keys=True - ) - assert expected_json == json.dumps( - REST_response3.get_json(force=True), sort_keys=True - ) - assert expected_json == json.dumps( - REST_response4.get_json(force=True), sort_keys=True - ) + assert expected_json == json.dumps(REST_response1.get_json(), sort_keys=True) + assert expected_json == json.dumps(REST_response2.get_json(), sort_keys=True) + assert expected_json == json.dumps(REST_response3.get_json(), sort_keys=True) + assert expected_json == json.dumps(REST_response4.get_json(), sort_keys=True) def test_get_full_plot(token, client, schemas_simple): @@ -63,9 +55,7 @@ def test_get_full_plot(token, client, schemas_simple): ), sort_keys=True, ) - assert expected_json == json.dumps( - REST_response1.get_json(force=True), sort_keys=True - ) + assert expected_json == json.dumps(REST_response1.get_json(), sort_keys=True) def test_get_attributes(token, client, schemas_simple): @@ -126,7 +116,7 @@ def test_get_attributes(token, client, schemas_simple): }, } - assert expected_json == REST_response.get_json(force=True) + assert expected_json == REST_response.get_json() def test_dynamic_restriction(token, client, schemas_simple): @@ -139,6 +129,4 @@ def test_dynamic_restriction(token, client, schemas_simple): "totalCount": 2, } ) - assert expected_json == json.dumps( - REST_response.get_json(force=True), sort_keys=True - ) + assert expected_json == json.dumps(REST_response.get_json(), sort_keys=True) From 494df730fdcccb9fbdce0c165f8a6cf71cbd1f5c Mon Sep 17 00:00:00 2001 From: jverswijver Date: Mon, 12 Sep 2022 11:28:20 -0500 Subject: [PATCH 21/33] fix bug --- pharus/component_interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index d5fe04c..dfa33bb 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -352,7 +352,7 @@ def dj_query_route(self): def attributes_route(self): attributes_meta = _DJConnector._get_attributes( - self.fetch_metadata["query"], include_unique_values=True + self.fetch_metadata["query"] & self.restriction, include_unique_values=True ) return NumpyEncoder.dumps( dict( From b8dc136d73d78fe426fec0704aa17e6d1eb1f40f Mon Sep 17 00:00:00 2001 From: jverswijver Date: Mon, 12 Sep 2022 15:37:42 -0500 Subject: [PATCH 22/33] fix bug --- pharus/component_interface.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index dfa33bb..5b1441f 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -123,9 +123,15 @@ def dj_query_route(self): record_header, table_records, total_count = _DJConnector._fetch_records( query=fetch_metadata["query"] & self.restriction, fetch_args=fetch_metadata["fetch_args"], + limit=int(request.args["limit"]) if "limit" in request.args else 1000, + page=int(request.args["page"]) if "page" in request.args else 1, ) - return dict( - recordHeader=record_header, records=table_records, totalCount=total_count + return NumpyEncoder.dumps( + dict( + recordHeader=record_header, + records=table_records, + totalCount=total_count, + ) ) From 6dffdba968791ade38f92d0c224b9cea2bf27083 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Tue, 15 Nov 2022 15:45:05 -0600 Subject: [PATCH 23/33] fix bug --- pharus/dynamic_api_gen.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pharus/dynamic_api_gen.py b/pharus/dynamic_api_gen.py index bc9e436..1d72587 100644 --- a/pharus/dynamic_api_gen.py +++ b/pharus/dynamic_api_gen.py @@ -11,14 +11,9 @@ def populate_api(): header_template = """# Auto-generated rest api from .server import app, protected_route -<<<<<<< HEAD from .interface import _DJConnector, dj from flask import request, Response -======= -from .interface import _DJConnector -from flask import request import datajoint as dj ->>>>>>> 481614654e42248542af6b4711cee6f40c38db97 from json import loads from base64 import b64decode from datetime import datetime From 85c84bf9f8826596dc413b062acaa706037c3ad3 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Wed, 30 Nov 2022 10:46:24 -0600 Subject: [PATCH 24/33] fix bug --- pharus/component_interface.py | 14 +------------- pharus/dynamic_api_gen.py | 8 ++------ 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index edb5aa1..8423450 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -90,7 +90,7 @@ def __init__(self, *args, **kwargs): self.vm_list = [ dj.VirtualModule( s, - s, + s.replace("__", "-"), connection=self.connection, ) for s in inspect.getfullargspec(self.dj_query).args @@ -100,10 +100,6 @@ def __init__(self, *args, **kwargs): def fetch_metadata(self): return self.dj_query(*self.vm_list) - @property - def GET_mimetype(self): - return self.response_mimetype - @property def restriction(self): # first element includes the spec's restriction, @@ -181,14 +177,6 @@ def __init__(self, *args, **kwargs): } self.input_lookup = {v: k for k, v in self.destination_lookup.items()} - @property - def GET_mimetype(self): - return self.get_response_mimetype - - @property - def POST_mimetype(self): - return self.post_response_mimetype - def dj_query_route(self): with self.connection.transaction: for t in self.tables: diff --git a/pharus/dynamic_api_gen.py b/pharus/dynamic_api_gen.py index 1d72587..4071f01 100644 --- a/pharus/dynamic_api_gen.py +++ b/pharus/dynamic_api_gen.py @@ -38,9 +38,7 @@ def {method_name}(connection: dj.Connection) -> dict: static_config={static_config}, connection=connection, {payload}) - return Response(response=component_instance.{method_name_type}(), - status=200, - mimetype=component_instance.{rest_verb}_mimetype) + return component_instance.{method_name_type}() except Exception as e: return traceback.format_exc(), 500 """ @@ -60,9 +58,7 @@ def {method_name}() -> dict: static_config={static_config}, connection=connection, {payload}) - return Response(response=component_instance.{method_name_type}(), - status=200, - mimetype=component_instance.{rest_verb}_mimetype) + return component_instance.{method_name_type}() except Exception as e: return traceback.format_exc(), 500 """ From 73fc80afea28b658c97ca2039bc9d027f11ce2b4 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Wed, 30 Nov 2022 13:25:46 -0600 Subject: [PATCH 25/33] fixbug --- tests/test_api_gen.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/tests/test_api_gen.py b/tests/test_api_gen.py index fe1ea8b..1177387 100644 --- a/tests/test_api_gen.py +++ b/tests/test_api_gen.py @@ -31,10 +31,18 @@ def test_auto_generated_route(token, client, schemas_simple): } ) - assert expected_json == json.dumps(REST_response1.get_json(), sort_keys=True) - assert expected_json == json.dumps(REST_response2.get_json(), sort_keys=True) - assert expected_json == json.dumps(REST_response3.get_json(), sort_keys=True) - assert expected_json == json.dumps(REST_response4.get_json(), sort_keys=True) + assert expected_json == json.dumps( + REST_response1.get_json(force=True), sort_keys=True + ) + assert expected_json == json.dumps( + REST_response2.get_json(force=True), sort_keys=True + ) + assert expected_json == json.dumps( + REST_response3.get_json(force=True), sort_keys=True + ) + assert expected_json == json.dumps( + REST_response4.get_json(force=True), sort_keys=True + ) def test_get_full_plot(token, client, schemas_simple): @@ -55,7 +63,9 @@ def test_get_full_plot(token, client, schemas_simple): ), sort_keys=True, ) - assert expected_json == json.dumps(REST_response1.get_json(), sort_keys=True) + assert expected_json == json.dumps( + REST_response1.get_json(force=True), sort_keys=True + ) def test_get_attributes(token, client, schemas_simple): @@ -116,7 +126,7 @@ def test_get_attributes(token, client, schemas_simple): }, } - assert expected_json == REST_response.get_json() + assert expected_json == REST_response.get_json(force=True) def test_dynamic_restriction(token, client, schemas_simple): @@ -129,4 +139,6 @@ def test_dynamic_restriction(token, client, schemas_simple): "totalCount": 2, } ) - assert expected_json == json.dumps(REST_response.get_json(), sort_keys=True) + assert expected_json == json.dumps( + REST_response.get_json(force=True), sort_keys=True + ) From 440a49374aee1f2057bece9d08ba059344979b6e Mon Sep 17 00:00:00 2001 From: jverswijver Date: Wed, 30 Nov 2022 13:38:22 -0600 Subject: [PATCH 26/33] remove unused imports --- pharus/component_interface.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index 8423450..5032f62 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -12,7 +12,6 @@ import io import numpy as np from uuid import UUID -from functools import reduce class NumpyEncoder(json.JSONEncoder): From 2f65ada213d8db80c3fb296dedb8a7d1bcf306ea Mon Sep 17 00:00:00 2001 From: A-Baji Date: Thu, 1 Dec 2022 17:47:05 -0600 Subject: [PATCH 27/33] remove force get_json --- pharus/component_interface.py | 74 ++++++++++++++++++++++------------- tests/test_api_gen.py | 26 ++++-------- 2 files changed, 54 insertions(+), 46 deletions(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index 5032f62..c56e9d6 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -129,12 +129,16 @@ def dj_query_route(self): limit=int(request.args["limit"]) if "limit" in request.args else 1000, page=int(request.args["page"]) if "page" in request.args else 1, ) - return NumpyEncoder.dumps( - dict( - recordHeader=record_header, - records=table_records, - totalCount=total_count, - ) + return ( + NumpyEncoder.dumps( + dict( + recordHeader=record_header, + records=table_records, + totalCount=total_count, + ) + ), + 200, + {"Content-Type": "application/json"}, ) @@ -299,23 +303,31 @@ def dj_query_route(self): order=request.args["order"].split(",") if "order" in request.args else None, ) - return NumpyEncoder.dumps( - dict( - recordHeader=record_header, - records=table_records, - totalCount=total_count, - ) + return ( + NumpyEncoder.dumps( + dict( + recordHeader=record_header, + records=table_records, + totalCount=total_count, + ) + ), + 200, + {"Content-Type": "application/json"}, ) def attributes_route(self): attributes_meta = _DJConnector._get_attributes( self.fetch_metadata["query"] & self.restriction, include_unique_values=True ) - return NumpyEncoder.dumps( - dict( - attributeHeaders=attributes_meta["attribute_headers"], - attributes=attributes_meta["attributes"], - ) + return ( + NumpyEncoder.dumps( + dict( + attributeHeaders=attributes_meta["attribute_headers"], + attributes=attributes_meta["attributes"], + ) + ), + 200, + {"Content-Type": "application/json"}, ) @@ -359,12 +371,16 @@ def dj_query_route(self): query=fetch_metadata["query"] & self.restriction, fetch_args=fetch_metadata["fetch_args"], ) - return NumpyEncoder.dumps( - dict( - recordHeader=record_header, - records=table_records, - totalCount=total_count, - ) + return ( + NumpyEncoder.dumps( + dict( + recordHeader=record_header, + records=table_records, + totalCount=total_count, + ) + ), + 200, + {"Content-Type": "application/json"}, ) @@ -390,10 +406,14 @@ def __init__(self, *args, **kwargs): def dj_query_route(self): fetch_metadata = self.fetch_metadata - return NumpyEncoder.dumps( - (fetch_metadata["query"] & self.restriction).fetch1( - *fetch_metadata["fetch_args"] - ) + return ( + NumpyEncoder.dumps( + (fetch_metadata["query"] & self.restriction).fetch1( + *fetch_metadata["fetch_args"] + ) + ), + 200, + {"Content-Type": "application/json"}, ) diff --git a/tests/test_api_gen.py b/tests/test_api_gen.py index 1177387..fe1ea8b 100644 --- a/tests/test_api_gen.py +++ b/tests/test_api_gen.py @@ -31,18 +31,10 @@ def test_auto_generated_route(token, client, schemas_simple): } ) - assert expected_json == json.dumps( - REST_response1.get_json(force=True), sort_keys=True - ) - assert expected_json == json.dumps( - REST_response2.get_json(force=True), sort_keys=True - ) - assert expected_json == json.dumps( - REST_response3.get_json(force=True), sort_keys=True - ) - assert expected_json == json.dumps( - REST_response4.get_json(force=True), sort_keys=True - ) + assert expected_json == json.dumps(REST_response1.get_json(), sort_keys=True) + assert expected_json == json.dumps(REST_response2.get_json(), sort_keys=True) + assert expected_json == json.dumps(REST_response3.get_json(), sort_keys=True) + assert expected_json == json.dumps(REST_response4.get_json(), sort_keys=True) def test_get_full_plot(token, client, schemas_simple): @@ -63,9 +55,7 @@ def test_get_full_plot(token, client, schemas_simple): ), sort_keys=True, ) - assert expected_json == json.dumps( - REST_response1.get_json(force=True), sort_keys=True - ) + assert expected_json == json.dumps(REST_response1.get_json(), sort_keys=True) def test_get_attributes(token, client, schemas_simple): @@ -126,7 +116,7 @@ def test_get_attributes(token, client, schemas_simple): }, } - assert expected_json == REST_response.get_json(force=True) + assert expected_json == REST_response.get_json() def test_dynamic_restriction(token, client, schemas_simple): @@ -139,6 +129,4 @@ def test_dynamic_restriction(token, client, schemas_simple): "totalCount": 2, } ) - assert expected_json == json.dumps( - REST_response.get_json(force=True), sort_keys=True - ) + assert expected_json == json.dumps(REST_response.get_json(), sort_keys=True) From 875010b1c6ebcc84a35f748a7e74d6c8edceea3c Mon Sep 17 00:00:00 2001 From: A-Baji Date: Fri, 2 Dec 2022 13:05:42 -0600 Subject: [PATCH 28/33] restriction query parameter handling and test --- pharus/component_interface.py | 7 +++++-- tests/test_api_gen.py | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/pharus/component_interface.py b/pharus/component_interface.py index c56e9d6..649ef10 100644 --- a/pharus/component_interface.py +++ b/pharus/component_interface.py @@ -1,4 +1,5 @@ """This module is a GUI component library of various common interfaces.""" +from base64 import b64decode import json import datajoint as dj import re @@ -126,9 +127,8 @@ def dj_query_route(self): record_header, table_records, total_count = _DJConnector._fetch_records( query=fetch_metadata["query"] & self.restriction, fetch_args=fetch_metadata["fetch_args"], - limit=int(request.args["limit"]) if "limit" in request.args else 1000, - page=int(request.args["page"]) if "page" in request.args else 1, ) + return ( NumpyEncoder.dumps( dict( @@ -301,6 +301,9 @@ def dj_query_route(self): limit=int(request.args["limit"]) if "limit" in request.args else 1000, page=int(request.args["page"]) if "page" in request.args else 1, order=request.args["order"].split(",") if "order" in request.args else None, + restriction=json.loads(b64decode(request.args["restriction"])) + if "restriction" in request.args + else [], ) return ( diff --git a/tests/test_api_gen.py b/tests/test_api_gen.py index fe1ea8b..3fbb51c 100644 --- a/tests/test_api_gen.py +++ b/tests/test_api_gen.py @@ -1,4 +1,5 @@ from . import SCHEMA_PREFIX, token, client, connection, schemas_simple +from base64 import b64encode import json @@ -130,3 +131,23 @@ def test_dynamic_restriction(token, client, schemas_simple): } ) assert expected_json == json.dumps(REST_response.get_json(), sort_keys=True) + + +def test_fetch_restriction(token, client, schemas_simple): + restriction = [{"attributeName": "a_id", "operation": "=", "value": 1}] + encoded = b64encode(json.dumps(restriction).encode("utf-8")) + REST_response = client.get( + f"/query1?restriction={encoded.decode()}", + headers=dict(Authorization=f"Bearer {token}"), + ) + # should restrict in the query parameter by a_id=1 + expected_json = json.dumps( + { + "recordHeader": ["a_id", "b_id", "a_name", "b_number"], + "records": [ + [1, 21, "Bernie", 7.77], + ], + "totalCount": 1, + } + ) + assert expected_json == json.dumps(REST_response.get_json(), sort_keys=True) From 8442e1e77a4461d1b21704c59d2c7bfe0daf59bd Mon Sep 17 00:00:00 2001 From: jverswijver Date: Mon, 5 Dec 2022 10:46:11 -0600 Subject: [PATCH 29/33] Update changelog, add deprication warning --- CHANGELOG.md | 9 ++++++++- pharus/dynamic_api_gen.py | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2578bb..93cfa0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,18 @@ Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) convention. +## [0.6.3] - 2022-12-07 + +### Added + +- Support for new `antd-table` component. Prior `table` component is deprecated and will be removed in the next major release. PR #128 +- Deprecated warning for `table` PR #128 + ## [0.6.3] - 2022-11-18 ### Added - Added attribute default value to the form component field route response -- Support for new `antd-table` component. Prior `table` component will be deprecated in the next minor release. PR #128 ## [0.6.2] - 2022-11-10 @@ -227,6 +233,7 @@ Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and - Support for DataJoint attribute types: `varchar`, `int`, `float`, `datetime`, `date`, `time`, `decimal`, `uuid`. - Check dependency utility to determine child table references. +[0.6.4]: https://github.com/datajoint/pharus/compare/0.6.3...0.6.4 [0.6.3]: https://github.com/datajoint/pharus/compare/0.6.2...0.6.3 [0.6.2]: https://github.com/datajoint/pharus/compare/0.6.1...0.6.2 [0.6.1]: https://github.com/datajoint/pharus/compare/0.6.0...0.6.1 diff --git a/pharus/dynamic_api_gen.py b/pharus/dynamic_api_gen.py index 4071f01..bd7d3f2 100644 --- a/pharus/dynamic_api_gen.py +++ b/pharus/dynamic_api_gen.py @@ -4,7 +4,7 @@ import pkg_resources import json import re - +from warnings import warn from pharus.component_interface import InsertComponent, TableComponent @@ -119,6 +119,13 @@ def {method_name}() -> dict: if "component_templates" in grid else grid["components"] ).items(): + if re.match(r"^table.*$", comp["type"]): + warn( + "table component be Deprecated in next major release, " + + "please use antd-table", + DeprecationWarning, + stacklevel=2, + ) if re.match( r"""^( table| From 41d86f3c4812c229c5fae6a8033f7e8be559ca27 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Mon, 5 Dec 2022 10:54:58 -0600 Subject: [PATCH 30/33] fix warning --- pharus/dynamic_api_gen.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pharus/dynamic_api_gen.py b/pharus/dynamic_api_gen.py index bd7d3f2..c62b232 100644 --- a/pharus/dynamic_api_gen.py +++ b/pharus/dynamic_api_gen.py @@ -4,7 +4,7 @@ import pkg_resources import json import re -from warnings import warn +import warnings from pharus.component_interface import InsertComponent, TableComponent @@ -120,8 +120,11 @@ def {method_name}() -> dict: else grid["components"] ).items(): if re.match(r"^table.*$", comp["type"]): - warn( - "table component be Deprecated in next major release, " + # For some reason the warnings package filters out deprecation + # warnings by default so make sure that filter is turned off + warnings.simplefilter("always", DeprecationWarning) + warnings.warn( + "table component to be Deprecated in next major release, " + "please use antd-table", DeprecationWarning, stacklevel=2, From 24337b5fc9fce99db31ec126d92552ea4de9d14e Mon Sep 17 00:00:00 2001 From: jverswijver Date: Mon, 5 Dec 2022 10:56:01 -0600 Subject: [PATCH 31/33] clean up changlog + version --- CHANGELOG.md | 2 +- pharus/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93cfa0f..d8d55d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) convention. -## [0.6.3] - 2022-12-07 +## [0.6.4] - 2022-12-07 ### Added diff --git a/pharus/version.py b/pharus/version.py index 3b6d053..3409e04 100644 --- a/pharus/version.py +++ b/pharus/version.py @@ -1,2 +1,2 @@ """Package metadata.""" -__version__ = "0.6.3" +__version__ = "0.6.4" From 7e701e1387b02dd7c36d53f48ee74f958b0cfc22 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Wed, 7 Dec 2022 11:09:10 -0600 Subject: [PATCH 32/33] bump versions --- README.rst | 4 ++-- docker-compose-deploy.yaml | 4 ++-- pharus/server.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 7cc1598..8bf0172 100644 --- a/README.rst +++ b/README.rst @@ -29,13 +29,13 @@ To start the API server, use the command: .. code-block:: bash - PHARUS_VERSION=0.6.3 docker-compose -f docker-compose-deploy.yaml up -d + PHARUS_VERSION=0.6.4 docker-compose -f docker-compose-deploy.yaml up -d To stop the API server, use the command: .. code-block:: bash - PHARUS_VERSION=0.6.3 docker-compose -f docker-compose-deploy.yaml down + PHARUS_VERSION=0.6.4 docker-compose -f docker-compose-deploy.yaml down References ---------- diff --git a/docker-compose-deploy.yaml b/docker-compose-deploy.yaml index ca1b3c3..fe1b312 100644 --- a/docker-compose-deploy.yaml +++ b/docker-compose-deploy.yaml @@ -1,5 +1,5 @@ -# PHARUS_VERSION=0.6.3 docker-compose -f docker-compose-deploy.yaml pull -# PHARUS_VERSION=0.6.3 docker-compose -f docker-compose-deploy.yaml up -d +# PHARUS_VERSION=0.6.4 docker-compose -f docker-compose-deploy.yaml pull +# PHARUS_VERSION=0.6.4 docker-compose -f docker-compose-deploy.yaml up -d # # Intended for production deployment. # Note: You must run both commands above for minimal outage diff --git a/pharus/server.py b/pharus/server.py index 40e8ad6..0332f82 100644 --- a/pharus/server.py +++ b/pharus/server.py @@ -126,7 +126,7 @@ def api_version() -> str: Content-Type: application/json { - "version": "0.6.3" + "version": "0.6.4" } :statuscode 200: No error. From 49c6b2efc1dd9319bf3f3f1846bd47cf4288c671 Mon Sep 17 00:00:00 2001 From: jverswijver Date: Wed, 7 Dec 2022 11:13:47 -0600 Subject: [PATCH 33/33] fix bug --- pharus/dynamic_api_gen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pharus/dynamic_api_gen.py b/pharus/dynamic_api_gen.py index c62b232..993590d 100644 --- a/pharus/dynamic_api_gen.py +++ b/pharus/dynamic_api_gen.py @@ -11,8 +11,8 @@ def populate_api(): header_template = """# Auto-generated rest api from .server import app, protected_route -from .interface import _DJConnector, dj -from flask import request, Response +from .interface import _DJConnector +from flask import request import datajoint as dj from json import loads from base64 import b64decode