From 4e5c6051e11e4e8f2afe8cea91d38212ce02d301 Mon Sep 17 00:00:00 2001 From: Patrick Croke <86120203+tannishmango@users.noreply.github.com> Date: Tue, 27 Feb 2024 22:06:47 -0700 Subject: [PATCH 1/3] Update setup.py --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 24c877a..bc6e271 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ # version compliant with PEP440 # https://peps.python.org/pep-0440/ - version='4.0.2', + version='4.1.0', # project meta long_description = long_description, @@ -33,8 +33,8 @@ url='https://github.com/TransposeData/transpose-python-sdk', # Author details - author='Michael Calvey (michaeljohncalvey), Alex Langshur (alangshur), Jonathan Becker (jon-becker)', - author_email='michael@transpose.io, alex@transpose.io, jon@transpose.io', + author='Michael Calvey (michaeljohncalvey), Alex Langshur (alangshur), Jonathan Becker (jon-becker), Patrick Croke (tannishmango)', + author_email='michael@transpose.io, alex@transpose.io, jon@transpose.io, patrick@transpose.io', # Find all packages in the directory packages=find_packages(exclude=['tests', 'demo', 'docs']), From e77f9fb30f32d5be11c766c90beda782aeb687bf Mon Sep 17 00:00:00 2001 From: Patrick Croke <86120203+tannishmango@users.noreply.github.com> Date: Wed, 28 Feb 2024 14:48:43 -0700 Subject: [PATCH 2/3] add toPandas() on QueryResult class --- setup.py | 2 +- tests/test_analytical.py | 6 +++-- tests/test_sql.py | 3 ++- transpose/src/api/analytical/base.py | 9 ++++---- transpose/src/api/sql/base.py | 9 ++++---- transpose/src/util/client.py | 28 ++++++----------------- transpose/src/util/models.py | 34 ++++++++++++++++++++++++---- 7 files changed, 53 insertions(+), 38 deletions(-) diff --git a/setup.py b/setup.py index bc6e271..b6cb016 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ # version compliant with PEP440 # https://peps.python.org/pep-0440/ - version='4.1.0', + version='4.2.0', # project meta long_description = long_description, diff --git a/tests/test_analytical.py b/tests/test_analytical.py index 070e987..27ae245 100644 --- a/tests/test_analytical.py +++ b/tests/test_analytical.py @@ -70,8 +70,10 @@ def test_query_df(): try: api = Transpose(api_key) - - response = api.analytical.query("SELECT * FROM cross_chain.transaction_flows LIMIT 10;", return_df=True) + query = "SELECT * FROM cross_chain.transaction_flows LIMIT 10;" + response = api.analytical.query( + query + ).toPandas() assert len(response) == 10 assert isinstance(response, DataFrame) diff --git a/tests/test_sql.py b/tests/test_sql.py index 13aa4d5..7e6818c 100644 --- a/tests/test_sql.py +++ b/tests/test_sql.py @@ -50,7 +50,8 @@ def test_query_df(): try: api = Transpose(api_key) - response = api.sql.query("SELECT * FROM ethereum.logs LIMIT 100;", return_df=True) + query = "SELECT * FROM ethereum.logs LIMIT 100;" + response = api.sql.query(query).toPandas() assert type(response) is DataFrame assert len(response) == 100 diff --git a/transpose/src/api/analytical/base.py b/transpose/src/api/analytical/base.py index 070eaef..fc62b25 100644 --- a/transpose/src/api/analytical/base.py +++ b/transpose/src/api/analytical/base.py @@ -1,3 +1,4 @@ +from ...util.models import QueryResult from ....src.util.client import post_api_request @@ -11,8 +12,7 @@ def query( self, sql_query: str, parameters: dict = None, - return_df: bool = False - ) -> dict: + ) -> QueryResult: parameters = {} if parameters is None else parameters @@ -22,10 +22,11 @@ def query( 'parameters': parameters } - return post_api_request( + result = post_api_request( url=url, api_key=self.super.api_key, body=body, - return_df=return_df, verbose=self.super.verbose ) + + return QueryResult(result) diff --git a/transpose/src/api/sql/base.py b/transpose/src/api/sql/base.py index 9f53cb8..6d79578 100644 --- a/transpose/src/api/sql/base.py +++ b/transpose/src/api/sql/base.py @@ -1,3 +1,4 @@ +from ...util.models import QueryResult from ....src.util.client import get_api_request, post_api_request @@ -11,8 +12,7 @@ def query( self, sql_query: str, parameters: dict = {}, - return_df: bool = False - ) -> dict: + ) -> QueryResult: url = "https://api.transpose.io/sql" body = { @@ -20,14 +20,15 @@ def query( 'parameters': parameters } - return post_api_request( + result = post_api_request( url=url, api_key=self.super.api_key, body=body, - return_df=return_df, verbose=self.super.verbose ) + return QueryResult(result) + # Gets the schema from the Transpose API def schema(self) -> dict: diff --git a/transpose/src/util/client.py b/transpose/src/util/client.py index 14b67c3..cfc139c 100644 --- a/transpose/src/util/client.py +++ b/transpose/src/util/client.py @@ -1,9 +1,5 @@ import json -from typing import Union - import requests -import pandas as pd -from pandas import DataFrame from transpose.src.util.errors import raise_custom_error @@ -16,22 +12,14 @@ def build_headers(api_key: str) -> dict: } -def handle_response(request: requests.Response, return_df: bool = False) -> Union[dict, DataFrame]: - if request.status_code == 200: - - # return the response as a DataFrame - if return_df: - - # check if pandas is installed - if not pd: - raise ImportError("Pandas is not installed. Please install pandas to use this feature.") - - return pd.DataFrame(request.json()['results']) +def handle_response(request: requests.Response) -> dict: + if request.status_code == 200: # return the response as a dictionary return request.json() else: + raise_custom_error(request.status_code, request.json()['message']) @@ -40,9 +28,8 @@ def get_api_request( api_key: str, body: dict = None, params=None, - return_df: bool = False, verbose: bool = False -) -> Union[dict, DataFrame]: +) -> dict: # set body/parameters to an empty dictionary if not provided body = {} if body is None else body @@ -60,7 +47,7 @@ def get_api_request( params=params ) - return handle_response(request, return_df) + return handle_response(request) def post_api_request( @@ -68,9 +55,8 @@ def post_api_request( api_key: str, body: dict, params=None, - return_df: bool = False, verbose: bool = False -) -> Union[dict, DataFrame]: +) -> dict: # set body/parameters to an empty dictionary if not provided body = {} if body is None else body @@ -88,4 +74,4 @@ def post_api_request( params=params ) - return handle_response(request, return_df) + return handle_response(request) diff --git a/transpose/src/util/models.py b/transpose/src/util/models.py index 89c03c9..74bd2a7 100644 --- a/transpose/src/util/models.py +++ b/transpose/src/util/models.py @@ -1,9 +1,8 @@ -import io -import json -import base64 - from typing import List +import pandas as pd + + # add a .to_dict and .__dict__ method to the list base class class list(list): def to_dict(self): @@ -11,8 +10,33 @@ def to_dict(self): def __dict__(self): return [obj.to_dict() for obj in self] - + +class QueryResult: + def __init__(self, result): + self.result = result + + def toPandas(self): + # Assumes the result is a dictionary and the data is in the 'results' key + return pd.DataFrame(self.result['results']) + + def keys(self): + return self.result.keys() + + def values(self): + return self.result.values() + + def __getitem__(self, key): + return self.result[key] + + def __setitem__(self, key, value): + self.result[key] = value + + def __delitem__(self, key): + del self.result[key] + + def __iter__(self): + return iter(self.result) # these are used in static typing so we can return useful tooltips for users # and allow for proper type checking and syntax highlighting From acc354b9f2d7e5a0a49c6982534e6cfed593002e Mon Sep 17 00:00:00 2001 From: Patrick Croke <86120203+tannishmango@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:02:08 -0700 Subject: [PATCH 3/3] add stringify_list function --- README.md | 11 +++++++++++ transpose/src/util/format.py | 4 ++++ 2 files changed, 15 insertions(+) create mode 100644 transpose/src/util/format.py diff --git a/README.md b/README.md index dd7186c..72c1d35 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,17 @@ You can find specific documentation on a per-product basis below. ## SDK Documentation You can learn more about the Transpose SDK and how it works below. +### SDK Helpers + +#### stringify_list +Converts a list of strings into a single string, separated by a delimiter. + +```python +from transpose.src.util.format import stringify_list + +stringify_list(['a', 'b', 'c'], ',') +>>> 'a,b,c' +``` ### SDK Classes The Transpose SDK uses custom classes to represent API responses: diff --git a/transpose/src/util/format.py b/transpose/src/util/format.py new file mode 100644 index 0000000..b51444b --- /dev/null +++ b/transpose/src/util/format.py @@ -0,0 +1,4 @@ + + +def stringify_list(data: list[str], delimiter: str = ",") -> str: + return delimiter.join([f"'{x}'" for x in data])