wizata-dsapi 2.1.0.dev17__tar.gz → 2.1.0.dev19__tar.gz
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.
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/PKG-INFO +1 -1
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/api_config.py +34 -6
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/api_interface.py +20 -3
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/deployment.py +6 -0
- wizata_dsapi-2.1.0.dev19/wizata_dsapi/version.py +1 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/wizata_dsapi_client.py +442 -35
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi.egg-info/PKG-INFO +1 -1
- wizata_dsapi-2.1.0.dev17/wizata_dsapi/version.py +0 -1
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/LICENSE.txt +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/README.rst +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/setup.cfg +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/setup.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/__init__.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/api_dto.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/bucket.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/business_label.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/context.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/dashboard.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/dataframe_toolkit.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/datapoint.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/datastore.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/ds_dataframe.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/dsapi_json_encoder.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/edge_config.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/edge_device.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/edge_module.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/evaluation.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/execution.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/execution_log.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/experiment.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/graylog_log.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/group_system.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/ilogger.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/insight.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/mlmodel.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/mobile_asset.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/model_toolkit.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/models/__init__.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/models/common.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/notification.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/paged_query_result.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/pipeline.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/pipeline_image.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/plot.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/plots/__init__.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/plots/common.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/plots/theme.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/request.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/script.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/scripts/__init__.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/scripts/common.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/search.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/solution_component.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/streamlit_utils.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/template.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/template_config.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/trigger.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/twin.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/twinregistration.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/user.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/wizard_function.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/wizard_request.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi/words.py +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi.egg-info/SOURCES.txt +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi.egg-info/dependency_links.txt +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi.egg-info/requires.txt +0 -0
- {wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi.egg-info/top_level.txt +0 -0
|
@@ -17,10 +17,33 @@ from .trigger import Trigger
|
|
|
17
17
|
from .twin import Twin, TwinType
|
|
18
18
|
from .pipeline import Pipeline
|
|
19
19
|
from .pipeline_image import PipelineImage
|
|
20
|
+
from .deployment import Deployment
|
|
20
21
|
from .insight import Insight
|
|
21
22
|
from .edge_device import EdgeDevice
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
# Entities withdrawn from the client, and what to say instead.
|
|
26
|
+
#
|
|
27
|
+
# Removing one from `_registry` alone would fail with "Api entity triggers is not
|
|
28
|
+
# supported", which reads as a bug in the client. These messages name the
|
|
29
|
+
# replacement, so the first person to hit it can act instead of filing.
|
|
30
|
+
#
|
|
31
|
+
# The DTO itself is NOT removed — `wizata_dsapi.Trigger` is still imported and used
|
|
32
|
+
# by the platform internally (the 12.1 adopt-triggers migration reads them through
|
|
33
|
+
# `ApiDataManager.backend_api`, which never consults this registry). What goes is
|
|
34
|
+
# the ability to schedule through the public client.
|
|
35
|
+
_retired = {
|
|
36
|
+
"triggers": (
|
|
37
|
+
Trigger,
|
|
38
|
+
"triggers are no longer managed through this client (12.1). A schedule now "
|
|
39
|
+
"belongs to a Deployment, which owns its trigger and knows how to pause, "
|
|
40
|
+
"resume and remove it: use api.deploy(...) and the deployment methods. "
|
|
41
|
+
"Deleting a trigger on its own would leave a Deployment record claiming a "
|
|
42
|
+
"schedule that no longer exists, and nothing would find it again."
|
|
43
|
+
),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
24
47
|
# init APIs supported operations
|
|
25
48
|
_registry = {
|
|
26
49
|
"business_labels":
|
|
@@ -41,6 +64,17 @@ _registry = {
|
|
|
41
64
|
"cloud_dsapi": ['lists', 'get_by_id', 'get_by_key', 'create', 'update', 'delete', 'search'],
|
|
42
65
|
"cloud_context": ['get_by_id', 'get_by_key', 'create', 'update', 'delete', 'search']
|
|
43
66
|
},
|
|
67
|
+
"deployments":
|
|
68
|
+
{
|
|
69
|
+
"class": Deployment,
|
|
70
|
+
# No 'create': a deployment is not posted into existence, it is the
|
|
71
|
+
# RESULT of deploying a pipeline — see WizataDSAPIClient.deploy, which
|
|
72
|
+
# runs merge, release, image and trigger as one call and returns the
|
|
73
|
+
# record to poll. Offering create here would invite a half-built record
|
|
74
|
+
# with no schedule behind it.
|
|
75
|
+
"cloud_dsapi": ['lists', 'get_by_id', 'update', 'delete', 'search'],
|
|
76
|
+
"cloud_context": []
|
|
77
|
+
},
|
|
44
78
|
"datastores":
|
|
45
79
|
{
|
|
46
80
|
"class": DataStore,
|
|
@@ -137,12 +171,6 @@ _registry = {
|
|
|
137
171
|
"cloud_dsapi": ['lists', 'get_by_id', 'create', 'update', 'delete'],
|
|
138
172
|
"cloud_context": []
|
|
139
173
|
},
|
|
140
|
-
"triggers":
|
|
141
|
-
{
|
|
142
|
-
"class": Trigger,
|
|
143
|
-
"cloud_dsapi": ['lists', 'get_by_id', 'create', 'update', 'delete'],
|
|
144
|
-
"cloud_context": ['get_by_id']
|
|
145
|
-
},
|
|
146
174
|
"twins":
|
|
147
175
|
{
|
|
148
176
|
"class": Twin,
|
|
@@ -134,14 +134,31 @@ class ApiInterface:
|
|
|
134
134
|
if entity in self._dto_registry.keys():
|
|
135
135
|
self._verify_operation(entity, operation)
|
|
136
136
|
return self._dto_registry[entity]["class"]
|
|
137
|
-
|
|
138
|
-
raise TypeError(f"Api entity {entity} is not supported.")
|
|
137
|
+
self._refuse(entity)
|
|
139
138
|
else:
|
|
140
139
|
for entity_name in self._dto_registry:
|
|
141
140
|
if self._dto_registry[entity_name]["class"] == entity:
|
|
142
141
|
self._verify_operation(entity_name, operation)
|
|
143
142
|
return entity
|
|
144
|
-
|
|
143
|
+
self._refuse(entity)
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def _refuse(entity):
|
|
147
|
+
"""
|
|
148
|
+
Raise for an entity this client does not serve, saying WHY when it is one
|
|
149
|
+
that was withdrawn rather than one that never existed.
|
|
150
|
+
|
|
151
|
+
"Api entity triggers is not supported" reads as a bug in the client, and the
|
|
152
|
+
person reading it has no way to know a schedule is now a Deployment. Both
|
|
153
|
+
the plural name and the DTO class are matched, since callers reach the
|
|
154
|
+
registry through either.
|
|
155
|
+
"""
|
|
156
|
+
from .api_config import _retired
|
|
157
|
+
|
|
158
|
+
for name, (dto, message) in _retired.items():
|
|
159
|
+
if entity == name or entity is dto:
|
|
160
|
+
raise TypeError(f"Api entity {name} is no longer supported: {message}")
|
|
161
|
+
raise TypeError(f"Api entity {entity} is not supported.")
|
|
145
162
|
|
|
146
163
|
def _verify_operation(self,
|
|
147
164
|
entity,
|
|
@@ -210,6 +210,12 @@ class Deployment(ApiDto):
|
|
|
210
210
|
if "updatedDate" in obj.keys() and obj["updatedDate"] is not None:
|
|
211
211
|
self.updatedDate = obj["updatedDate"]
|
|
212
212
|
|
|
213
|
+
def to_dict(self) -> dict:
|
|
214
|
+
"""Serialize the Deployment. Alias of :meth:`to_json`, which is the
|
|
215
|
+
canonical implementation here — the DTO convention accepts either
|
|
216
|
+
spelling, and the client calls ``to_dict``."""
|
|
217
|
+
return self.to_json()
|
|
218
|
+
|
|
213
219
|
def to_json(self, target: str = None):
|
|
214
220
|
obj = {
|
|
215
221
|
"id": str(self.deployment_id),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.1.0.dev19"
|
|
@@ -53,6 +53,7 @@ from .api_interface import ApiInterface
|
|
|
53
53
|
from .mobile_asset import MobileAssetTile, MobileAssetConfig
|
|
54
54
|
from .version import __version__
|
|
55
55
|
import ast
|
|
56
|
+
from .deployment import Deployment, DeploymentStatus
|
|
56
57
|
from .api_config import _registry
|
|
57
58
|
|
|
58
59
|
from .streamlit_utils import get_streamlit_token, get_streamlit_domain
|
|
@@ -713,39 +714,28 @@ class WizataDSAPIClient(ApiInterface, ApiDtoInterface):
|
|
|
713
714
|
else:
|
|
714
715
|
return self.create(obj)
|
|
715
716
|
|
|
716
|
-
def
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
"""
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
:
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
:param interval: interval in milliseconds.
|
|
739
|
-
:param template: template to fetch.
|
|
740
|
-
:param twin: hardware ID of twin to fetch based on template.
|
|
741
|
-
:param null: By default at 'drop' and dropping NaN values. If not intended behavior please set it to 'ignore' or 'all'.
|
|
742
|
-
:param group: can be use to set group system and event retrieving instructions (dict or list[dict]).
|
|
743
|
-
:param filters: dict of filters.
|
|
744
|
-
:param options: dict of options.
|
|
745
|
-
:param field: by default 'value' if none, accept str or list (e.g. value, reliability, eventId, ...)
|
|
746
|
-
:param bucket: specify on which bucket the query applies.
|
|
747
|
-
:param scopes: list of scope filters for SCOPED datapoints (e.g. ["normal", {"vibration": {"gte": 0.6}}]).
|
|
748
|
-
:return: dataframe
|
|
717
|
+
def _build_request(self,
|
|
718
|
+
datapoints: list = None,
|
|
719
|
+
start: datetime = None,
|
|
720
|
+
end: datetime = None,
|
|
721
|
+
interval: int = None,
|
|
722
|
+
agg_method: str = "mean",
|
|
723
|
+
template: str = None,
|
|
724
|
+
twin: str = None,
|
|
725
|
+
null: str = None,
|
|
726
|
+
filters: dict = None,
|
|
727
|
+
options: dict = None,
|
|
728
|
+
group=None,
|
|
729
|
+
field=None,
|
|
730
|
+
bucket: str = None,
|
|
731
|
+
tags: dict = None,
|
|
732
|
+
scopes: list = None) -> Request:
|
|
733
|
+
"""
|
|
734
|
+
Build the Request behind a time-series query.
|
|
735
|
+
|
|
736
|
+
Shared by :meth:`query` and :meth:`query_json` so the two cannot disagree
|
|
737
|
+
about what an argument means. They differ only in what they ask the server
|
|
738
|
+
to send back — a DataFrame or described JSON — never in what they ask for.
|
|
749
739
|
"""
|
|
750
740
|
request = Request()
|
|
751
741
|
|
|
@@ -796,7 +786,47 @@ class WizataDSAPIClient(ApiInterface, ApiDtoInterface):
|
|
|
796
786
|
if bucket is not None:
|
|
797
787
|
request.bucket = bucket
|
|
798
788
|
|
|
799
|
-
return
|
|
789
|
+
return request
|
|
790
|
+
|
|
791
|
+
def query(self,
|
|
792
|
+
datapoints: list = None,
|
|
793
|
+
start: datetime = None,
|
|
794
|
+
end: datetime = None,
|
|
795
|
+
interval: int = None,
|
|
796
|
+
agg_method: str = "mean",
|
|
797
|
+
template: str = None,
|
|
798
|
+
twin: str = None,
|
|
799
|
+
null: str = None,
|
|
800
|
+
filters: dict = None,
|
|
801
|
+
options: dict = None,
|
|
802
|
+
group = None,
|
|
803
|
+
field=None,
|
|
804
|
+
bucket: str = None,
|
|
805
|
+
tags: dict = None,
|
|
806
|
+
scopes: list = None) -> pandas.DataFrame:
|
|
807
|
+
"""
|
|
808
|
+
Query a dataframe from API.
|
|
809
|
+
:param agg_method:
|
|
810
|
+
:param datapoints: list of datapoints to fetch.
|
|
811
|
+
:param start: start datetime of range to fetch
|
|
812
|
+
:param end: end datetime of range to fetch
|
|
813
|
+
:param interval: interval in milliseconds.
|
|
814
|
+
:param template: template to fetch.
|
|
815
|
+
:param twin: hardware ID of twin to fetch based on template.
|
|
816
|
+
:param null: By default at 'drop' and dropping NaN values. If not intended behavior please set it to 'ignore' or 'all'.
|
|
817
|
+
:param group: can be use to set group system and event retrieving instructions (dict or list[dict]).
|
|
818
|
+
:param filters: dict of filters.
|
|
819
|
+
:param options: dict of options.
|
|
820
|
+
:param field: by default 'value' if none, accept str or list (e.g. value, reliability, eventId, ...)
|
|
821
|
+
:param bucket: specify on which bucket the query applies.
|
|
822
|
+
:param scopes: list of scope filters for SCOPED datapoints (e.g. ["normal", {"vibration": {"gte": 0.6}}]).
|
|
823
|
+
:return: dataframe
|
|
824
|
+
"""
|
|
825
|
+
return self._query(request=self._build_request(
|
|
826
|
+
datapoints=datapoints, start=start, end=end, interval=interval,
|
|
827
|
+
agg_method=agg_method, template=template, twin=twin, null=null,
|
|
828
|
+
filters=filters, options=options, group=group, field=field,
|
|
829
|
+
bucket=bucket, tags=tags, scopes=scopes))
|
|
800
830
|
|
|
801
831
|
def batch_query(self,
|
|
802
832
|
step: timedelta,
|
|
@@ -1124,7 +1154,12 @@ class WizataDSAPIClient(ApiInterface, ApiDtoInterface):
|
|
|
1124
1154
|
else:
|
|
1125
1155
|
raise ValueError('No plot has been fetch.')
|
|
1126
1156
|
elif figure is not None:
|
|
1127
|
-
|
|
1157
|
+
# Read `figure`, not `plot.figure` — with only a figure passed there is no
|
|
1158
|
+
# plot to read it from. A live plot's envelope carries the figure as the
|
|
1159
|
+
# JSON string it was stored as, an experiment plot as a dict once decoded,
|
|
1160
|
+
# and both must render.
|
|
1161
|
+
return plotly.io.from_json(figure if isinstance(figure, str)
|
|
1162
|
+
else json.dumps(figure))
|
|
1128
1163
|
elif plot_id is not None:
|
|
1129
1164
|
plot = self.get(Plot(plot_id=plot_id))
|
|
1130
1165
|
if plot.figure is not None:
|
|
@@ -1156,6 +1191,90 @@ class WizataDSAPIClient(ApiInterface, ApiDtoInterface):
|
|
|
1156
1191
|
else:
|
|
1157
1192
|
raise TypeError("execution must be an ExecutionLog")
|
|
1158
1193
|
|
|
1194
|
+
@staticmethod
|
|
1195
|
+
def _pipeline_ref(pipeline) -> str:
|
|
1196
|
+
"""
|
|
1197
|
+
Path segment addressing a pipeline, from a key, a UUID or a Pipeline.
|
|
1198
|
+
|
|
1199
|
+
The live-plot route accepts both the key and the UUID, so a caller passes
|
|
1200
|
+
whichever it already has. A Pipeline is asked for its key first: a deployed
|
|
1201
|
+
plot is addressed by pipeline KEY everywhere else it appears (the blob path,
|
|
1202
|
+
ExecutionLog.plots), so the key is the reference that matches what comes back.
|
|
1203
|
+
"""
|
|
1204
|
+
if isinstance(pipeline, Pipeline):
|
|
1205
|
+
reference = pipeline.key if pipeline.key is not None else pipeline.pipeline_id
|
|
1206
|
+
else:
|
|
1207
|
+
reference = pipeline
|
|
1208
|
+
if reference is None:
|
|
1209
|
+
raise ValueError("a pipeline key, UUID or Pipeline is required")
|
|
1210
|
+
return urllib.parse.quote(str(reference), safe="")
|
|
1211
|
+
|
|
1212
|
+
def deployed_plots(self, pipeline, twin: str = None) -> list:
|
|
1213
|
+
"""
|
|
1214
|
+
list a deployed pipeline's LIVE plots - what its deployments are currently
|
|
1215
|
+
drawing, one per (twin, plot step).
|
|
1216
|
+
|
|
1217
|
+
A live plot is overwritten on every triggered run, so there is exactly one
|
|
1218
|
+
current figure per plot and no history to page through. Fetch one with
|
|
1219
|
+
:meth:`deployed_plot`. Experiment plots are elsewhere and unchanged - one blob
|
|
1220
|
+
per execution, see :meth:`plots`.
|
|
1221
|
+
|
|
1222
|
+
:param pipeline: pipeline key, UUID or Pipeline - both references are accepted.
|
|
1223
|
+
:param twin: hardware id, to list only that twin's plots.
|
|
1224
|
+
:return: list of dict {pipelineKey, twin, script, dataframe, key, deploymentId,
|
|
1225
|
+
produced, lastModified, size}. 'produced' is False when the definition has
|
|
1226
|
+
this plot step but no run has written it yet.
|
|
1227
|
+
"""
|
|
1228
|
+
response = self._process_request(
|
|
1229
|
+
method="GET",
|
|
1230
|
+
route=f"{Pipeline.route()}/{self._pipeline_ref(pipeline)}/plots/",
|
|
1231
|
+
params={"twin": twin} if twin is not None else None
|
|
1232
|
+
)
|
|
1233
|
+
return response.json().get("results", [])
|
|
1234
|
+
|
|
1235
|
+
def deployed_plot(self,
|
|
1236
|
+
pipeline,
|
|
1237
|
+
script: str,
|
|
1238
|
+
dataframe: str,
|
|
1239
|
+
twin: str = None,
|
|
1240
|
+
shape: str = "figure") -> dict:
|
|
1241
|
+
"""
|
|
1242
|
+
fetch one of a deployed pipeline's LIVE plots.
|
|
1243
|
+
|
|
1244
|
+
The plot step is identified by BOTH its script and its input dataframe - that
|
|
1245
|
+
pair is the plot's identity, as :meth:`deployed_plots` lists it.
|
|
1246
|
+
|
|
1247
|
+
Returns the envelope, not the bare figure, because the provenance is the point:
|
|
1248
|
+
a live plot is overwritten on every run and 'generatedAt' / 'executionId' say
|
|
1249
|
+
which one wrote it. Render it with ``client.plot(figure=envelope["figure"])``.
|
|
1250
|
+
|
|
1251
|
+
:param pipeline: pipeline key, UUID or Pipeline - both references are accepted.
|
|
1252
|
+
:param script: the plot step's script.
|
|
1253
|
+
:param dataframe: the plot step's input dataframe.
|
|
1254
|
+
:param twin: hardware id - required on a templated pipeline.
|
|
1255
|
+
:param shape: 'figure' (default) for the stored Plotly figure, or 'data' for its
|
|
1256
|
+
traces projected to 'series'.
|
|
1257
|
+
:return: dict with {pipelineKey, twin, script, dataframe, name, executionId,
|
|
1258
|
+
release, sha, imageId, generatedAt} plus 'figure', or 'series' and
|
|
1259
|
+
'notProjected' when shape is 'data'.
|
|
1260
|
+
"""
|
|
1261
|
+
if script is None or dataframe is None:
|
|
1262
|
+
raise ValueError(
|
|
1263
|
+
"script and dataframe are both parts of a plot's identity - pass both"
|
|
1264
|
+
)
|
|
1265
|
+
if shape not in ("figure", "data"):
|
|
1266
|
+
raise ValueError(f"shape must be 'figure' or 'data', got {shape!r}")
|
|
1267
|
+
|
|
1268
|
+
params = {"script": script, "dataframe": dataframe, "format": shape}
|
|
1269
|
+
if twin is not None:
|
|
1270
|
+
params["twin"] = twin
|
|
1271
|
+
response = self._process_request(
|
|
1272
|
+
method="GET",
|
|
1273
|
+
route=f"{Pipeline.route()}/{self._pipeline_ref(pipeline)}/plots/",
|
|
1274
|
+
params=params
|
|
1275
|
+
)
|
|
1276
|
+
return response.json()
|
|
1277
|
+
|
|
1159
1278
|
def upload_model(self,
|
|
1160
1279
|
model_info: ModelInfo,
|
|
1161
1280
|
bytes_content = None):
|
|
@@ -1924,6 +2043,294 @@ class WizataDSAPIClient(ApiInterface, ApiDtoInterface):
|
|
|
1924
2043
|
)
|
|
1925
2044
|
return response.text
|
|
1926
2045
|
|
|
2046
|
+
def query_json(self, include_format: bool = True, **kwargs) -> dict:
|
|
2047
|
+
"""
|
|
2048
|
+
query time-series as JSON, described.
|
|
2049
|
+
|
|
2050
|
+
Takes the same arguments as :meth:`query`. Where ``query`` hands back a
|
|
2051
|
+
DataFrame, this returns the rows as JSON — and by default wraps them in a
|
|
2052
|
+
description of their own shape, so a caller that is not pandas can render
|
|
2053
|
+
them without guessing:
|
|
2054
|
+
|
|
2055
|
+
{"columns": [...], "pivot": ..., "data": [...]}
|
|
2056
|
+
|
|
2057
|
+
That is what the front-end's query form is built on: an aggregated query
|
|
2058
|
+
comes back pivoted with one column per datapoint, a raw one comes back long
|
|
2059
|
+
with a 'value' column, and only the envelope says which you got (DEV-6031).
|
|
2060
|
+
|
|
2061
|
+
A separate method rather than a flag on :meth:`query`, deliberately — a
|
|
2062
|
+
parameter that changes the return type between a DataFrame and a dict makes
|
|
2063
|
+
every caller branch on what it asked for.
|
|
2064
|
+
|
|
2065
|
+
:param include_format: False to get the bare rows without the envelope.
|
|
2066
|
+
:return: dict with columns/pivot/data, or the rows alone when
|
|
2067
|
+
``include_format`` is False.
|
|
2068
|
+
"""
|
|
2069
|
+
request = self._build_request(**kwargs)
|
|
2070
|
+
response = self._process_request(
|
|
2071
|
+
method="POST",
|
|
2072
|
+
route="data/",
|
|
2073
|
+
params={"include-format": "true" if include_format else "false"},
|
|
2074
|
+
data=json.dumps(request.to_json(), cls=DSAPIEncoder)
|
|
2075
|
+
)
|
|
2076
|
+
return response.json()
|
|
2077
|
+
|
|
2078
|
+
# ------------------------------------------------------------------ git branches
|
|
2079
|
+
|
|
2080
|
+
def branches(self, pipeline) -> dict:
|
|
2081
|
+
"""
|
|
2082
|
+
list the branches relevant to a pipeline.
|
|
2083
|
+
|
|
2084
|
+
:param pipeline: pipeline key, UUID or Pipeline.
|
|
2085
|
+
:return: dict with 'default', 'feature' and 'release' - feature branches are
|
|
2086
|
+
this pipeline's drafts, release branches are the frozen refs deployments
|
|
2087
|
+
run from.
|
|
2088
|
+
"""
|
|
2089
|
+
response = self._process_request(
|
|
2090
|
+
method="GET",
|
|
2091
|
+
route=f"{Pipeline.route()}/{self._pipeline_ref(pipeline)}/branches/"
|
|
2092
|
+
)
|
|
2093
|
+
return response.json()
|
|
2094
|
+
|
|
2095
|
+
def create_branch(self, pipeline, name: str = None, source: str = None) -> Pipeline:
|
|
2096
|
+
"""
|
|
2097
|
+
create a branch for a pipeline and return the pipeline as it stands on it.
|
|
2098
|
+
|
|
2099
|
+
:param pipeline: pipeline key, UUID or Pipeline.
|
|
2100
|
+
:param name: branch name. Leave None for a draft, which is named for you.
|
|
2101
|
+
A release is named ``release/<name>``.
|
|
2102
|
+
:param source: ref to branch from; the default branch when None.
|
|
2103
|
+
:return: the Pipeline read at the new branch, with 'branch' set.
|
|
2104
|
+
"""
|
|
2105
|
+
payload = {}
|
|
2106
|
+
if name is not None:
|
|
2107
|
+
payload["name"] = name
|
|
2108
|
+
if source is not None:
|
|
2109
|
+
payload["source"] = source
|
|
2110
|
+
response = self._process_request(
|
|
2111
|
+
method="POST",
|
|
2112
|
+
route=f"{Pipeline.route()}/{self._pipeline_ref(pipeline)}/branches/",
|
|
2113
|
+
data=json.dumps(payload)
|
|
2114
|
+
)
|
|
2115
|
+
return Pipeline.from_dict(response.json())
|
|
2116
|
+
|
|
2117
|
+
def delete_branch(self, pipeline, branch: str) -> None:
|
|
2118
|
+
"""
|
|
2119
|
+
discard a branch.
|
|
2120
|
+
|
|
2121
|
+
A draft goes on demand. A release is refused with 409 while a deployment or a
|
|
2122
|
+
trigger still runs from it - pause or redeploy those first, rather than
|
|
2123
|
+
leaving a schedule pointing at a ref that no longer exists.
|
|
2124
|
+
|
|
2125
|
+
:param pipeline: pipeline key, UUID or Pipeline.
|
|
2126
|
+
:param branch: branch ref.
|
|
2127
|
+
"""
|
|
2128
|
+
self._process_request(
|
|
2129
|
+
method="DELETE",
|
|
2130
|
+
route=f"{Pipeline.route()}/{self._pipeline_ref(pipeline)}/branches/{branch}/"
|
|
2131
|
+
)
|
|
2132
|
+
|
|
2133
|
+
def merge_branch(self, pipeline, branch: str) -> dict:
|
|
2134
|
+
"""
|
|
2135
|
+
merge a draft into the default branch and delete it.
|
|
2136
|
+
|
|
2137
|
+
:param pipeline: pipeline key, UUID or Pipeline.
|
|
2138
|
+
:param branch: the feature branch to publish.
|
|
2139
|
+
:return: dict with 'merged_sha', 'deleted_branch' and 'files_changed'.
|
|
2140
|
+
"""
|
|
2141
|
+
response = self._process_request(
|
|
2142
|
+
method="POST",
|
|
2143
|
+
route=f"{Pipeline.route()}/{self._pipeline_ref(pipeline)}/branches/{branch}/merge"
|
|
2144
|
+
)
|
|
2145
|
+
return response.json()
|
|
2146
|
+
|
|
2147
|
+
def get_pipeline_at(self, pipeline, branch: str) -> Pipeline:
|
|
2148
|
+
"""
|
|
2149
|
+
read a pipeline as it stands on a branch, rather than on the default one.
|
|
2150
|
+
|
|
2151
|
+
:param pipeline: pipeline key, UUID or Pipeline.
|
|
2152
|
+
:param branch: branch ref.
|
|
2153
|
+
:return: Pipeline.
|
|
2154
|
+
"""
|
|
2155
|
+
response = self._process_request(
|
|
2156
|
+
method="GET",
|
|
2157
|
+
route=f"{Pipeline.route()}/{self._pipeline_ref(pipeline)}/branches/{branch}/"
|
|
2158
|
+
)
|
|
2159
|
+
return Pipeline.from_dict(response.json())
|
|
2160
|
+
|
|
2161
|
+
def get_script_at(self, name: str, branch: str) -> Script:
|
|
2162
|
+
"""
|
|
2163
|
+
read a RAW script's source as it stands on a branch.
|
|
2164
|
+
|
|
2165
|
+
A branch run executes the branch's script, so this is what actually runs
|
|
2166
|
+
there - the default-branch copy may differ.
|
|
2167
|
+
|
|
2168
|
+
:param name: script name.
|
|
2169
|
+
:param branch: branch ref.
|
|
2170
|
+
:return: Script.
|
|
2171
|
+
"""
|
|
2172
|
+
response = self._process_request(
|
|
2173
|
+
method="GET",
|
|
2174
|
+
route=f"{Script.route()}/{name}/branches/{branch}/"
|
|
2175
|
+
)
|
|
2176
|
+
return Script.from_dict(response.json())
|
|
2177
|
+
|
|
2178
|
+
def save_script_at(self, script: Script, branch: str) -> None:
|
|
2179
|
+
"""
|
|
2180
|
+
save a RAW script on a branch, leaving the default branch untouched until the
|
|
2181
|
+
branch is merged.
|
|
2182
|
+
|
|
2183
|
+
:param script: Script with its source set.
|
|
2184
|
+
:param branch: branch ref.
|
|
2185
|
+
"""
|
|
2186
|
+
self._process_request(
|
|
2187
|
+
method="PUT",
|
|
2188
|
+
route=f"{Script.route()}/{script.name}/branches/{branch}/",
|
|
2189
|
+
files={"payload": (None, json.dumps(script.to_dict(), cls=DSAPIEncoder))}
|
|
2190
|
+
)
|
|
2191
|
+
|
|
2192
|
+
# ------------------------------------------------------------------ deployments
|
|
2193
|
+
|
|
2194
|
+
def deploy(self,
|
|
2195
|
+
pipeline,
|
|
2196
|
+
twins: list = None,
|
|
2197
|
+
interval: int = None,
|
|
2198
|
+
delay: int = None,
|
|
2199
|
+
packaging: str = None,
|
|
2200
|
+
source: str = None,
|
|
2201
|
+
properties: dict = None,
|
|
2202
|
+
plot: bool = None,
|
|
2203
|
+
train: bool = None,
|
|
2204
|
+
write: bool = None) -> Deployment:
|
|
2205
|
+
"""
|
|
2206
|
+
deploy a pipeline: merge the draft, cut the release, build the image and create
|
|
2207
|
+
the schedule - the whole sequence, as one call.
|
|
2208
|
+
|
|
2209
|
+
Returns immediately with a Deployment to poll; an image build outlasts the
|
|
2210
|
+
gateway timeout, so the record carries the progress instead of the request.
|
|
2211
|
+
Poll it with :meth:`get` on the returned id, or :meth:`await_deployment`.
|
|
2212
|
+
|
|
2213
|
+
:param pipeline: pipeline key, UUID or Pipeline.
|
|
2214
|
+
:param twins: hardware ids or twin UUIDs this schedule covers - both forms are
|
|
2215
|
+
accepted - or ``{twin, target, deviceId}`` entries where target is "cloud"
|
|
2216
|
+
(default) or "edge".
|
|
2217
|
+
:param interval: milliseconds between runs.
|
|
2218
|
+
:param delay: milliseconds to wait after each interval boundary.
|
|
2219
|
+
:param packaging: "image" (default) or "release".
|
|
2220
|
+
:param source: branch or ref to deploy from.
|
|
2221
|
+
:param properties: pipeline properties for every run.
|
|
2222
|
+
:param plot: produce plots on each run. Refused on edge.
|
|
2223
|
+
:param train: train models on each run. Refused on edge, and on an
|
|
2224
|
+
image-packaged deployment - the model would be trained and silently
|
|
2225
|
+
discarded, because the image's frozen model is what predict uses.
|
|
2226
|
+
:param write: write results back. Defaults on.
|
|
2227
|
+
:return: Deployment to poll.
|
|
2228
|
+
"""
|
|
2229
|
+
payload = {}
|
|
2230
|
+
if twins is not None:
|
|
2231
|
+
payload["twins"] = twins
|
|
2232
|
+
for name, value in (("interval", interval), ("delay", delay),
|
|
2233
|
+
("packaging", packaging), ("source", source),
|
|
2234
|
+
("properties", properties)):
|
|
2235
|
+
if value is not None:
|
|
2236
|
+
payload[name] = value
|
|
2237
|
+
for name, value in (("plot", plot), ("train", train), ("write", write)):
|
|
2238
|
+
if value is not None:
|
|
2239
|
+
payload[name] = value
|
|
2240
|
+
response = self._process_request(
|
|
2241
|
+
method="POST",
|
|
2242
|
+
route=f"{Pipeline.route()}/{self._pipeline_ref(pipeline)}/deploy/",
|
|
2243
|
+
data=json.dumps(payload)
|
|
2244
|
+
)
|
|
2245
|
+
return Deployment.from_dict(response.json())
|
|
2246
|
+
|
|
2247
|
+
def deploy_preview(self, pipeline, **kwargs) -> dict:
|
|
2248
|
+
"""
|
|
2249
|
+
plan a deploy without performing it: what would be merged, cut, built and
|
|
2250
|
+
scheduled, and every reason it would be refused.
|
|
2251
|
+
|
|
2252
|
+
Writes nothing, and always answers 200 - read ``ok`` and ``problems`` rather
|
|
2253
|
+
than catching an exception. Takes the same arguments as :meth:`deploy`.
|
|
2254
|
+
|
|
2255
|
+
:param pipeline: pipeline key, UUID or Pipeline.
|
|
2256
|
+
:return: the plan, including 'ok' and 'problems'.
|
|
2257
|
+
"""
|
|
2258
|
+
payload = {k: v for k, v in kwargs.items() if v is not None}
|
|
2259
|
+
response = self._process_request(
|
|
2260
|
+
method="POST",
|
|
2261
|
+
route=f"{Pipeline.route()}/{self._pipeline_ref(pipeline)}/deploy/preview/",
|
|
2262
|
+
data=json.dumps(payload)
|
|
2263
|
+
)
|
|
2264
|
+
return response.json()
|
|
2265
|
+
|
|
2266
|
+
def _deployment_action(self, deployment, action: str) -> Deployment:
|
|
2267
|
+
deployment_id = (deployment.deployment_id
|
|
2268
|
+
if isinstance(deployment, Deployment) else deployment)
|
|
2269
|
+
response = self._process_request(
|
|
2270
|
+
method="POST",
|
|
2271
|
+
route=f"{Deployment.route()}/{deployment_id}/{action}/"
|
|
2272
|
+
)
|
|
2273
|
+
return Deployment.from_dict(response.json())
|
|
2274
|
+
|
|
2275
|
+
def pause_deployment(self, deployment) -> Deployment:
|
|
2276
|
+
"""
|
|
2277
|
+
pause a deployment: delete its trigger, keep the record.
|
|
2278
|
+
|
|
2279
|
+
Synchronous. The manifest is kept, so :meth:`resume_deployment` rebuilds the
|
|
2280
|
+
same schedule rather than asking for it again.
|
|
2281
|
+
|
|
2282
|
+
:param deployment: Deployment or its id.
|
|
2283
|
+
:return: the updated Deployment.
|
|
2284
|
+
"""
|
|
2285
|
+
return self._deployment_action(deployment, "pause")
|
|
2286
|
+
|
|
2287
|
+
def resume_deployment(self, deployment) -> Deployment:
|
|
2288
|
+
"""
|
|
2289
|
+
resume a paused deployment, re-creating its trigger from the stored manifest.
|
|
2290
|
+
|
|
2291
|
+
:param deployment: Deployment or its id.
|
|
2292
|
+
:return: the updated Deployment.
|
|
2293
|
+
"""
|
|
2294
|
+
return self._deployment_action(deployment, "resume")
|
|
2295
|
+
|
|
2296
|
+
def retry_deployment(self, deployment) -> Deployment:
|
|
2297
|
+
"""
|
|
2298
|
+
continue a failed or interrupted deployment from where it stopped.
|
|
2299
|
+
|
|
2300
|
+
Safe to call repeatedly: a merge or a release cut cannot be undone, so
|
|
2301
|
+
completed steps are skipped rather than replayed.
|
|
2302
|
+
|
|
2303
|
+
:param deployment: Deployment or its id.
|
|
2304
|
+
:return: the updated Deployment.
|
|
2305
|
+
"""
|
|
2306
|
+
return self._deployment_action(deployment, "retry")
|
|
2307
|
+
|
|
2308
|
+
def await_deployment(self,
|
|
2309
|
+
deployment,
|
|
2310
|
+
timeout: int = 900,
|
|
2311
|
+
interval: int = 5) -> Deployment:
|
|
2312
|
+
"""
|
|
2313
|
+
poll a deployment until it stops moving.
|
|
2314
|
+
|
|
2315
|
+
:param deployment: Deployment or its id.
|
|
2316
|
+
:param timeout: seconds before giving up waiting.
|
|
2317
|
+
:param interval: seconds between polls.
|
|
2318
|
+
:return: the Deployment in its final state - check ``status``, which is
|
|
2319
|
+
'deployed' on success and 'failed' otherwise.
|
|
2320
|
+
"""
|
|
2321
|
+
import time
|
|
2322
|
+
deployment_id = (deployment.deployment_id
|
|
2323
|
+
if isinstance(deployment, Deployment) else deployment)
|
|
2324
|
+
deadline = time.time() + timeout
|
|
2325
|
+
current = self.get(deployment_id=deployment_id)
|
|
2326
|
+
while time.time() < deadline:
|
|
2327
|
+
if current.status in (DeploymentStatus.DEPLOYED, DeploymentStatus.FAILED,
|
|
2328
|
+
DeploymentStatus.PAUSED):
|
|
2329
|
+
return current
|
|
2330
|
+
time.sleep(interval)
|
|
2331
|
+
current = self.get(deployment_id=deployment_id)
|
|
2332
|
+
return current
|
|
2333
|
+
|
|
1927
2334
|
def build_image(self, key: str, branch: str = None) -> str:
|
|
1928
2335
|
"""
|
|
1929
2336
|
build an image of a pipeline, store it on the image repository and return its pipeline image id.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.1.0.dev17"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{wizata_dsapi-2.1.0.dev17 → wizata_dsapi-2.1.0.dev19}/wizata_dsapi.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|