octostar-python-client 0.1.759__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- octostar/__init__.py +9 -0
- octostar/api/__init__.py +1 -0
- octostar/api/apps/__init__.py +0 -0
- octostar/api/apps/deploy_app.py +210 -0
- octostar/api/apps/execute_app_job.py +188 -0
- octostar/api/apps/get_app_logs.py +210 -0
- octostar/api/apps/get_apps_url.py +188 -0
- octostar/api/apps/get_job_logs.py +210 -0
- octostar/api/apps/get_job_progress.py +162 -0
- octostar/api/apps/kill_job.py +160 -0
- octostar/api/apps/list_app_jobs.py +276 -0
- octostar/api/apps/list_apps.py +251 -0
- octostar/api/apps/set_job_progress.py +216 -0
- octostar/api/apps/undeploy_app.py +160 -0
- octostar/api/metadata/__init__.py +0 -0
- octostar/api/metadata/get_version.py +232 -0
- octostar/api/metadata/get_whoami.py +232 -0
- octostar/api/notifications/__init__.py +0 -0
- octostar/api/notifications/delete_stream.py +222 -0
- octostar/api/notifications/get_subscriptions.py +240 -0
- octostar/api/notifications/publish_notification.py +275 -0
- octostar/api/notifications/pull_events_from_stream.py +282 -0
- octostar/api/notifications/push_event_to_stream.py +265 -0
- octostar/api/notifications/toast.py +264 -0
- octostar/api/ontology/__init__.py +0 -0
- octostar/api/ontology/fetch_ontology_data.py +275 -0
- octostar/api/ontology/get_ontologies.py +237 -0
- octostar/api/ontology/multi_query.py +297 -0
- octostar/api/ontology/query.py +276 -0
- octostar/api/pipeline/__init__.py +1 -0
- octostar/api/pipeline/get_processing_status.py +185 -0
- octostar/api/pipeline/update_processing_status.py +164 -0
- octostar/api/search/__init__.py +0 -0
- octostar/api/search/get_annotations.py +153 -0
- octostar/api/workspace_data/__init__.py +0 -0
- octostar/api/workspace_data/delete_blob.py +212 -0
- octostar/api/workspace_data/delete_entities.py +326 -0
- octostar/api/workspace_data/download_blob.py +235 -0
- octostar/api/workspace_data/get_attachment.py +336 -0
- octostar/api/workspace_data/get_files_tree.py +397 -0
- octostar/api/workspace_data/upload_blob.py +235 -0
- octostar/api/workspace_data/upsert_entities.py +284 -0
- octostar/api/workspace_permissions/__init__.py +0 -0
- octostar/api/workspace_permissions/get_permissions.py +325 -0
- octostar/api/workspace_tags/__init__.py +0 -0
- octostar/api/workspace_tags/delete_tag_from_entities.py +141 -0
- octostar/api/workspace_tags/tag_entities.py +180 -0
- octostar/client.py +492 -0
- octostar/errors.py +50 -0
- octostar/models/__init__.py +249 -0
- octostar/models/acknowledgement.py +74 -0
- octostar/models/acknowledgement_with_data.py +82 -0
- octostar/models/app_status.py +239 -0
- octostar/models/app_status_annotations.py +66 -0
- octostar/models/app_status_labels.py +69 -0
- octostar/models/app_with_url.py +82 -0
- octostar/models/child_processing_status.py +118 -0
- octostar/models/delete_entities_response_401.py +74 -0
- octostar/models/delete_entities_response_409.py +82 -0
- octostar/models/delete_entities_response_500.py +82 -0
- octostar/models/delete_stream_response_401.py +74 -0
- octostar/models/delete_tag_from_entities_response_401.py +74 -0
- octostar/models/deploy_app_json_body.py +90 -0
- octostar/models/deploy_app_json_body_secrets.py +65 -0
- octostar/models/deploy_app_response_200.py +98 -0
- octostar/models/deploy_app_response_200_data.py +60 -0
- octostar/models/deploy_app_response_400.py +82 -0
- octostar/models/deploy_app_response_403.py +82 -0
- octostar/models/deploy_app_response_404.py +82 -0
- octostar/models/deploy_app_response_409.py +82 -0
- octostar/models/deploy_app_response_500.py +82 -0
- octostar/models/entity.py +80 -0
- octostar/models/entity_response.py +99 -0
- octostar/models/entity_response_s3_urls.py +93 -0
- octostar/models/entity_response_s3_urls_additional_property.py +105 -0
- octostar/models/entity_response_s3_urls_additional_property_fields.py +114 -0
- octostar/models/execute_app_job_json_body.py +151 -0
- octostar/models/execute_app_job_json_body_annotation.py +65 -0
- octostar/models/execute_app_job_response_401.py +74 -0
- octostar/models/fetch_ontology_data_response_200.py +60 -0
- octostar/models/fetch_ontology_data_response_401.py +74 -0
- octostar/models/fetch_ontology_data_response_500.py +82 -0
- octostar/models/get_app_logs_response_401.py +74 -0
- octostar/models/get_app_logs_response_404.py +74 -0
- octostar/models/get_app_logs_response_500.py +82 -0
- octostar/models/get_apps_url_json_body.py +76 -0
- octostar/models/get_apps_url_response_401.py +74 -0
- octostar/models/get_apps_url_response_500.py +82 -0
- octostar/models/get_attachment_response_200.py +74 -0
- octostar/models/get_attachment_response_401.py +74 -0
- octostar/models/get_files_tree_response_200.py +106 -0
- octostar/models/get_files_tree_response_200_status.py +8 -0
- octostar/models/get_files_tree_response_400.py +111 -0
- octostar/models/get_files_tree_response_400_data.py +60 -0
- octostar/models/get_files_tree_response_400_status.py +8 -0
- octostar/models/get_files_tree_response_401.py +74 -0
- octostar/models/get_files_tree_response_500.py +111 -0
- octostar/models/get_files_tree_response_500_data.py +60 -0
- octostar/models/get_files_tree_response_500_status.py +8 -0
- octostar/models/get_job_logs_response_401.py +74 -0
- octostar/models/get_job_logs_response_404.py +74 -0
- octostar/models/get_job_logs_response_500.py +82 -0
- octostar/models/get_job_progress_response_401.py +74 -0
- octostar/models/get_object_response_401.py +74 -0
- octostar/models/get_ontologies_response_401.py +74 -0
- octostar/models/get_ontologies_response_500.py +81 -0
- octostar/models/get_permissions_response_200.py +98 -0
- octostar/models/get_permissions_response_400.py +82 -0
- octostar/models/get_permissions_response_401.py +74 -0
- octostar/models/get_permissions_response_500.py +82 -0
- octostar/models/get_processing_status_response_200.py +104 -0
- octostar/models/get_processing_status_response_200_data.py +87 -0
- octostar/models/get_processing_status_response_400.py +82 -0
- octostar/models/get_processing_status_response_500.py +82 -0
- octostar/models/get_subscriptions_response_200_item.py +74 -0
- octostar/models/get_version_response_200.py +74 -0
- octostar/models/get_version_response_404.py +74 -0
- octostar/models/get_whoami_response_200.py +129 -0
- octostar/models/get_whoami_response_401.py +74 -0
- octostar/models/insert_entity.py +114 -0
- octostar/models/insert_entity_base.py +266 -0
- octostar/models/insert_entity_relationships_item.py +107 -0
- octostar/models/insert_entity_request.py +94 -0
- octostar/models/internal_server_error.py +82 -0
- octostar/models/job_execution_result.py +146 -0
- octostar/models/job_status.py +196 -0
- octostar/models/job_status_labels.py +60 -0
- octostar/models/job_with_url.py +82 -0
- octostar/models/kill_job_response_401.py +74 -0
- octostar/models/list_app_jobs_response_401.py +74 -0
- octostar/models/list_app_jobs_response_500.py +82 -0
- octostar/models/list_apps_response_401.py +74 -0
- octostar/models/list_apps_response_500.py +82 -0
- octostar/models/multi_query_json_body.py +100 -0
- octostar/models/multi_query_json_body_queries_item.py +80 -0
- octostar/models/multi_query_response_400.py +82 -0
- octostar/models/multi_query_response_401.py +74 -0
- octostar/models/not_found_error.py +74 -0
- octostar/models/octostar_event.py +96 -0
- octostar/models/octostar_event_octostar_payload.py +100 -0
- octostar/models/octostar_event_octostar_payload_level.py +11 -0
- octostar/models/os_notification.py +122 -0
- octostar/models/processing_status.py +262 -0
- octostar/models/processing_status_code.py +14 -0
- octostar/models/progress_request.py +73 -0
- octostar/models/publish_notification_response_401.py +74 -0
- octostar/models/pull_events_from_stream_response_401.py +74 -0
- octostar/models/push_event_to_stream_response_401.py +74 -0
- octostar/models/query_json_body.py +101 -0
- octostar/models/query_json_body_params.py +60 -0
- octostar/models/query_response_400.py +82 -0
- octostar/models/query_response_401.py +74 -0
- octostar/models/set_job_progress_response_401.py +74 -0
- octostar/models/string_to_value_label_map.py +99 -0
- octostar/models/string_to_value_label_map_data.py +89 -0
- octostar/models/string_to_value_label_map_data_additional_property.py +80 -0
- octostar/models/successful_get_tags.py +103 -0
- octostar/models/successful_insertion.py +98 -0
- octostar/models/tag_entities_response_401.py +74 -0
- octostar/models/toast_level.py +11 -0
- octostar/models/toast_response_401.py +74 -0
- octostar/models/undeploy_app_response_401.py +74 -0
- octostar/models/update_processing_status_response_200.py +82 -0
- octostar/models/update_processing_status_response_400.py +82 -0
- octostar/models/update_processing_status_response_500.py +82 -0
- octostar/models/upsert_entities_response_401.py +74 -0
- octostar/models/upsert_entity.py +114 -0
- octostar/models/upsert_entity_base.py +266 -0
- octostar/models/upsert_entity_relationships_item.py +107 -0
- octostar/py.typed +1 -0
- octostar/types.py +54 -0
- octostar/utils/__init__.py +15 -0
- octostar/utils/chat/__init__.py +0 -0
- octostar/utils/chat/chat.py +513 -0
- octostar/utils/chat/detokenize.py +105 -0
- octostar/utils/chat/get_default_model.py +50 -0
- octostar/utils/chat/list_models.py +91 -0
- octostar/utils/chat/tokenize.py +105 -0
- octostar/utils/commons.py +226 -0
- octostar/utils/exceptions.py +134 -0
- octostar/utils/jobs/__init__.py +0 -0
- octostar/utils/jobs/apps/__init__.py +0 -0
- octostar/utils/jobs/apps/deploy_app.py +81 -0
- octostar/utils/jobs/apps/execute_app_job.py +114 -0
- octostar/utils/jobs/apps/get_app_logs.py +113 -0
- octostar/utils/jobs/apps/get_app_secret.py +102 -0
- octostar/utils/jobs/apps/get_apps_url.py +73 -0
- octostar/utils/jobs/apps/list_app_jobs.py +62 -0
- octostar/utils/jobs/apps/list_apps.py +126 -0
- octostar/utils/jobs/apps/undeploy_app.py +48 -0
- octostar/utils/jobs/get_job_logs.py +113 -0
- octostar/utils/jobs/get_job_progress.py +76 -0
- octostar/utils/jobs/kill_job.py +47 -0
- octostar/utils/jobs/set_job_progress.py +67 -0
- octostar/utils/meta/__init__.py +0 -0
- octostar/utils/meta/get_version.py +30 -0
- octostar/utils/meta/get_whoami.py +30 -0
- octostar/utils/notifications/__init__.py +0 -0
- octostar/utils/notifications/delete_stream.py +58 -0
- octostar/utils/notifications/get_my_subscriptions.py +49 -0
- octostar/utils/notifications/publish_notification.py +73 -0
- octostar/utils/notifications/pull_event_from_stream.py +63 -0
- octostar/utils/notifications/pull_events_from_stream.py +64 -0
- octostar/utils/notifications/push_event_to_stream.py +109 -0
- octostar/utils/notifications/push_events_to_stream.py +137 -0
- octostar/utils/notifications/toast.py +92 -0
- octostar/utils/ontology/__init__.py +10 -0
- octostar/utils/ontology/fetch_ontology_data.py +141 -0
- octostar/utils/ontology/get_ontologies.py +55 -0
- octostar/utils/ontology/multiquery_ontology.py +287 -0
- octostar/utils/ontology/query_ontology.py +186 -0
- octostar/utils/pipeline/__init__.py +1 -0
- octostar/utils/pipeline/get_processing_status.py +230 -0
- octostar/utils/pipeline/update_processing_status.py +286 -0
- octostar/utils/search/__init__.py +11 -0
- octostar/utils/search/bulk_update.py +138 -0
- octostar/utils/search/count.py +117 -0
- octostar/utils/search/get_entity_annotations.py +304 -0
- octostar/utils/search/get_index_definition.py +111 -0
- octostar/utils/search/multi_search.py +129 -0
- octostar/utils/workspace/__init__.py +0 -0
- octostar/utils/workspace/delete_entities.py +247 -0
- octostar/utils/workspace/delete_entity.py +81 -0
- octostar/utils/workspace/delete_relationship.py +78 -0
- octostar/utils/workspace/delete_relationships.py +85 -0
- octostar/utils/workspace/delete_temporary_blob.py +85 -0
- octostar/utils/workspace/extract_entities.py +140 -0
- octostar/utils/workspace/get_filepath_from_item.py +85 -0
- octostar/utils/workspace/get_filepaths_from_items.py +100 -0
- octostar/utils/workspace/get_files_tree.py +102 -0
- octostar/utils/workspace/get_item_from_filepath.py +102 -0
- octostar/utils/workspace/get_items_from_filepaths.py +108 -0
- octostar/utils/workspace/linkcharts/__init__.py +0 -0
- octostar/utils/workspace/linkcharts/create_linkchart.py +241 -0
- octostar/utils/workspace/permissions/PermissionLevel.py +8 -0
- octostar/utils/workspace/permissions/__init__.py +1 -0
- octostar/utils/workspace/permissions/get_permissions.py +81 -0
- octostar/utils/workspace/read_attachment.py +284 -0
- octostar/utils/workspace/read_file.py +113 -0
- octostar/utils/workspace/read_temporary_blob.py +428 -0
- octostar/utils/workspace/saved_searches/__init__.py +0 -0
- octostar/utils/workspace/saved_searches/create_saved_search.py +183 -0
- octostar/utils/workspace/tags/__init__.py +0 -0
- octostar/utils/workspace/tags/delete_tag_from_entities.py +96 -0
- octostar/utils/workspace/tags/tag_entities.py +175 -0
- octostar/utils/workspace/upsert_entities.py +268 -0
- octostar/utils/workspace/upsert_entity.py +110 -0
- octostar/utils/workspace/upsert_relationship.py +128 -0
- octostar/utils/workspace/upsert_relationships.py +194 -0
- octostar/utils/workspace/write_attachment.py +263 -0
- octostar/utils/workspace/write_file.py +335 -0
- octostar/utils/workspace/write_temporary_blob.py +218 -0
- octostar_python_client-0.1.759.dist-info/METADATA +159 -0
- octostar_python_client-0.1.759.dist-info/RECORD +257 -0
- octostar_python_client-0.1.759.dist-info/WHEEL +5 -0
- octostar_python_client-0.1.759.dist-info/licenses/LICENSE +21 -0
- octostar_python_client-0.1.759.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import sys
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Optional, Union, List, Dict
|
|
6
|
+
from collections.abc import AsyncIterator
|
|
7
|
+
|
|
8
|
+
from ...api.ontology import multi_query
|
|
9
|
+
from ...models.multi_query_json_body import MultiQueryJsonBody
|
|
10
|
+
from ...models.multi_query_json_body_queries_item import MultiQueryJsonBodyQueriesItem
|
|
11
|
+
from ...client import Client
|
|
12
|
+
from ...types import Response
|
|
13
|
+
from ..exceptions import StopAsyncIterationWithResult
|
|
14
|
+
from ..commons import (
|
|
15
|
+
sync_request,
|
|
16
|
+
asyncio_request,
|
|
17
|
+
streaming_request,
|
|
18
|
+
FailCondition,
|
|
19
|
+
ChunksEndCondition,
|
|
20
|
+
ChunksFailCondition,
|
|
21
|
+
streaming_get_full_content,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def sync(
|
|
28
|
+
sql_queries: Union[List[str], Dict[str, str]],
|
|
29
|
+
raise_on_missing: bool = False,
|
|
30
|
+
ontology_name: str = os.getenv("OS_ONTOLOGY"),
|
|
31
|
+
client: Client = None,
|
|
32
|
+
) -> Union[List[str], Dict[str, str]]:
|
|
33
|
+
"""
|
|
34
|
+
Query an ontology with multiple queries (batching) for entities or data.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
sql_queries: The queries to send, expressed either as:
|
|
38
|
+
A list of strings which are valid timbr/SQL queries, OR
|
|
39
|
+
A dictionary of arbitrary query IDs to valid timbr/SQL queries.
|
|
40
|
+
raise_on_missing: Whether to raise a ValueError if some of the queries error out. If not set, invalid queries will correspond to None in the output.
|
|
41
|
+
ontology_name: The name of the ontology.
|
|
42
|
+
client: The Client with which to connect to Octostar. If None, the default one is used.
|
|
43
|
+
Returns:
|
|
44
|
+
A list of elements or a dictionary, as per the input.
|
|
45
|
+
Each element is the result of a query expressed as a list of dictionaries,
|
|
46
|
+
each containing a result row with named columns.
|
|
47
|
+
The result of each query can be passed e.g. to pd.DataFrame().
|
|
48
|
+
If some queries have failed, the corresponding elements are None.
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
ApiConnectionError: If the overall query batch was unsuccessful.
|
|
52
|
+
ValueError: If some queries in the batch have failed and raise_on_missing is set.
|
|
53
|
+
"""
|
|
54
|
+
return_as_list = False
|
|
55
|
+
if isinstance(sql_queries, list):
|
|
56
|
+
return_as_list = True
|
|
57
|
+
sql_queries = {str(i): sql_queries[i] for i in range(len(sql_queries))}
|
|
58
|
+
body = MultiQueryJsonBody(
|
|
59
|
+
queries=[
|
|
60
|
+
MultiQueryJsonBodyQueriesItem(query_id=str(id), sql=query)
|
|
61
|
+
for id, query in sql_queries.items()
|
|
62
|
+
]
|
|
63
|
+
)
|
|
64
|
+
fail_condition = FailCondition(callable=lambda resp: resp.status_code != 200)
|
|
65
|
+
response = sync_request(
|
|
66
|
+
client=client,
|
|
67
|
+
module=multi_query,
|
|
68
|
+
fail_condition=fail_condition,
|
|
69
|
+
json_body=body,
|
|
70
|
+
ontology=ontology_name,
|
|
71
|
+
)
|
|
72
|
+
result = [
|
|
73
|
+
json.loads(r)
|
|
74
|
+
for r in response.content.decode("utf-8", errors="ignore").split("\n")
|
|
75
|
+
if r.strip()
|
|
76
|
+
]
|
|
77
|
+
result = {r["query_id"]: r for r in result}
|
|
78
|
+
error_result = {
|
|
79
|
+
rk: rv
|
|
80
|
+
for rk, rv in result.items()
|
|
81
|
+
if "status" not in rv.keys()
|
|
82
|
+
or rv["status"] != "success"
|
|
83
|
+
or "data" not in rv
|
|
84
|
+
or not isinstance(rv["data"], list)
|
|
85
|
+
}
|
|
86
|
+
if raise_on_missing and any(error_result):
|
|
87
|
+
raise ValueError(
|
|
88
|
+
"Some of the queries in multiquery_ontology failed! "
|
|
89
|
+
+ str(list(error_result.values()))
|
|
90
|
+
)
|
|
91
|
+
error_result = {rk: {} for rk in error_result.keys()}
|
|
92
|
+
result = {**result, **error_result}
|
|
93
|
+
result = {rk: rv.get("data") for rk, rv in result.items()}
|
|
94
|
+
result = {**{rk: None for rk in sql_queries.keys()}, **result}
|
|
95
|
+
if return_as_list:
|
|
96
|
+
result = [x[1] for x in sorted(result.items(), key=lambda x: x[0])]
|
|
97
|
+
return result
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
async def asyncio(
|
|
101
|
+
sql_queries: Union[List[str], Dict[str, str]],
|
|
102
|
+
raise_on_missing: bool = False,
|
|
103
|
+
ontology_name: str = os.getenv("OS_ONTOLOGY"),
|
|
104
|
+
client: Client = None,
|
|
105
|
+
) -> Union[List[str], Dict[str, str]]:
|
|
106
|
+
"""
|
|
107
|
+
Query asynchronously an ontology with multiple queries (batching) for entities or data.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
sql_queries: The queries to send, expressed either as:
|
|
111
|
+
A list of strings which are valid timbr/SQL queries, OR
|
|
112
|
+
A dictionary of arbitrary query IDs to valid timbr/SQL queries.
|
|
113
|
+
raise_on_missing: Whether to raise a ValueError if some of the queries error out. If not set, invalid queries will correspond to None in the output.
|
|
114
|
+
ontology_name: The name of the ontology.
|
|
115
|
+
client: The Client with which to connect to Octostar. If None, the default one is used.
|
|
116
|
+
Returns:
|
|
117
|
+
A list of elements or a dictionary, as per the input.
|
|
118
|
+
Each element is the result of a query expressed as a list of dictionaries,
|
|
119
|
+
each containing a result row with named columns.
|
|
120
|
+
The result of each query can be passed e.g. to pd.DataFrame().
|
|
121
|
+
If some queries have failed, the corresponding elements are None.
|
|
122
|
+
|
|
123
|
+
Raises:
|
|
124
|
+
ApiConnectionError: If the overall query batch was unsuccessful.
|
|
125
|
+
ValueError: If some queries in the batch have failed and raise_on_missing is set.
|
|
126
|
+
"""
|
|
127
|
+
return_as_list = False
|
|
128
|
+
if isinstance(sql_queries, list):
|
|
129
|
+
return_as_list = True
|
|
130
|
+
sql_queries = {str(i): sql_queries[i] for i in range(len(sql_queries))}
|
|
131
|
+
body = MultiQueryJsonBody(
|
|
132
|
+
queries=[
|
|
133
|
+
MultiQueryJsonBodyQueriesItem(query_id=str(id), sql=query)
|
|
134
|
+
for id, query in sql_queries.items()
|
|
135
|
+
]
|
|
136
|
+
)
|
|
137
|
+
fail_condition = FailCondition(callable=lambda resp: resp.status_code != 200)
|
|
138
|
+
response = await asyncio_request(
|
|
139
|
+
client=client,
|
|
140
|
+
module=multi_query,
|
|
141
|
+
fail_condition=fail_condition,
|
|
142
|
+
json_body=body,
|
|
143
|
+
ontology=ontology_name,
|
|
144
|
+
)
|
|
145
|
+
result = [
|
|
146
|
+
json.loads(r)
|
|
147
|
+
for r in response.content.decode("utf-8", errors="ignore").split("\n")
|
|
148
|
+
if r.strip()
|
|
149
|
+
]
|
|
150
|
+
result = {r["query_id"]: r for r in result}
|
|
151
|
+
error_result = {
|
|
152
|
+
rk: rv
|
|
153
|
+
for rk, rv in result.items()
|
|
154
|
+
if "status" not in rv.keys()
|
|
155
|
+
or rv["status"] != "success"
|
|
156
|
+
or "data" not in rv
|
|
157
|
+
or not isinstance(rv["data"], list)
|
|
158
|
+
}
|
|
159
|
+
if raise_on_missing and any(error_result):
|
|
160
|
+
raise ValueError(
|
|
161
|
+
"Some of the queries in multiquery_ontology failed! "
|
|
162
|
+
+ str(list(error_result.values()))
|
|
163
|
+
)
|
|
164
|
+
error_result = {rk: {} for rk in error_result.keys()}
|
|
165
|
+
result = {**result, **error_result}
|
|
166
|
+
result = {rk: rv.get("data") for rk, rv in result.items()}
|
|
167
|
+
result = {**{rk: None for rk in sql_queries.keys()}, **result}
|
|
168
|
+
if return_as_list:
|
|
169
|
+
result = [x[1] for x in sorted(result.items(), key=lambda x: x[0])]
|
|
170
|
+
return result
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
async def streaming(
|
|
174
|
+
sql_queries: Union[List[str], Dict[str, str]],
|
|
175
|
+
raise_on_missing: bool = False,
|
|
176
|
+
ontology_name: str = os.getenv("OS_ONTOLOGY"),
|
|
177
|
+
client: Client = None,
|
|
178
|
+
timeout: float = None,
|
|
179
|
+
fail_condition: Optional[ChunksFailCondition] = None,
|
|
180
|
+
end_condition: Optional[ChunksEndCondition] = None,
|
|
181
|
+
) -> AsyncIterator[Union[Response, Union[List[str], Dict[str, str]]]]:
|
|
182
|
+
"""
|
|
183
|
+
Query with results streaming an ontology with multiple queries (batching) for entities or data.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
sql_queries: The queries to send, expressed either as:
|
|
187
|
+
A list of strings which are valid timbr/SQL queries, OR
|
|
188
|
+
A dictionary of arbitrary query IDs to valid timbr/SQL queries.
|
|
189
|
+
raise_on_missing: Whether to raise a ValueError if some of the queries error out. If not set, invalid queries will correspond to None in the output.
|
|
190
|
+
ontology_name: The name of the ontology.
|
|
191
|
+
client: The Client with which to connect to Octostar. If None, the default one is used.
|
|
192
|
+
timeout: The amount of time after which to stop streaming results.
|
|
193
|
+
fail_condition: Custom condition to determine when the stream should be considered
|
|
194
|
+
failed. Receives the list of Response chunks collected so far and returns True
|
|
195
|
+
to trigger an ApiConnectionError. If None, defaults to checking for non-200
|
|
196
|
+
status codes (and error results when raise_on_missing is set).
|
|
197
|
+
end_condition: Custom condition to determine when the stream should stop early.
|
|
198
|
+
Receives the list of Response chunks collected so far and returns True to end
|
|
199
|
+
the stream. If None, defaults to streaming until completion (or timeout).
|
|
200
|
+
Returns:
|
|
201
|
+
Return raw responses corresponding to the server's response to each query, as they come.
|
|
202
|
+
Each chunk will not necessarily map to a specific query (they may be incomplete).
|
|
203
|
+
|
|
204
|
+
Catch StopAsyncIterationWithResult() to get the final parsed result from the stream:
|
|
205
|
+
a list of elements or a dictionary, as per the input.
|
|
206
|
+
Each element is the result of a query expressed as a list of dictionaries,
|
|
207
|
+
each containing a result row with named columns.
|
|
208
|
+
The result of each query can be passed e.g. to pd.DataFrame().
|
|
209
|
+
If some queries have failed or timed-out, the corresponding elements are None.
|
|
210
|
+
|
|
211
|
+
Raises:
|
|
212
|
+
ApiConnectionError: If the overall query batch was unsuccessful.
|
|
213
|
+
ValueError: If some queries in the batch have failed and raise_on_missing is set.
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
def _process_response_chunks(chunk_resps):
|
|
217
|
+
content = streaming_get_full_content(chunk_resps)
|
|
218
|
+
parsed = content.decode("utf-8", errors="ignore")
|
|
219
|
+
elements = [e.strip() for e in parsed.split("\n") if e.strip()]
|
|
220
|
+
result = []
|
|
221
|
+
for i in range(len(elements)):
|
|
222
|
+
try:
|
|
223
|
+
result.append(json.loads(elements[i]))
|
|
224
|
+
except json.JSONDecodeError:
|
|
225
|
+
break # we reached incomplete chunks, ignore them
|
|
226
|
+
result = {r["query_id"]: r for r in result}
|
|
227
|
+
return result
|
|
228
|
+
|
|
229
|
+
return_as_list = False
|
|
230
|
+
if isinstance(sql_queries, list):
|
|
231
|
+
return_as_list = True
|
|
232
|
+
sql_queries = {str(i): sql_queries[i] for i in range(len(sql_queries))}
|
|
233
|
+
body = MultiQueryJsonBody(
|
|
234
|
+
queries=[
|
|
235
|
+
MultiQueryJsonBodyQueriesItem(query_id=str(id), sql=query)
|
|
236
|
+
for id, query in sql_queries.items()
|
|
237
|
+
]
|
|
238
|
+
)
|
|
239
|
+
is_error_result = (
|
|
240
|
+
lambda rk, rv: "status" not in rv.keys()
|
|
241
|
+
or rv["status"] != "success"
|
|
242
|
+
or "data" not in rv
|
|
243
|
+
or not isinstance(rv["data"], list)
|
|
244
|
+
)
|
|
245
|
+
if fail_condition is None:
|
|
246
|
+
fail_condition = ChunksFailCondition(
|
|
247
|
+
callable=lambda resps: not resps
|
|
248
|
+
or resps[0].status_code != 200
|
|
249
|
+
or (
|
|
250
|
+
raise_on_missing
|
|
251
|
+
and any(
|
|
252
|
+
is_error_result(rk, rv)
|
|
253
|
+
for rk, rv in _process_response_chunks(resps).items()
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
if end_condition is None:
|
|
258
|
+
end_condition = ChunksEndCondition(callable=lambda _: False, timeout=timeout)
|
|
259
|
+
elif timeout is not None:
|
|
260
|
+
end_condition.timeout = timeout
|
|
261
|
+
try:
|
|
262
|
+
async for chunk in streaming_request(
|
|
263
|
+
client=client,
|
|
264
|
+
module=multi_query,
|
|
265
|
+
fail_condition=fail_condition,
|
|
266
|
+
end_condition=end_condition,
|
|
267
|
+
json_body=body,
|
|
268
|
+
ontology=ontology_name,
|
|
269
|
+
):
|
|
270
|
+
yield chunk
|
|
271
|
+
except StopAsyncIterationWithResult as r:
|
|
272
|
+
result = r.value
|
|
273
|
+
result = _process_response_chunks(result)
|
|
274
|
+
error_result = {
|
|
275
|
+
rk: {}
|
|
276
|
+
for rk, rv in result.items()
|
|
277
|
+
if "status" not in rv.keys()
|
|
278
|
+
or rv["status"] != "success"
|
|
279
|
+
or "data" not in rv
|
|
280
|
+
or not isinstance(rv["data"], list)
|
|
281
|
+
}
|
|
282
|
+
result = {**result, **error_result}
|
|
283
|
+
result = {rk: rv.get("data") for rk, rv in result.items()}
|
|
284
|
+
result = {**{rk: None for rk in sql_queries.keys()}, **result}
|
|
285
|
+
if return_as_list:
|
|
286
|
+
result = [x[1] for x in sorted(result.items(), key=lambda x: x[0])]
|
|
287
|
+
raise StopAsyncIterationWithResult(value=result)
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# General SQL queries on one ontology
|
|
3
|
+
Use this to query your ontology for entities or data. The syntax is based on timbr/SQL. For more information, see the "fusion centre" documentation.
|
|
4
|
+
|
|
5
|
+
Example of a query:
|
|
6
|
+
```sql
|
|
7
|
+
SELECT * FROM os_thing WHERE os_workspace = 'my_workspace';
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
from typing import List, Dict, Optional, Any, Union
|
|
16
|
+
|
|
17
|
+
from ...api.ontology import query
|
|
18
|
+
from ...models.query_json_body import QueryJsonBody
|
|
19
|
+
from ...client import Client, get_default_client
|
|
20
|
+
from ..exceptions import ApiConnectionError
|
|
21
|
+
|
|
22
|
+
_logger = logging.getLogger(__name__)
|
|
23
|
+
_CONTEXT_NAME = "query_ontology"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _process_query_response(
|
|
27
|
+
response, sql_query: str, client: Client
|
|
28
|
+
) -> Optional[List[Dict[str, Any]]]:
|
|
29
|
+
if response is None:
|
|
30
|
+
_logger.error(f"{_CONTEXT_NAME}: Response is None for query {sql_query}")
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
# Check if content exists
|
|
34
|
+
if not hasattr(response, "content") or response.content is None:
|
|
35
|
+
_logger.error(
|
|
36
|
+
f"{_CONTEXT_NAME}: No content available in response for query {sql_query}"
|
|
37
|
+
)
|
|
38
|
+
raise ApiConnectionError(_CONTEXT_NAME, response, client)
|
|
39
|
+
|
|
40
|
+
# Log the raw response content
|
|
41
|
+
_logger.debug(
|
|
42
|
+
f"{_CONTEXT_NAME}: Response content for query {sql_query}: {response.content}"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Check response status code
|
|
46
|
+
if (
|
|
47
|
+
hasattr(response, "status_code")
|
|
48
|
+
and response.status_code
|
|
49
|
+
and response.status_code.value != 200
|
|
50
|
+
):
|
|
51
|
+
_logger.error(
|
|
52
|
+
f"{_CONTEXT_NAME}: Failed with status code {response.status_code.value} for query {sql_query}"
|
|
53
|
+
)
|
|
54
|
+
raise ApiConnectionError(_CONTEXT_NAME, response, client)
|
|
55
|
+
|
|
56
|
+
response_content = response.content
|
|
57
|
+
|
|
58
|
+
# Handle empty content
|
|
59
|
+
if not response_content:
|
|
60
|
+
_logger.error(
|
|
61
|
+
f"{_CONTEXT_NAME}: Empty content in response for query {sql_query}"
|
|
62
|
+
)
|
|
63
|
+
raise ApiConnectionError(_CONTEXT_NAME, response, client)
|
|
64
|
+
|
|
65
|
+
# Handle the case where response_content is already a dictionary
|
|
66
|
+
if isinstance(response_content, dict):
|
|
67
|
+
content = response_content
|
|
68
|
+
else:
|
|
69
|
+
try:
|
|
70
|
+
# If it's a string, try to parse as JSON
|
|
71
|
+
if isinstance(response_content, str):
|
|
72
|
+
content = json.loads(response_content)
|
|
73
|
+
elif isinstance(response_content, bytes):
|
|
74
|
+
content = json.loads(response_content.decode("utf-8"))
|
|
75
|
+
else:
|
|
76
|
+
_logger.error(
|
|
77
|
+
f"{_CONTEXT_NAME}: Unexpected response content type: {type(response_content)}"
|
|
78
|
+
)
|
|
79
|
+
raise ValueError(
|
|
80
|
+
f"Unexpected response content type: {type(response_content)}"
|
|
81
|
+
)
|
|
82
|
+
except json.JSONDecodeError as e:
|
|
83
|
+
_logger.error(
|
|
84
|
+
f"{_CONTEXT_NAME}: Failed to parse JSON response: %s. Content: %s",
|
|
85
|
+
str(e),
|
|
86
|
+
str(response_content),
|
|
87
|
+
)
|
|
88
|
+
raise ValueError(
|
|
89
|
+
f"Invalid JSON response from API: {str(e)}. Content (truncated): {str(response_content)[:200]}"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if not content:
|
|
93
|
+
_logger.error(f"{_CONTEXT_NAME}: Empty parsed content for query {sql_query}")
|
|
94
|
+
raise ApiConnectionError(_CONTEXT_NAME, response, client)
|
|
95
|
+
|
|
96
|
+
# Get detailed error information if status is not success
|
|
97
|
+
if content.get("status") != "success":
|
|
98
|
+
error_message = content.get("message", "Unknown error")
|
|
99
|
+
error_details = content.get("error", {})
|
|
100
|
+
|
|
101
|
+
# Construct a detailed error message
|
|
102
|
+
detailed_msg = f"Request failed with status: {content.get('status')}. Message: {error_message}"
|
|
103
|
+
if error_details:
|
|
104
|
+
detailed_msg += f". Details: {json.dumps(error_details)}"
|
|
105
|
+
|
|
106
|
+
_logger.error(f"{_CONTEXT_NAME}: {detailed_msg} for query {sql_query}")
|
|
107
|
+
raise ApiConnectionError(_CONTEXT_NAME, response, client)
|
|
108
|
+
|
|
109
|
+
if (
|
|
110
|
+
isinstance(content, dict)
|
|
111
|
+
and "data" in content
|
|
112
|
+
and isinstance(content["data"], list)
|
|
113
|
+
):
|
|
114
|
+
return content["data"]
|
|
115
|
+
|
|
116
|
+
else:
|
|
117
|
+
_logger.debug(
|
|
118
|
+
f"{_CONTEXT_NAME}: Non-SELECT query executed successfully: %s", str(content)
|
|
119
|
+
)
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def sync(
|
|
124
|
+
sql_query: str, ontology_name: str = None, client: Client = None
|
|
125
|
+
) -> Optional[List[Dict[str, Any]]]:
|
|
126
|
+
"""
|
|
127
|
+
# Query Ontology for Entities or Data
|
|
128
|
+
|
|
129
|
+
Executes a query on an ontology to retrieve entities or data.
|
|
130
|
+
|
|
131
|
+
## Arguments
|
|
132
|
+
- `sql_query`: A string which is a valid timbr/SQL query.
|
|
133
|
+
- `ontology_name`: The name of the ontology.
|
|
134
|
+
- `client`: The Client with which to connect to Octostar. If None, the default one is used.
|
|
135
|
+
|
|
136
|
+
## Returns
|
|
137
|
+
- For SELECT queries: A list of dictionaries, each containing a result row with named columns.
|
|
138
|
+
The result can be passed e.g. to pd.DataFrame().
|
|
139
|
+
- For non-SELECT queries: None
|
|
140
|
+
|
|
141
|
+
## Raises
|
|
142
|
+
- `ApiConnectionError`: If the query was unsuccessful.
|
|
143
|
+
- `ValueError`: If the response cannot be properly parsed.
|
|
144
|
+
"""
|
|
145
|
+
if not ontology_name:
|
|
146
|
+
if not client:
|
|
147
|
+
client = get_default_client()
|
|
148
|
+
ontology_name = client.ontology
|
|
149
|
+
response = query.sync_detailed(
|
|
150
|
+
client=client, json_body=QueryJsonBody(query=sql_query), ontology=ontology_name
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
return _process_query_response(response, sql_query, client)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
async def asyncio(
|
|
157
|
+
sql_query: str, ontology_name: str = None, client: Client = None
|
|
158
|
+
) -> Optional[List[Dict[str, Any]]]:
|
|
159
|
+
"""
|
|
160
|
+
# Query Ontology for Entities or Data Asynchronously
|
|
161
|
+
|
|
162
|
+
Asynchronously executes a query on an ontology to retrieve entities or data.
|
|
163
|
+
|
|
164
|
+
## Arguments
|
|
165
|
+
- `sql_query`: A string which is a valid timbr/SQL query.
|
|
166
|
+
- `ontology_name`: The name of the ontology.
|
|
167
|
+
- `client`: The Client with which to connect to Octostar. If None, the default one is used.
|
|
168
|
+
|
|
169
|
+
## Returns
|
|
170
|
+
- For SELECT queries: A list of dictionaries, each containing a result row with named columns.
|
|
171
|
+
The result can be passed e.g. to pd.DataFrame().
|
|
172
|
+
- For non-SELECT queries: None
|
|
173
|
+
|
|
174
|
+
## Raises
|
|
175
|
+
- `ApiConnectionError`: If the query was unsuccessful.
|
|
176
|
+
- `ValueError`: If the response cannot be properly parsed.
|
|
177
|
+
"""
|
|
178
|
+
if not ontology_name:
|
|
179
|
+
if not client:
|
|
180
|
+
client = get_default_client()
|
|
181
|
+
ontology_name = client.ontology
|
|
182
|
+
response = await query.asyncio_detailed(
|
|
183
|
+
client=client, json_body=QueryJsonBody(query=sql_query), ontology=ontology_name
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
return _process_query_response(response, sql_query, client)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|