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.
- google/__init__.py +1 -0
- google/api/__init__.py +0 -0
- google/api/annotations_pb2.py +27 -0
- google/api/annotations_pb2.pyi +29 -0
- google/api/annotations_pb2_grpc.py +4 -0
- google/api/annotations_pb2_grpc.pyi +30 -0
- google/api/field_behavior_pb2.py +30 -0
- google/api/field_behavior_pb2.pyi +175 -0
- google/api/field_behavior_pb2_grpc.py +4 -0
- google/api/field_behavior_pb2_grpc.pyi +30 -0
- google/api/http_pb2.py +31 -0
- google/api/http_pb2.pyi +433 -0
- google/api/http_pb2_grpc.py +4 -0
- google/api/http_pb2_grpc.pyi +30 -0
- protoc_gen_openapiv2/__init__.py +0 -0
- protoc_gen_openapiv2/options/__init__.py +0 -0
- protoc_gen_openapiv2/options/annotations_pb2.py +27 -0
- protoc_gen_openapiv2/options/annotations_pb2.pyi +48 -0
- protoc_gen_openapiv2/options/annotations_pb2_grpc.py +4 -0
- protoc_gen_openapiv2/options/annotations_pb2_grpc.pyi +17 -0
- protoc_gen_openapiv2/options/openapiv2_pb2.py +132 -0
- protoc_gen_openapiv2/options/openapiv2_pb2.pyi +1533 -0
- protoc_gen_openapiv2/options/openapiv2_pb2_grpc.py +4 -0
- protoc_gen_openapiv2/options/openapiv2_pb2_grpc.pyi +17 -0
- sift/__init__.py +0 -0
- sift/annotation_logs/__init__.py +0 -0
- sift/annotation_logs/v1/__init__.py +0 -0
- sift/annotation_logs/v1/annotation_logs_pb2.py +115 -0
- sift/annotation_logs/v1/annotation_logs_pb2.pyi +370 -0
- sift/annotation_logs/v1/annotation_logs_pb2_grpc.py +135 -0
- sift/annotation_logs/v1/annotation_logs_pb2_grpc.pyi +84 -0
- sift/annotations/__init__.py +0 -0
- sift/annotations/v1/__init__.py +0 -0
- sift/annotations/v1/annotations_pb2.py +180 -0
- sift/annotations/v1/annotations_pb2.pyi +539 -0
- sift/annotations/v1/annotations_pb2_grpc.py +237 -0
- sift/annotations/v1/annotations_pb2_grpc.pyi +144 -0
- sift/assets/__init__.py +0 -0
- sift/assets/v1/__init__.py +0 -0
- sift/assets/v1/assets_pb2.py +90 -0
- sift/assets/v1/assets_pb2.pyi +235 -0
- sift/assets/v1/assets_pb2_grpc.py +168 -0
- sift/assets/v1/assets_pb2_grpc.pyi +101 -0
- sift/calculated_channels/__init__.py +0 -0
- sift/calculated_channels/v1/__init__.py +0 -0
- sift/calculated_channels/v1/calculated_channels_pb2.py +99 -0
- sift/calculated_channels/v1/calculated_channels_pb2.pyi +280 -0
- sift/calculated_channels/v1/calculated_channels_pb2_grpc.py +101 -0
- sift/calculated_channels/v1/calculated_channels_pb2_grpc.pyi +64 -0
- sift/campaigns/__init__.py +0 -0
- sift/campaigns/v1/__init__.py +0 -0
- sift/campaigns/v1/campaigns_pb2.py +144 -0
- sift/campaigns/v1/campaigns_pb2.pyi +383 -0
- sift/campaigns/v1/campaigns_pb2_grpc.py +169 -0
- sift/campaigns/v1/campaigns_pb2_grpc.pyi +104 -0
- sift/channel_schemas/__init__.py +0 -0
- sift/channel_schemas/v1/__init__.py +0 -0
- sift/channel_schemas/v1/channel_schemas_pb2.py +69 -0
- sift/channel_schemas/v1/channel_schemas_pb2.pyi +117 -0
- sift/channel_schemas/v1/channel_schemas_pb2_grpc.py +101 -0
- sift/channel_schemas/v1/channel_schemas_pb2_grpc.pyi +64 -0
- sift/channels/__init__.py +0 -0
- sift/channels/v2/__init__.py +0 -0
- sift/channels/v2/channels_pb2.py +88 -0
- sift/channels/v2/channels_pb2.pyi +183 -0
- sift/channels/v2/channels_pb2_grpc.py +101 -0
- sift/channels/v2/channels_pb2_grpc.pyi +64 -0
- sift/common/__init__.py +0 -0
- sift/common/type/__init__.py +0 -0
- sift/common/type/v1/__init__.py +0 -0
- sift/common/type/v1/channel_bit_field_element_pb2.py +34 -0
- sift/common/type/v1/channel_bit_field_element_pb2.pyi +33 -0
- sift/common/type/v1/channel_bit_field_element_pb2_grpc.py +4 -0
- sift/common/type/v1/channel_bit_field_element_pb2_grpc.pyi +17 -0
- sift/common/type/v1/channel_data_type_pb2.py +29 -0
- sift/common/type/v1/channel_data_type_pb2.pyi +50 -0
- sift/common/type/v1/channel_data_type_pb2_grpc.py +4 -0
- sift/common/type/v1/channel_data_type_pb2_grpc.pyi +17 -0
- sift/common/type/v1/channel_enum_type_pb2.py +32 -0
- sift/common/type/v1/channel_enum_type_pb2.pyi +29 -0
- sift/common/type/v1/channel_enum_type_pb2_grpc.py +4 -0
- sift/common/type/v1/channel_enum_type_pb2_grpc.pyi +17 -0
- sift/common/type/v1/organization_pb2.py +27 -0
- sift/common/type/v1/organization_pb2.pyi +29 -0
- sift/common/type/v1/organization_pb2_grpc.py +4 -0
- sift/common/type/v1/organization_pb2_grpc.pyi +17 -0
- sift/common/type/v1/resource_identifier_pb2.py +46 -0
- sift/common/type/v1/resource_identifier_pb2.pyi +145 -0
- sift/common/type/v1/resource_identifier_pb2_grpc.py +4 -0
- sift/common/type/v1/resource_identifier_pb2_grpc.pyi +17 -0
- sift/common/type/v1/user_pb2.py +33 -0
- sift/common/type/v1/user_pb2.pyi +36 -0
- sift/common/type/v1/user_pb2_grpc.py +4 -0
- sift/common/type/v1/user_pb2_grpc.pyi +17 -0
- sift/data/__init__.py +0 -0
- sift/data/v1/__init__.py +0 -0
- sift/data/v1/data_pb2.py +212 -0
- sift/data/v1/data_pb2.pyi +745 -0
- sift/data/v1/data_pb2_grpc.py +67 -0
- sift/data/v1/data_pb2_grpc.pyi +44 -0
- sift/ingest/__init__.py +0 -0
- sift/ingest/v1/__init__.py +0 -0
- sift/ingest/v1/ingest_pb2.py +35 -0
- sift/ingest/v1/ingest_pb2.pyi +118 -0
- sift/ingest/v1/ingest_pb2_grpc.py +66 -0
- sift/ingest/v1/ingest_pb2_grpc.pyi +41 -0
- sift/ingestion_configs/__init__.py +0 -0
- sift/ingestion_configs/v1/__init__.py +0 -0
- sift/ingestion_configs/v1/ingestion_configs_pb2.py +115 -0
- sift/ingestion_configs/v1/ingestion_configs_pb2.pyi +332 -0
- sift/ingestion_configs/v1/ingestion_configs_pb2_grpc.py +203 -0
- sift/ingestion_configs/v1/ingestion_configs_pb2_grpc.pyi +124 -0
- sift/notifications/__init__.py +0 -0
- sift/notifications/v1/__init__.py +0 -0
- sift/notifications/v1/notifications_pb2.py +64 -0
- sift/notifications/v1/notifications_pb2.pyi +225 -0
- sift/notifications/v1/notifications_pb2_grpc.py +101 -0
- sift/notifications/v1/notifications_pb2_grpc.pyi +64 -0
- sift/ping/__init__.py +0 -0
- sift/ping/v1/__init__.py +0 -0
- sift/ping/v1/ping_pb2.py +38 -0
- sift/ping/v1/ping_pb2.pyi +36 -0
- sift/ping/v1/ping_pb2_grpc.py +66 -0
- sift/ping/v1/ping_pb2_grpc.pyi +41 -0
- sift/remote_files/__init__.py +0 -0
- sift/remote_files/v1/__init__.py +0 -0
- sift/remote_files/v1/remote_files_pb2.py +174 -0
- sift/remote_files/v1/remote_files_pb2.pyi +472 -0
- sift/remote_files/v1/remote_files_pb2_grpc.py +271 -0
- sift/remote_files/v1/remote_files_pb2_grpc.pyi +164 -0
- sift/report_templates/__init__.py +0 -0
- sift/report_templates/v1/__init__.py +0 -0
- sift/report_templates/v1/report_templates_pb2.py +146 -0
- sift/report_templates/v1/report_templates_pb2.pyi +381 -0
- sift/report_templates/v1/report_templates_pb2_grpc.py +169 -0
- sift/report_templates/v1/report_templates_pb2_grpc.pyi +104 -0
- sift/reports/__init__.py +0 -0
- sift/reports/v1/__init__.py +0 -0
- sift/reports/v1/reports_pb2.py +193 -0
- sift/reports/v1/reports_pb2.pyi +562 -0
- sift/reports/v1/reports_pb2_grpc.py +205 -0
- sift/reports/v1/reports_pb2_grpc.pyi +136 -0
- sift/rule_evaluation/__init__.py +0 -0
- sift/rule_evaluation/v1/__init__.py +0 -0
- sift/rule_evaluation/v1/rule_evaluation_pb2.py +89 -0
- sift/rule_evaluation/v1/rule_evaluation_pb2.pyi +263 -0
- sift/rule_evaluation/v1/rule_evaluation_pb2_grpc.py +101 -0
- sift/rule_evaluation/v1/rule_evaluation_pb2_grpc.pyi +64 -0
- sift/rules/__init__.py +0 -0
- sift/rules/v1/__init__.py +0 -0
- sift/rules/v1/rules_pb2.py +420 -0
- sift/rules/v1/rules_pb2.pyi +1355 -0
- sift/rules/v1/rules_pb2_grpc.py +577 -0
- sift/rules/v1/rules_pb2_grpc.pyi +351 -0
- sift/runs/__init__.py +0 -0
- sift/runs/v2/__init__.py +0 -0
- sift/runs/v2/runs_pb2.py +150 -0
- sift/runs/v2/runs_pb2.pyi +413 -0
- sift/runs/v2/runs_pb2_grpc.py +271 -0
- sift/runs/v2/runs_pb2_grpc.pyi +164 -0
- sift/saved_searches/__init__.py +0 -0
- sift/saved_searches/v1/__init__.py +0 -0
- sift/saved_searches/v1/saved_searches_pb2.py +144 -0
- sift/saved_searches/v1/saved_searches_pb2.pyi +385 -0
- sift/saved_searches/v1/saved_searches_pb2_grpc.py +237 -0
- sift/saved_searches/v1/saved_searches_pb2_grpc.pyi +144 -0
- sift/tags/__init__.py +0 -0
- sift/tags/v1/__init__.py +0 -0
- sift/tags/v1/tags_pb2.py +49 -0
- sift/tags/v1/tags_pb2.pyi +71 -0
- sift/tags/v1/tags_pb2_grpc.py +4 -0
- sift/tags/v1/tags_pb2_grpc.pyi +17 -0
- sift/users/__init__.py +0 -0
- sift/users/v2/__init__.py +0 -0
- sift/users/v2/users_pb2.py +61 -0
- sift/users/v2/users_pb2.pyi +142 -0
- sift/users/v2/users_pb2_grpc.py +135 -0
- sift/users/v2/users_pb2_grpc.pyi +84 -0
- sift/views/__init__.py +0 -0
- sift/views/v1/__init__.py +0 -0
- sift/views/v1/views_pb2.py +130 -0
- sift/views/v1/views_pb2.pyi +466 -0
- sift/views/v1/views_pb2_grpc.py +305 -0
- sift/views/v1/views_pb2_grpc.pyi +184 -0
- sift_grafana/py.typed +0 -0
- sift_grafana/sift_query_model.py +64 -0
- sift_py/__init__.py +923 -0
- sift_py/_internal/__init__.py +5 -0
- sift_py/_internal/cel.py +18 -0
- sift_py/_internal/channel.py +42 -0
- sift_py/_internal/convert/__init__.py +3 -0
- sift_py/_internal/convert/json.py +24 -0
- sift_py/_internal/convert/protobuf.py +34 -0
- sift_py/_internal/convert/timestamp.py +9 -0
- sift_py/_internal/test_util/__init__.py +0 -0
- sift_py/_internal/test_util/channel.py +136 -0
- sift_py/_internal/test_util/fn.py +14 -0
- sift_py/_internal/test_util/server_interceptor.py +62 -0
- sift_py/_internal/time.py +48 -0
- sift_py/_internal/user.py +39 -0
- sift_py/data/__init__.py +171 -0
- sift_py/data/_channel.py +38 -0
- sift_py/data/_deserialize.py +208 -0
- sift_py/data/_deserialize_test.py +134 -0
- sift_py/data/_service_test.py +276 -0
- sift_py/data/_validate.py +10 -0
- sift_py/data/error.py +5 -0
- sift_py/data/query.py +299 -0
- sift_py/data/service.py +497 -0
- sift_py/data_import/__init__.py +130 -0
- sift_py/data_import/_config.py +167 -0
- sift_py/data_import/_config_test.py +166 -0
- sift_py/data_import/_csv_test.py +395 -0
- sift_py/data_import/_status_test.py +176 -0
- sift_py/data_import/_tdms_test.py +238 -0
- sift_py/data_import/ch10.py +157 -0
- sift_py/data_import/config.py +19 -0
- sift_py/data_import/csv.py +259 -0
- sift_py/data_import/status.py +113 -0
- sift_py/data_import/tdms.py +206 -0
- sift_py/data_import/tempfile.py +30 -0
- sift_py/data_import/time_format.py +39 -0
- sift_py/error.py +11 -0
- sift_py/file_attachment/__init__.py +88 -0
- sift_py/file_attachment/_internal/__init__.py +0 -0
- sift_py/file_attachment/_internal/download.py +13 -0
- sift_py/file_attachment/_internal/upload.py +100 -0
- sift_py/file_attachment/_service_test.py +161 -0
- sift_py/file_attachment/entity.py +30 -0
- sift_py/file_attachment/metadata.py +107 -0
- sift_py/file_attachment/service.py +142 -0
- sift_py/grpc/__init__.py +15 -0
- sift_py/grpc/_async_interceptors/__init__.py +0 -0
- sift_py/grpc/_async_interceptors/base.py +72 -0
- sift_py/grpc/_async_interceptors/metadata.py +36 -0
- sift_py/grpc/_interceptors/__init__.py +0 -0
- sift_py/grpc/_interceptors/base.py +61 -0
- sift_py/grpc/_interceptors/context.py +25 -0
- sift_py/grpc/_interceptors/metadata.py +33 -0
- sift_py/grpc/_retry.py +70 -0
- sift_py/grpc/keepalive.py +34 -0
- sift_py/grpc/transport.py +250 -0
- sift_py/grpc/transport_test.py +170 -0
- sift_py/ingestion/__init__.py +6 -0
- sift_py/ingestion/_internal/__init__.py +6 -0
- sift_py/ingestion/_internal/channel.py +12 -0
- sift_py/ingestion/_internal/error.py +10 -0
- sift_py/ingestion/_internal/ingest.py +350 -0
- sift_py/ingestion/_internal/ingest_test.py +357 -0
- sift_py/ingestion/_internal/ingestion_config.py +130 -0
- sift_py/ingestion/_internal/run.py +46 -0
- sift_py/ingestion/_service_test.py +478 -0
- sift_py/ingestion/buffer.py +189 -0
- sift_py/ingestion/channel.py +422 -0
- sift_py/ingestion/config/__init__.py +3 -0
- sift_py/ingestion/config/telemetry.py +281 -0
- sift_py/ingestion/config/telemetry_test.py +405 -0
- sift_py/ingestion/config/yaml/__init__.py +0 -0
- sift_py/ingestion/config/yaml/error.py +44 -0
- sift_py/ingestion/config/yaml/load.py +126 -0
- sift_py/ingestion/config/yaml/spec.py +58 -0
- sift_py/ingestion/config/yaml/test_load.py +25 -0
- sift_py/ingestion/flow.py +73 -0
- sift_py/ingestion/manager.py +99 -0
- sift_py/ingestion/rule/__init__.py +4 -0
- sift_py/ingestion/rule/config.py +11 -0
- sift_py/ingestion/service.py +237 -0
- sift_py/py.typed +0 -0
- sift_py/report_templates/__init__.py +0 -0
- sift_py/report_templates/_config_test.py +34 -0
- sift_py/report_templates/_service_test.py +94 -0
- sift_py/report_templates/config.py +36 -0
- sift_py/report_templates/service.py +171 -0
- sift_py/rest.py +29 -0
- sift_py/rule/__init__.py +0 -0
- sift_py/rule/_config_test.py +109 -0
- sift_py/rule/_service_test.py +168 -0
- sift_py/rule/config.py +229 -0
- sift_py/rule/service.py +484 -0
- sift_py/yaml/__init__.py +0 -0
- sift_py/yaml/_channel_test.py +169 -0
- sift_py/yaml/_rule_test.py +207 -0
- sift_py/yaml/channel.py +224 -0
- sift_py/yaml/report_templates.py +73 -0
- sift_py/yaml/rule.py +321 -0
- sift_py/yaml/utils.py +15 -0
- sift_stack_py-0.3.2.dist-info/LICENSE +7 -0
- sift_stack_py-0.3.2.dist-info/METADATA +109 -0
- sift_stack_py-0.3.2.dist-info/RECORD +291 -0
- sift_stack_py-0.3.2.dist-info/WHEEL +5 -0
- 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
|