sift-stack-py 0.3.2__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 (291) hide show
  1. google/__init__.py +1 -0
  2. google/api/__init__.py +0 -0
  3. google/api/annotations_pb2.py +27 -0
  4. google/api/annotations_pb2.pyi +29 -0
  5. google/api/annotations_pb2_grpc.py +4 -0
  6. google/api/annotations_pb2_grpc.pyi +30 -0
  7. google/api/field_behavior_pb2.py +30 -0
  8. google/api/field_behavior_pb2.pyi +175 -0
  9. google/api/field_behavior_pb2_grpc.py +4 -0
  10. google/api/field_behavior_pb2_grpc.pyi +30 -0
  11. google/api/http_pb2.py +31 -0
  12. google/api/http_pb2.pyi +433 -0
  13. google/api/http_pb2_grpc.py +4 -0
  14. google/api/http_pb2_grpc.pyi +30 -0
  15. protoc_gen_openapiv2/__init__.py +0 -0
  16. protoc_gen_openapiv2/options/__init__.py +0 -0
  17. protoc_gen_openapiv2/options/annotations_pb2.py +27 -0
  18. protoc_gen_openapiv2/options/annotations_pb2.pyi +48 -0
  19. protoc_gen_openapiv2/options/annotations_pb2_grpc.py +4 -0
  20. protoc_gen_openapiv2/options/annotations_pb2_grpc.pyi +17 -0
  21. protoc_gen_openapiv2/options/openapiv2_pb2.py +132 -0
  22. protoc_gen_openapiv2/options/openapiv2_pb2.pyi +1533 -0
  23. protoc_gen_openapiv2/options/openapiv2_pb2_grpc.py +4 -0
  24. protoc_gen_openapiv2/options/openapiv2_pb2_grpc.pyi +17 -0
  25. sift/__init__.py +0 -0
  26. sift/annotation_logs/__init__.py +0 -0
  27. sift/annotation_logs/v1/__init__.py +0 -0
  28. sift/annotation_logs/v1/annotation_logs_pb2.py +115 -0
  29. sift/annotation_logs/v1/annotation_logs_pb2.pyi +370 -0
  30. sift/annotation_logs/v1/annotation_logs_pb2_grpc.py +135 -0
  31. sift/annotation_logs/v1/annotation_logs_pb2_grpc.pyi +84 -0
  32. sift/annotations/__init__.py +0 -0
  33. sift/annotations/v1/__init__.py +0 -0
  34. sift/annotations/v1/annotations_pb2.py +180 -0
  35. sift/annotations/v1/annotations_pb2.pyi +539 -0
  36. sift/annotations/v1/annotations_pb2_grpc.py +237 -0
  37. sift/annotations/v1/annotations_pb2_grpc.pyi +144 -0
  38. sift/assets/__init__.py +0 -0
  39. sift/assets/v1/__init__.py +0 -0
  40. sift/assets/v1/assets_pb2.py +90 -0
  41. sift/assets/v1/assets_pb2.pyi +235 -0
  42. sift/assets/v1/assets_pb2_grpc.py +168 -0
  43. sift/assets/v1/assets_pb2_grpc.pyi +101 -0
  44. sift/calculated_channels/__init__.py +0 -0
  45. sift/calculated_channels/v1/__init__.py +0 -0
  46. sift/calculated_channels/v1/calculated_channels_pb2.py +99 -0
  47. sift/calculated_channels/v1/calculated_channels_pb2.pyi +280 -0
  48. sift/calculated_channels/v1/calculated_channels_pb2_grpc.py +101 -0
  49. sift/calculated_channels/v1/calculated_channels_pb2_grpc.pyi +64 -0
  50. sift/campaigns/__init__.py +0 -0
  51. sift/campaigns/v1/__init__.py +0 -0
  52. sift/campaigns/v1/campaigns_pb2.py +144 -0
  53. sift/campaigns/v1/campaigns_pb2.pyi +383 -0
  54. sift/campaigns/v1/campaigns_pb2_grpc.py +169 -0
  55. sift/campaigns/v1/campaigns_pb2_grpc.pyi +104 -0
  56. sift/channel_schemas/__init__.py +0 -0
  57. sift/channel_schemas/v1/__init__.py +0 -0
  58. sift/channel_schemas/v1/channel_schemas_pb2.py +69 -0
  59. sift/channel_schemas/v1/channel_schemas_pb2.pyi +117 -0
  60. sift/channel_schemas/v1/channel_schemas_pb2_grpc.py +101 -0
  61. sift/channel_schemas/v1/channel_schemas_pb2_grpc.pyi +64 -0
  62. sift/channels/__init__.py +0 -0
  63. sift/channels/v2/__init__.py +0 -0
  64. sift/channels/v2/channels_pb2.py +88 -0
  65. sift/channels/v2/channels_pb2.pyi +183 -0
  66. sift/channels/v2/channels_pb2_grpc.py +101 -0
  67. sift/channels/v2/channels_pb2_grpc.pyi +64 -0
  68. sift/common/__init__.py +0 -0
  69. sift/common/type/__init__.py +0 -0
  70. sift/common/type/v1/__init__.py +0 -0
  71. sift/common/type/v1/channel_bit_field_element_pb2.py +34 -0
  72. sift/common/type/v1/channel_bit_field_element_pb2.pyi +33 -0
  73. sift/common/type/v1/channel_bit_field_element_pb2_grpc.py +4 -0
  74. sift/common/type/v1/channel_bit_field_element_pb2_grpc.pyi +17 -0
  75. sift/common/type/v1/channel_data_type_pb2.py +29 -0
  76. sift/common/type/v1/channel_data_type_pb2.pyi +50 -0
  77. sift/common/type/v1/channel_data_type_pb2_grpc.py +4 -0
  78. sift/common/type/v1/channel_data_type_pb2_grpc.pyi +17 -0
  79. sift/common/type/v1/channel_enum_type_pb2.py +32 -0
  80. sift/common/type/v1/channel_enum_type_pb2.pyi +29 -0
  81. sift/common/type/v1/channel_enum_type_pb2_grpc.py +4 -0
  82. sift/common/type/v1/channel_enum_type_pb2_grpc.pyi +17 -0
  83. sift/common/type/v1/organization_pb2.py +27 -0
  84. sift/common/type/v1/organization_pb2.pyi +29 -0
  85. sift/common/type/v1/organization_pb2_grpc.py +4 -0
  86. sift/common/type/v1/organization_pb2_grpc.pyi +17 -0
  87. sift/common/type/v1/resource_identifier_pb2.py +46 -0
  88. sift/common/type/v1/resource_identifier_pb2.pyi +145 -0
  89. sift/common/type/v1/resource_identifier_pb2_grpc.py +4 -0
  90. sift/common/type/v1/resource_identifier_pb2_grpc.pyi +17 -0
  91. sift/common/type/v1/user_pb2.py +33 -0
  92. sift/common/type/v1/user_pb2.pyi +36 -0
  93. sift/common/type/v1/user_pb2_grpc.py +4 -0
  94. sift/common/type/v1/user_pb2_grpc.pyi +17 -0
  95. sift/data/__init__.py +0 -0
  96. sift/data/v1/__init__.py +0 -0
  97. sift/data/v1/data_pb2.py +212 -0
  98. sift/data/v1/data_pb2.pyi +745 -0
  99. sift/data/v1/data_pb2_grpc.py +67 -0
  100. sift/data/v1/data_pb2_grpc.pyi +44 -0
  101. sift/ingest/__init__.py +0 -0
  102. sift/ingest/v1/__init__.py +0 -0
  103. sift/ingest/v1/ingest_pb2.py +35 -0
  104. sift/ingest/v1/ingest_pb2.pyi +118 -0
  105. sift/ingest/v1/ingest_pb2_grpc.py +66 -0
  106. sift/ingest/v1/ingest_pb2_grpc.pyi +41 -0
  107. sift/ingestion_configs/__init__.py +0 -0
  108. sift/ingestion_configs/v1/__init__.py +0 -0
  109. sift/ingestion_configs/v1/ingestion_configs_pb2.py +115 -0
  110. sift/ingestion_configs/v1/ingestion_configs_pb2.pyi +332 -0
  111. sift/ingestion_configs/v1/ingestion_configs_pb2_grpc.py +203 -0
  112. sift/ingestion_configs/v1/ingestion_configs_pb2_grpc.pyi +124 -0
  113. sift/notifications/__init__.py +0 -0
  114. sift/notifications/v1/__init__.py +0 -0
  115. sift/notifications/v1/notifications_pb2.py +64 -0
  116. sift/notifications/v1/notifications_pb2.pyi +225 -0
  117. sift/notifications/v1/notifications_pb2_grpc.py +101 -0
  118. sift/notifications/v1/notifications_pb2_grpc.pyi +64 -0
  119. sift/ping/__init__.py +0 -0
  120. sift/ping/v1/__init__.py +0 -0
  121. sift/ping/v1/ping_pb2.py +38 -0
  122. sift/ping/v1/ping_pb2.pyi +36 -0
  123. sift/ping/v1/ping_pb2_grpc.py +66 -0
  124. sift/ping/v1/ping_pb2_grpc.pyi +41 -0
  125. sift/remote_files/__init__.py +0 -0
  126. sift/remote_files/v1/__init__.py +0 -0
  127. sift/remote_files/v1/remote_files_pb2.py +174 -0
  128. sift/remote_files/v1/remote_files_pb2.pyi +472 -0
  129. sift/remote_files/v1/remote_files_pb2_grpc.py +271 -0
  130. sift/remote_files/v1/remote_files_pb2_grpc.pyi +164 -0
  131. sift/report_templates/__init__.py +0 -0
  132. sift/report_templates/v1/__init__.py +0 -0
  133. sift/report_templates/v1/report_templates_pb2.py +146 -0
  134. sift/report_templates/v1/report_templates_pb2.pyi +381 -0
  135. sift/report_templates/v1/report_templates_pb2_grpc.py +169 -0
  136. sift/report_templates/v1/report_templates_pb2_grpc.pyi +104 -0
  137. sift/reports/__init__.py +0 -0
  138. sift/reports/v1/__init__.py +0 -0
  139. sift/reports/v1/reports_pb2.py +193 -0
  140. sift/reports/v1/reports_pb2.pyi +562 -0
  141. sift/reports/v1/reports_pb2_grpc.py +205 -0
  142. sift/reports/v1/reports_pb2_grpc.pyi +136 -0
  143. sift/rule_evaluation/__init__.py +0 -0
  144. sift/rule_evaluation/v1/__init__.py +0 -0
  145. sift/rule_evaluation/v1/rule_evaluation_pb2.py +89 -0
  146. sift/rule_evaluation/v1/rule_evaluation_pb2.pyi +263 -0
  147. sift/rule_evaluation/v1/rule_evaluation_pb2_grpc.py +101 -0
  148. sift/rule_evaluation/v1/rule_evaluation_pb2_grpc.pyi +64 -0
  149. sift/rules/__init__.py +0 -0
  150. sift/rules/v1/__init__.py +0 -0
  151. sift/rules/v1/rules_pb2.py +420 -0
  152. sift/rules/v1/rules_pb2.pyi +1355 -0
  153. sift/rules/v1/rules_pb2_grpc.py +577 -0
  154. sift/rules/v1/rules_pb2_grpc.pyi +351 -0
  155. sift/runs/__init__.py +0 -0
  156. sift/runs/v2/__init__.py +0 -0
  157. sift/runs/v2/runs_pb2.py +150 -0
  158. sift/runs/v2/runs_pb2.pyi +413 -0
  159. sift/runs/v2/runs_pb2_grpc.py +271 -0
  160. sift/runs/v2/runs_pb2_grpc.pyi +164 -0
  161. sift/saved_searches/__init__.py +0 -0
  162. sift/saved_searches/v1/__init__.py +0 -0
  163. sift/saved_searches/v1/saved_searches_pb2.py +144 -0
  164. sift/saved_searches/v1/saved_searches_pb2.pyi +385 -0
  165. sift/saved_searches/v1/saved_searches_pb2_grpc.py +237 -0
  166. sift/saved_searches/v1/saved_searches_pb2_grpc.pyi +144 -0
  167. sift/tags/__init__.py +0 -0
  168. sift/tags/v1/__init__.py +0 -0
  169. sift/tags/v1/tags_pb2.py +49 -0
  170. sift/tags/v1/tags_pb2.pyi +71 -0
  171. sift/tags/v1/tags_pb2_grpc.py +4 -0
  172. sift/tags/v1/tags_pb2_grpc.pyi +17 -0
  173. sift/users/__init__.py +0 -0
  174. sift/users/v2/__init__.py +0 -0
  175. sift/users/v2/users_pb2.py +61 -0
  176. sift/users/v2/users_pb2.pyi +142 -0
  177. sift/users/v2/users_pb2_grpc.py +135 -0
  178. sift/users/v2/users_pb2_grpc.pyi +84 -0
  179. sift/views/__init__.py +0 -0
  180. sift/views/v1/__init__.py +0 -0
  181. sift/views/v1/views_pb2.py +130 -0
  182. sift/views/v1/views_pb2.pyi +466 -0
  183. sift/views/v1/views_pb2_grpc.py +305 -0
  184. sift/views/v1/views_pb2_grpc.pyi +184 -0
  185. sift_grafana/py.typed +0 -0
  186. sift_grafana/sift_query_model.py +64 -0
  187. sift_py/__init__.py +923 -0
  188. sift_py/_internal/__init__.py +5 -0
  189. sift_py/_internal/cel.py +18 -0
  190. sift_py/_internal/channel.py +42 -0
  191. sift_py/_internal/convert/__init__.py +3 -0
  192. sift_py/_internal/convert/json.py +24 -0
  193. sift_py/_internal/convert/protobuf.py +34 -0
  194. sift_py/_internal/convert/timestamp.py +9 -0
  195. sift_py/_internal/test_util/__init__.py +0 -0
  196. sift_py/_internal/test_util/channel.py +136 -0
  197. sift_py/_internal/test_util/fn.py +14 -0
  198. sift_py/_internal/test_util/server_interceptor.py +62 -0
  199. sift_py/_internal/time.py +48 -0
  200. sift_py/_internal/user.py +39 -0
  201. sift_py/data/__init__.py +171 -0
  202. sift_py/data/_channel.py +38 -0
  203. sift_py/data/_deserialize.py +208 -0
  204. sift_py/data/_deserialize_test.py +134 -0
  205. sift_py/data/_service_test.py +276 -0
  206. sift_py/data/_validate.py +10 -0
  207. sift_py/data/error.py +5 -0
  208. sift_py/data/query.py +299 -0
  209. sift_py/data/service.py +497 -0
  210. sift_py/data_import/__init__.py +130 -0
  211. sift_py/data_import/_config.py +167 -0
  212. sift_py/data_import/_config_test.py +166 -0
  213. sift_py/data_import/_csv_test.py +395 -0
  214. sift_py/data_import/_status_test.py +176 -0
  215. sift_py/data_import/_tdms_test.py +238 -0
  216. sift_py/data_import/ch10.py +157 -0
  217. sift_py/data_import/config.py +19 -0
  218. sift_py/data_import/csv.py +259 -0
  219. sift_py/data_import/status.py +113 -0
  220. sift_py/data_import/tdms.py +206 -0
  221. sift_py/data_import/tempfile.py +30 -0
  222. sift_py/data_import/time_format.py +39 -0
  223. sift_py/error.py +11 -0
  224. sift_py/file_attachment/__init__.py +88 -0
  225. sift_py/file_attachment/_internal/__init__.py +0 -0
  226. sift_py/file_attachment/_internal/download.py +13 -0
  227. sift_py/file_attachment/_internal/upload.py +100 -0
  228. sift_py/file_attachment/_service_test.py +161 -0
  229. sift_py/file_attachment/entity.py +30 -0
  230. sift_py/file_attachment/metadata.py +107 -0
  231. sift_py/file_attachment/service.py +142 -0
  232. sift_py/grpc/__init__.py +15 -0
  233. sift_py/grpc/_async_interceptors/__init__.py +0 -0
  234. sift_py/grpc/_async_interceptors/base.py +72 -0
  235. sift_py/grpc/_async_interceptors/metadata.py +36 -0
  236. sift_py/grpc/_interceptors/__init__.py +0 -0
  237. sift_py/grpc/_interceptors/base.py +61 -0
  238. sift_py/grpc/_interceptors/context.py +25 -0
  239. sift_py/grpc/_interceptors/metadata.py +33 -0
  240. sift_py/grpc/_retry.py +70 -0
  241. sift_py/grpc/keepalive.py +34 -0
  242. sift_py/grpc/transport.py +250 -0
  243. sift_py/grpc/transport_test.py +170 -0
  244. sift_py/ingestion/__init__.py +6 -0
  245. sift_py/ingestion/_internal/__init__.py +6 -0
  246. sift_py/ingestion/_internal/channel.py +12 -0
  247. sift_py/ingestion/_internal/error.py +10 -0
  248. sift_py/ingestion/_internal/ingest.py +350 -0
  249. sift_py/ingestion/_internal/ingest_test.py +357 -0
  250. sift_py/ingestion/_internal/ingestion_config.py +130 -0
  251. sift_py/ingestion/_internal/run.py +46 -0
  252. sift_py/ingestion/_service_test.py +478 -0
  253. sift_py/ingestion/buffer.py +189 -0
  254. sift_py/ingestion/channel.py +422 -0
  255. sift_py/ingestion/config/__init__.py +3 -0
  256. sift_py/ingestion/config/telemetry.py +281 -0
  257. sift_py/ingestion/config/telemetry_test.py +405 -0
  258. sift_py/ingestion/config/yaml/__init__.py +0 -0
  259. sift_py/ingestion/config/yaml/error.py +44 -0
  260. sift_py/ingestion/config/yaml/load.py +126 -0
  261. sift_py/ingestion/config/yaml/spec.py +58 -0
  262. sift_py/ingestion/config/yaml/test_load.py +25 -0
  263. sift_py/ingestion/flow.py +73 -0
  264. sift_py/ingestion/manager.py +99 -0
  265. sift_py/ingestion/rule/__init__.py +4 -0
  266. sift_py/ingestion/rule/config.py +11 -0
  267. sift_py/ingestion/service.py +237 -0
  268. sift_py/py.typed +0 -0
  269. sift_py/report_templates/__init__.py +0 -0
  270. sift_py/report_templates/_config_test.py +34 -0
  271. sift_py/report_templates/_service_test.py +94 -0
  272. sift_py/report_templates/config.py +36 -0
  273. sift_py/report_templates/service.py +171 -0
  274. sift_py/rest.py +29 -0
  275. sift_py/rule/__init__.py +0 -0
  276. sift_py/rule/_config_test.py +109 -0
  277. sift_py/rule/_service_test.py +168 -0
  278. sift_py/rule/config.py +229 -0
  279. sift_py/rule/service.py +484 -0
  280. sift_py/yaml/__init__.py +0 -0
  281. sift_py/yaml/_channel_test.py +169 -0
  282. sift_py/yaml/_rule_test.py +207 -0
  283. sift_py/yaml/channel.py +224 -0
  284. sift_py/yaml/report_templates.py +73 -0
  285. sift_py/yaml/rule.py +321 -0
  286. sift_py/yaml/utils.py +15 -0
  287. sift_stack_py-0.3.2.dist-info/LICENSE +7 -0
  288. sift_stack_py-0.3.2.dist-info/METADATA +109 -0
  289. sift_stack_py-0.3.2.dist-info/RECORD +291 -0
  290. sift_stack_py-0.3.2.dist-info/WHEEL +5 -0
  291. sift_stack_py-0.3.2.dist-info/top_level.txt +5 -0
