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.
Files changed (257) hide show
  1. octostar/__init__.py +9 -0
  2. octostar/api/__init__.py +1 -0
  3. octostar/api/apps/__init__.py +0 -0
  4. octostar/api/apps/deploy_app.py +210 -0
  5. octostar/api/apps/execute_app_job.py +188 -0
  6. octostar/api/apps/get_app_logs.py +210 -0
  7. octostar/api/apps/get_apps_url.py +188 -0
  8. octostar/api/apps/get_job_logs.py +210 -0
  9. octostar/api/apps/get_job_progress.py +162 -0
  10. octostar/api/apps/kill_job.py +160 -0
  11. octostar/api/apps/list_app_jobs.py +276 -0
  12. octostar/api/apps/list_apps.py +251 -0
  13. octostar/api/apps/set_job_progress.py +216 -0
  14. octostar/api/apps/undeploy_app.py +160 -0
  15. octostar/api/metadata/__init__.py +0 -0
  16. octostar/api/metadata/get_version.py +232 -0
  17. octostar/api/metadata/get_whoami.py +232 -0
  18. octostar/api/notifications/__init__.py +0 -0
  19. octostar/api/notifications/delete_stream.py +222 -0
  20. octostar/api/notifications/get_subscriptions.py +240 -0
  21. octostar/api/notifications/publish_notification.py +275 -0
  22. octostar/api/notifications/pull_events_from_stream.py +282 -0
  23. octostar/api/notifications/push_event_to_stream.py +265 -0
  24. octostar/api/notifications/toast.py +264 -0
  25. octostar/api/ontology/__init__.py +0 -0
  26. octostar/api/ontology/fetch_ontology_data.py +275 -0
  27. octostar/api/ontology/get_ontologies.py +237 -0
  28. octostar/api/ontology/multi_query.py +297 -0
  29. octostar/api/ontology/query.py +276 -0
  30. octostar/api/pipeline/__init__.py +1 -0
  31. octostar/api/pipeline/get_processing_status.py +185 -0
  32. octostar/api/pipeline/update_processing_status.py +164 -0
  33. octostar/api/search/__init__.py +0 -0
  34. octostar/api/search/get_annotations.py +153 -0
  35. octostar/api/workspace_data/__init__.py +0 -0
  36. octostar/api/workspace_data/delete_blob.py +212 -0
  37. octostar/api/workspace_data/delete_entities.py +326 -0
  38. octostar/api/workspace_data/download_blob.py +235 -0
  39. octostar/api/workspace_data/get_attachment.py +336 -0
  40. octostar/api/workspace_data/get_files_tree.py +397 -0
  41. octostar/api/workspace_data/upload_blob.py +235 -0
  42. octostar/api/workspace_data/upsert_entities.py +284 -0
  43. octostar/api/workspace_permissions/__init__.py +0 -0
  44. octostar/api/workspace_permissions/get_permissions.py +325 -0
  45. octostar/api/workspace_tags/__init__.py +0 -0
  46. octostar/api/workspace_tags/delete_tag_from_entities.py +141 -0
  47. octostar/api/workspace_tags/tag_entities.py +180 -0
  48. octostar/client.py +492 -0
  49. octostar/errors.py +50 -0
  50. octostar/models/__init__.py +249 -0
  51. octostar/models/acknowledgement.py +74 -0
  52. octostar/models/acknowledgement_with_data.py +82 -0
  53. octostar/models/app_status.py +239 -0
  54. octostar/models/app_status_annotations.py +66 -0
  55. octostar/models/app_status_labels.py +69 -0
  56. octostar/models/app_with_url.py +82 -0
  57. octostar/models/child_processing_status.py +118 -0
  58. octostar/models/delete_entities_response_401.py +74 -0
  59. octostar/models/delete_entities_response_409.py +82 -0
  60. octostar/models/delete_entities_response_500.py +82 -0
  61. octostar/models/delete_stream_response_401.py +74 -0
  62. octostar/models/delete_tag_from_entities_response_401.py +74 -0
  63. octostar/models/deploy_app_json_body.py +90 -0
  64. octostar/models/deploy_app_json_body_secrets.py +65 -0
  65. octostar/models/deploy_app_response_200.py +98 -0
  66. octostar/models/deploy_app_response_200_data.py +60 -0
  67. octostar/models/deploy_app_response_400.py +82 -0
  68. octostar/models/deploy_app_response_403.py +82 -0
  69. octostar/models/deploy_app_response_404.py +82 -0
  70. octostar/models/deploy_app_response_409.py +82 -0
  71. octostar/models/deploy_app_response_500.py +82 -0
  72. octostar/models/entity.py +80 -0
  73. octostar/models/entity_response.py +99 -0
  74. octostar/models/entity_response_s3_urls.py +93 -0
  75. octostar/models/entity_response_s3_urls_additional_property.py +105 -0
  76. octostar/models/entity_response_s3_urls_additional_property_fields.py +114 -0
  77. octostar/models/execute_app_job_json_body.py +151 -0
  78. octostar/models/execute_app_job_json_body_annotation.py +65 -0
  79. octostar/models/execute_app_job_response_401.py +74 -0
  80. octostar/models/fetch_ontology_data_response_200.py +60 -0
  81. octostar/models/fetch_ontology_data_response_401.py +74 -0
  82. octostar/models/fetch_ontology_data_response_500.py +82 -0
  83. octostar/models/get_app_logs_response_401.py +74 -0
  84. octostar/models/get_app_logs_response_404.py +74 -0
  85. octostar/models/get_app_logs_response_500.py +82 -0
  86. octostar/models/get_apps_url_json_body.py +76 -0
  87. octostar/models/get_apps_url_response_401.py +74 -0
  88. octostar/models/get_apps_url_response_500.py +82 -0
  89. octostar/models/get_attachment_response_200.py +74 -0
  90. octostar/models/get_attachment_response_401.py +74 -0
  91. octostar/models/get_files_tree_response_200.py +106 -0
  92. octostar/models/get_files_tree_response_200_status.py +8 -0
  93. octostar/models/get_files_tree_response_400.py +111 -0
  94. octostar/models/get_files_tree_response_400_data.py +60 -0
  95. octostar/models/get_files_tree_response_400_status.py +8 -0
  96. octostar/models/get_files_tree_response_401.py +74 -0
  97. octostar/models/get_files_tree_response_500.py +111 -0
  98. octostar/models/get_files_tree_response_500_data.py +60 -0
  99. octostar/models/get_files_tree_response_500_status.py +8 -0
  100. octostar/models/get_job_logs_response_401.py +74 -0
  101. octostar/models/get_job_logs_response_404.py +74 -0
  102. octostar/models/get_job_logs_response_500.py +82 -0
  103. octostar/models/get_job_progress_response_401.py +74 -0
  104. octostar/models/get_object_response_401.py +74 -0
  105. octostar/models/get_ontologies_response_401.py +74 -0
  106. octostar/models/get_ontologies_response_500.py +81 -0
  107. octostar/models/get_permissions_response_200.py +98 -0
  108. octostar/models/get_permissions_response_400.py +82 -0
  109. octostar/models/get_permissions_response_401.py +74 -0
  110. octostar/models/get_permissions_response_500.py +82 -0
  111. octostar/models/get_processing_status_response_200.py +104 -0
  112. octostar/models/get_processing_status_response_200_data.py +87 -0
  113. octostar/models/get_processing_status_response_400.py +82 -0
  114. octostar/models/get_processing_status_response_500.py +82 -0
  115. octostar/models/get_subscriptions_response_200_item.py +74 -0
  116. octostar/models/get_version_response_200.py +74 -0
  117. octostar/models/get_version_response_404.py +74 -0
  118. octostar/models/get_whoami_response_200.py +129 -0
  119. octostar/models/get_whoami_response_401.py +74 -0
  120. octostar/models/insert_entity.py +114 -0
  121. octostar/models/insert_entity_base.py +266 -0
  122. octostar/models/insert_entity_relationships_item.py +107 -0
  123. octostar/models/insert_entity_request.py +94 -0
  124. octostar/models/internal_server_error.py +82 -0
  125. octostar/models/job_execution_result.py +146 -0
  126. octostar/models/job_status.py +196 -0
  127. octostar/models/job_status_labels.py +60 -0
  128. octostar/models/job_with_url.py +82 -0
  129. octostar/models/kill_job_response_401.py +74 -0
  130. octostar/models/list_app_jobs_response_401.py +74 -0
  131. octostar/models/list_app_jobs_response_500.py +82 -0
  132. octostar/models/list_apps_response_401.py +74 -0
  133. octostar/models/list_apps_response_500.py +82 -0
  134. octostar/models/multi_query_json_body.py +100 -0
  135. octostar/models/multi_query_json_body_queries_item.py +80 -0
  136. octostar/models/multi_query_response_400.py +82 -0
  137. octostar/models/multi_query_response_401.py +74 -0
  138. octostar/models/not_found_error.py +74 -0
  139. octostar/models/octostar_event.py +96 -0
  140. octostar/models/octostar_event_octostar_payload.py +100 -0
  141. octostar/models/octostar_event_octostar_payload_level.py +11 -0
  142. octostar/models/os_notification.py +122 -0
  143. octostar/models/processing_status.py +262 -0
  144. octostar/models/processing_status_code.py +14 -0
  145. octostar/models/progress_request.py +73 -0
  146. octostar/models/publish_notification_response_401.py +74 -0
  147. octostar/models/pull_events_from_stream_response_401.py +74 -0
  148. octostar/models/push_event_to_stream_response_401.py +74 -0
  149. octostar/models/query_json_body.py +101 -0
  150. octostar/models/query_json_body_params.py +60 -0
  151. octostar/models/query_response_400.py +82 -0
  152. octostar/models/query_response_401.py +74 -0
  153. octostar/models/set_job_progress_response_401.py +74 -0
  154. octostar/models/string_to_value_label_map.py +99 -0
  155. octostar/models/string_to_value_label_map_data.py +89 -0
  156. octostar/models/string_to_value_label_map_data_additional_property.py +80 -0
  157. octostar/models/successful_get_tags.py +103 -0
  158. octostar/models/successful_insertion.py +98 -0
  159. octostar/models/tag_entities_response_401.py +74 -0
  160. octostar/models/toast_level.py +11 -0
  161. octostar/models/toast_response_401.py +74 -0
  162. octostar/models/undeploy_app_response_401.py +74 -0
  163. octostar/models/update_processing_status_response_200.py +82 -0
  164. octostar/models/update_processing_status_response_400.py +82 -0
  165. octostar/models/update_processing_status_response_500.py +82 -0
  166. octostar/models/upsert_entities_response_401.py +74 -0
  167. octostar/models/upsert_entity.py +114 -0
  168. octostar/models/upsert_entity_base.py +266 -0
  169. octostar/models/upsert_entity_relationships_item.py +107 -0
  170. octostar/py.typed +1 -0
  171. octostar/types.py +54 -0
  172. octostar/utils/__init__.py +15 -0
  173. octostar/utils/chat/__init__.py +0 -0
  174. octostar/utils/chat/chat.py +513 -0
  175. octostar/utils/chat/detokenize.py +105 -0
  176. octostar/utils/chat/get_default_model.py +50 -0
  177. octostar/utils/chat/list_models.py +91 -0
  178. octostar/utils/chat/tokenize.py +105 -0
  179. octostar/utils/commons.py +226 -0
  180. octostar/utils/exceptions.py +134 -0
  181. octostar/utils/jobs/__init__.py +0 -0
  182. octostar/utils/jobs/apps/__init__.py +0 -0
  183. octostar/utils/jobs/apps/deploy_app.py +81 -0
  184. octostar/utils/jobs/apps/execute_app_job.py +114 -0
  185. octostar/utils/jobs/apps/get_app_logs.py +113 -0
  186. octostar/utils/jobs/apps/get_app_secret.py +102 -0
  187. octostar/utils/jobs/apps/get_apps_url.py +73 -0
  188. octostar/utils/jobs/apps/list_app_jobs.py +62 -0
  189. octostar/utils/jobs/apps/list_apps.py +126 -0
  190. octostar/utils/jobs/apps/undeploy_app.py +48 -0
  191. octostar/utils/jobs/get_job_logs.py +113 -0
  192. octostar/utils/jobs/get_job_progress.py +76 -0
  193. octostar/utils/jobs/kill_job.py +47 -0
  194. octostar/utils/jobs/set_job_progress.py +67 -0
  195. octostar/utils/meta/__init__.py +0 -0
  196. octostar/utils/meta/get_version.py +30 -0
  197. octostar/utils/meta/get_whoami.py +30 -0
  198. octostar/utils/notifications/__init__.py +0 -0
  199. octostar/utils/notifications/delete_stream.py +58 -0
  200. octostar/utils/notifications/get_my_subscriptions.py +49 -0
  201. octostar/utils/notifications/publish_notification.py +73 -0
  202. octostar/utils/notifications/pull_event_from_stream.py +63 -0
  203. octostar/utils/notifications/pull_events_from_stream.py +64 -0
  204. octostar/utils/notifications/push_event_to_stream.py +109 -0
  205. octostar/utils/notifications/push_events_to_stream.py +137 -0
  206. octostar/utils/notifications/toast.py +92 -0
  207. octostar/utils/ontology/__init__.py +10 -0
  208. octostar/utils/ontology/fetch_ontology_data.py +141 -0
  209. octostar/utils/ontology/get_ontologies.py +55 -0
  210. octostar/utils/ontology/multiquery_ontology.py +287 -0
  211. octostar/utils/ontology/query_ontology.py +186 -0
  212. octostar/utils/pipeline/__init__.py +1 -0
  213. octostar/utils/pipeline/get_processing_status.py +230 -0
  214. octostar/utils/pipeline/update_processing_status.py +286 -0
  215. octostar/utils/search/__init__.py +11 -0
  216. octostar/utils/search/bulk_update.py +138 -0
  217. octostar/utils/search/count.py +117 -0
  218. octostar/utils/search/get_entity_annotations.py +304 -0
  219. octostar/utils/search/get_index_definition.py +111 -0
  220. octostar/utils/search/multi_search.py +129 -0
  221. octostar/utils/workspace/__init__.py +0 -0
  222. octostar/utils/workspace/delete_entities.py +247 -0
  223. octostar/utils/workspace/delete_entity.py +81 -0
  224. octostar/utils/workspace/delete_relationship.py +78 -0
  225. octostar/utils/workspace/delete_relationships.py +85 -0
  226. octostar/utils/workspace/delete_temporary_blob.py +85 -0
  227. octostar/utils/workspace/extract_entities.py +140 -0
  228. octostar/utils/workspace/get_filepath_from_item.py +85 -0
  229. octostar/utils/workspace/get_filepaths_from_items.py +100 -0
  230. octostar/utils/workspace/get_files_tree.py +102 -0
  231. octostar/utils/workspace/get_item_from_filepath.py +102 -0
  232. octostar/utils/workspace/get_items_from_filepaths.py +108 -0
  233. octostar/utils/workspace/linkcharts/__init__.py +0 -0
  234. octostar/utils/workspace/linkcharts/create_linkchart.py +241 -0
  235. octostar/utils/workspace/permissions/PermissionLevel.py +8 -0
  236. octostar/utils/workspace/permissions/__init__.py +1 -0
  237. octostar/utils/workspace/permissions/get_permissions.py +81 -0
  238. octostar/utils/workspace/read_attachment.py +284 -0
  239. octostar/utils/workspace/read_file.py +113 -0
  240. octostar/utils/workspace/read_temporary_blob.py +428 -0
  241. octostar/utils/workspace/saved_searches/__init__.py +0 -0
  242. octostar/utils/workspace/saved_searches/create_saved_search.py +183 -0
  243. octostar/utils/workspace/tags/__init__.py +0 -0
  244. octostar/utils/workspace/tags/delete_tag_from_entities.py +96 -0
  245. octostar/utils/workspace/tags/tag_entities.py +175 -0
  246. octostar/utils/workspace/upsert_entities.py +268 -0
  247. octostar/utils/workspace/upsert_entity.py +110 -0
  248. octostar/utils/workspace/upsert_relationship.py +128 -0
  249. octostar/utils/workspace/upsert_relationships.py +194 -0
  250. octostar/utils/workspace/write_attachment.py +263 -0
  251. octostar/utils/workspace/write_file.py +335 -0
  252. octostar/utils/workspace/write_temporary_blob.py +218 -0
  253. octostar_python_client-0.1.759.dist-info/METADATA +159 -0
  254. octostar_python_client-0.1.759.dist-info/RECORD +257 -0
  255. octostar_python_client-0.1.759.dist-info/WHEEL +5 -0
  256. octostar_python_client-0.1.759.dist-info/licenses/LICENSE +21 -0
  257. 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
+