sift_py/data/query.py ADDED
@@ -0,0 +1,299 @@
1
+ """
2
+ Module containing utilities to construct a data query which is ultimately
3
+ passed to `sift_py.data.service.DataService.execute` to download telemetry.
4
+
5
+ This module also contains types that represent the result of a data query
6
+ which can be easily converted into a `pandas` data frame or series.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from datetime import datetime
12
+ from typing import Any, Dict, List, Optional, Tuple, TypedDict, Union, cast
13
+
14
+ import pandas as pd
15
+ from google.protobuf.timestamp_pb2 import Timestamp as TimestampPb
16
+ from typing_extensions import NotRequired, TypeAlias
17
+
18
+ from sift_py._internal.channel import channel_fqn
19
+ from sift_py._internal.time import to_timestamp_nanos
20
+ from sift_py.data._channel import ChannelTimeSeries
21
+ from sift_py.ingestion.channel import ChannelDataType
22
+
23
+
24
+ class DataQuery:
25
+ """
26
+ A query that is meant to be passed to `sift_py.data.service.DataService.execute` to
27
+ retrieve telemetry.
28
+
29
+ - `asset_name`: The name of the asset to query telemetry for.
30
+ - `start_time`: The start time of the time range of the data to request.
31
+ - `end_time`: The end time of the time range of the data to request.
32
+ - `sample_ms`:
33
+ The sampling rate to use when retrieving data. The lower the sampling rate, the
34
+ greater the data-fidelity. A sampling rate of `0` retrieves full-fidelity data.
35
+ - `channels`:
36
+ List of either `ChannelQuery` or `CalculatedChannelQuery`, but not both. Represents the
37
+ channels to retrieve data from.
38
+ """
39
+
40
+ DEFAULT_PAGE_SIZE = 100_000
41
+
42
+ asset_name: str
43
+ start_time: pd.Timestamp
44
+ end_time: pd.Timestamp
45
+ sample_ms: int
46
+ page_size: int
47
+ channels: List[Union[ChannelQuery, CalculatedChannelQuery]]
48
+
49
+ def __init__(
50
+ self,
51
+ asset_name: str,
52
+ start_time: Union[pd.Timestamp, TimestampPb, datetime, str, int],
53
+ end_time: Union[pd.Timestamp, TimestampPb, datetime, str, int],
54
+ channels: List[Union[ChannelQuery, CalculatedChannelQuery]],
55
+ sample_ms: int = 0,
56
+ # Currently not in use outside of testing purposes.
57
+ _: int = DEFAULT_PAGE_SIZE,
58
+ ):
59
+ self.start_time = to_timestamp_nanos(start_time)
60
+ self.end_time = to_timestamp_nanos(end_time)
61
+ self.asset_name = asset_name
62
+ self.sample_ms = sample_ms
63
+ self.channels = channels
64
+ self.page_size = self.__class__.DEFAULT_PAGE_SIZE
65
+
66
+
67
+ """
68
+ Either the fully qualified channel name or a tuple of the fully qualified
69
+ channel name as well as the channel's type.
70
+ """
71
+ ChannelLookupInfo: TypeAlias = Union[str, Tuple[str, ChannelDataType]]
72
+
73
+
74
+ class DataQueryResult:
75
+ """
76
+ The result of a data query which can contain multiple channels.
77
+ """
78
+
79
+ _result: Dict[str, List[ChannelTimeSeries]]
80
+
81
+ def __init__(self, merged_channel_data: Dict[str, List[ChannelTimeSeries]]):
82
+ self._result = merged_channel_data
83
+
84
+ def channel(self, lookup: ChannelLookupInfo) -> Optional[DataQueryResultSet]:
85
+ """
86
+ Like `channels` but returns a single `DataQueryResultSet`.
87
+ """
88
+
89
+ result = self.channels(lookup)
90
+
91
+ if len(result) > 0:
92
+ return result[0]
93
+
94
+ return None
95
+
96
+ def channels(self, *lookup: ChannelLookupInfo) -> List[DataQueryResultSet]:
97
+ """
98
+ Returns a `sift_py.data.channel.ChannelTimeSeries` given the `lookup` argument.
99
+ If a `lookup` is a fully qualified name (FQN) `str` and there are multiple channels
100
+ with the same FQN, this will raise a `ValueError`. In these situations, `lookup` must
101
+ be a tuple where the first item is the channel FQN and the second the
102
+ `sift_py.ingestion.channel.ChannelDataType`.
103
+
104
+ If `lookup` is a tuple, then the channel data-type will be appended to the key referencing
105
+ the `sift_py.data.channel.ChannelTimeSeries`.
106
+ """
107
+
108
+ result: List[DataQueryResultSet] = []
109
+
110
+ for info in lookup:
111
+ if isinstance(info, str):
112
+ time_series = self._result.get(info)
113
+
114
+ if not time_series:
115
+ continue
116
+ if len(time_series) > 1:
117
+ raise ValueError(
118
+ f"Ambiguous lookup provided: '{info}' is associated with {len(time_series)} channels."
119
+ )
120
+
121
+ series = time_series[0]
122
+ result.append(
123
+ DataQueryResultSet(
124
+ identifier=info,
125
+ timestamps=series.time_column,
126
+ values=series.value_column,
127
+ )
128
+ )
129
+ else:
130
+ fqn, data_type = cast(Tuple[str, ChannelDataType], info)
131
+ identifier = f"{fqn}.{data_type.as_human_str()}"
132
+
133
+ time_series = self._result.get(fqn)
134
+
135
+ if not time_series:
136
+ continue
137
+ if len(time_series) == 1:
138
+ series = time_series[0]
139
+ result.append(
140
+ DataQueryResultSet(
141
+ identifier=identifier,
142
+ timestamps=series.time_column,
143
+ values=series.value_column,
144
+ )
145
+ )
146
+ continue
147
+
148
+ for series in time_series:
149
+ if series.data_type == data_type:
150
+ result.append(
151
+ DataQueryResultSet(
152
+ identifier=identifier,
153
+ timestamps=series.time_column,
154
+ values=series.value_column,
155
+ )
156
+ )
157
+ break
158
+
159
+ return result
160
+
161
+ def all_channels(self) -> List[DataQueryResultSet]:
162
+ """
163
+ Returns all channel data.
164
+ """
165
+
166
+ result = []
167
+
168
+ for fqn, time_series in self._result.items():
169
+ if len(time_series) > 1:
170
+ for series in time_series:
171
+ human_data_type = series.data_type.as_human_str()
172
+ fqn_extended = f"{fqn}.{human_data_type}"
173
+
174
+ result.append(
175
+ DataQueryResultSet(
176
+ identifier=fqn_extended,
177
+ timestamps=series.time_column,
178
+ values=series.value_column,
179
+ )
180
+ )
181
+ continue
182
+
183
+ for series in time_series:
184
+ result.append(
185
+ DataQueryResultSet(
186
+ identifier=fqn,
187
+ timestamps=series.time_column,
188
+ values=series.value_column,
189
+ )
190
+ )
191
+
192
+ return result
193
+
194
+
195
+ class DataQueryResultSet:
196
+ """
197
+ Represents time series data for a single channel. Can easily be converted into a `pandas` data frame like so:
198
+
199
+ ```python
200
+ pd.DataFrame(data_query_result_set.all_columns())
201
+ ```
202
+
203
+ """
204
+
205
+ identifier: str
206
+ timestamps: List[pd.Timestamp]
207
+ values: List[Any]
208
+
209
+ def __init__(self, identifier: str, timestamps: List[pd.Timestamp], values: List[Any]):
210
+ self.identifier = identifier
211
+ self.timestamps = timestamps
212
+ self.values = values
213
+
214
+ def value_column(self, column_name: Optional[str] = None) -> Dict[str, List[Any]]:
215
+ """
216
+ Returns a single key-value pair dictionary meant to represent the value column of the data-set.
217
+ `column_name` can be used to override the name of the column.
218
+ """
219
+
220
+ if column_name is None:
221
+ return {self.identifier: self.values}
222
+ else:
223
+ return {column_name: self.values}
224
+
225
+ def time_column(self, column_name: Optional[str] = None) -> Dict[str, List[Any]]:
226
+ """
227
+ Returns a single key-value pair dictionary meant to represent the time column of the data-set.
228
+ `column_name` can be used to override the name of the column.
229
+ """
230
+ if column_name is None:
231
+ return {"time": self.timestamps}
232
+ else:
233
+ return {column_name: self.timestamps}
234
+
235
+ def columns(
236
+ self,
237
+ time_column_name: Optional[str] = None,
238
+ value_column_name: Optional[str] = None,
239
+ ) -> Dict[str, List[Any]]:
240
+ """
241
+ Returns both the time and value columns with options to override the column names.
242
+ """
243
+
244
+ cols = self.time_column(time_column_name)
245
+ cols.update(self.value_column(value_column_name))
246
+ return cols
247
+
248
+
249
+ class ChannelQuery:
250
+ """
251
+ Represents a single channel to include in the `sift_py.data.query.DataQuery`.
252
+ """
253
+
254
+ channel_name: str
255
+ component: Optional[str]
256
+ run_name: Optional[str]
257
+
258
+ def __init__(
259
+ self,
260
+ channel_name: str,
261
+ component: Optional[str] = None,
262
+ run_name: Optional[str] = None,
263
+ ):
264
+ self.channel_name = channel_name
265
+ self.component = component
266
+ self.run_name = run_name
267
+
268
+ def fqn(self) -> str:
269
+ return channel_fqn(self.channel_name, self.component)
270
+
271
+
272
+ class ExpressionChannelReference(TypedDict):
273
+ reference: str
274
+ channel_name: str
275
+ component: NotRequired[str]
276
+ data_type: NotRequired[ChannelDataType]
277
+
278
+
279
+ class CalculatedChannelQuery:
280
+ """
281
+ Represents a single calculated channel to include in the `sift_py.data.query.DataQuery`.
282
+ """
283
+
284
+ channel_key: str
285
+ expression: str
286
+ expression_channel_references: List[ExpressionChannelReference]
287
+ run_name: Optional[str]
288
+
289
+ def __init__(
290
+ self,
291
+ channel_key: str,
292
+ expression: str,
293
+ expression_channel_references: List[ExpressionChannelReference],
294
+ run_name: Optional[str] = None,
295
+ ):
296
+ self.channel_key = channel_key
297
+ self.run_name = run_name
298
+ self.expression = expression
299
+ self.expression_channel_references = expression_channel_references