wizata-dsapi 2.2.0.dev17__tar.gz → 2.2.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.
Files changed (72) hide show
  1. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/PKG-INFO +1 -1
  2. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/__init__.py +1 -0
  3. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/edge_device.py +80 -17
  4. wizata_dsapi-2.2.0.dev19/wizata_dsapi/edge_package.py +128 -0
  5. wizata_dsapi-2.2.0.dev19/wizata_dsapi/version.py +1 -0
  6. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/wizata_dsapi_client.py +20 -2
  7. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi.egg-info/PKG-INFO +1 -1
  8. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi.egg-info/SOURCES.txt +1 -0
  9. wizata_dsapi-2.2.0.dev17/wizata_dsapi/version.py +0 -1
  10. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/LICENSE.txt +0 -0
  11. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/README.rst +0 -0
  12. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/setup.cfg +0 -0
  13. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/setup.py +0 -0
  14. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/api_config.py +0 -0
  15. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/api_dto.py +0 -0
  16. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/api_interface.py +0 -0
  17. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/bucket.py +0 -0
  18. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/business_label.py +0 -0
  19. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/connection.py +0 -0
  20. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/connection_types.py +0 -0
  21. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/context.py +0 -0
  22. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/dashboard.py +0 -0
  23. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/dataframe_toolkit.py +0 -0
  24. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/datapoint.py +0 -0
  25. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/datastore.py +0 -0
  26. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/deployment.py +0 -0
  27. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/ds_dataframe.py +0 -0
  28. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/dsapi_json_encoder.py +0 -0
  29. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/edge_config.py +0 -0
  30. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/edge_module.py +0 -0
  31. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/evaluation.py +0 -0
  32. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/execution.py +0 -0
  33. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/execution_log.py +0 -0
  34. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/experiment.py +0 -0
  35. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/graylog_log.py +0 -0
  36. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/group_system.py +0 -0
  37. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/ilogger.py +0 -0
  38. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/insight.py +0 -0
  39. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/inventory.py +0 -0
  40. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/mlmodel.py +0 -0
  41. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/mobile_asset.py +0 -0
  42. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/model_toolkit.py +0 -0
  43. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/models/__init__.py +0 -0
  44. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/models/common.py +0 -0
  45. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/notification.py +0 -0
  46. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/paged_query_result.py +0 -0
  47. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/pipeline.py +0 -0
  48. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/pipeline_image.py +0 -0
  49. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/plot.py +0 -0
  50. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/plots/__init__.py +0 -0
  51. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/plots/common.py +0 -0
  52. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/plots/theme.py +0 -0
  53. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/product.py +0 -0
  54. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/request.py +0 -0
  55. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/script.py +0 -0
  56. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/scripts/__init__.py +0 -0
  57. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/scripts/common.py +0 -0
  58. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/search.py +0 -0
  59. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/solution_component.py +0 -0
  60. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/streamlit_utils.py +0 -0
  61. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/template.py +0 -0
  62. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/template_config.py +0 -0
  63. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/trigger.py +0 -0
  64. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/twin.py +0 -0
  65. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/twinregistration.py +0 -0
  66. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/user.py +0 -0
  67. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/wizard_function.py +0 -0
  68. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/wizard_request.py +0 -0
  69. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi/words.py +0 -0
  70. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi.egg-info/dependency_links.txt +0 -0
  71. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi.egg-info/requires.txt +0 -0
  72. {wizata_dsapi-2.2.0.dev17 → wizata_dsapi-2.2.0.dev19}/wizata_dsapi.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wizata_dsapi
3
- Version: 2.2.0.dev17
3
+ Version: 2.2.0.dev19
4
4
  Summary: Wizata Data Science Toolkit
5
5
  Author: Wizata S.A.
6
6
  Author-email: info@wizata.com
@@ -61,6 +61,7 @@ from .notification import NotificationScope, DeviceToken, AlertRule, AlertActive
61
61
  from .edge_device import EdgeDevice, EdgeDeviceStatus, EdgeDeviceType, EdgeDeviceProvisioning
62
62
  from .edge_module import EdgeModule, EdgeModuleType, WIZATA_MODULES
63
63
  from .edge_config import EdgeConsumer, EdgeWriter, EdgeTrigger, EdgeFile, EdgeConfig, EdgeCommand
64
+ from .edge_package import EdgePackage, EdgePackageOperation, EdgeRelease
64
65
  from .connection import Connection, ConnectionMapping, ConnectionDirection, TransformKind, \
65
66
  EdgeDeviceKey, EdgeDeviceKeyStatus
66
67
  from .connection_types import ConnectionType, CONNECTION_TYPES, TRANSFORM_SCHEMAS, get_connection_type, \
@@ -9,6 +9,7 @@ class EdgeDeviceStatus(Enum):
9
9
  """
10
10
  WAITING_FOR_REGISTRATION = "WaitingForRegistration"
11
11
  REGISTERED = "Registered"
12
+ DEPLOYING = "Deploying"
12
13
  DEPLOYED = "Deployed"
13
14
  FAILED = "Failed"
14
15
  OFFLINE = "Offline"
@@ -35,25 +36,41 @@ class EdgeDevice(ApiDto):
35
36
  """
36
37
  Edge device registered on the platform.
37
38
 
38
- :ivar uuid.UUID edge_device_id: technical id of the edge device.
39
- :ivar str device_id: logical device identifier string.
39
+ The device id is its only identifier (DEV-6101): the name it runs as (its IoT Hub
40
+ device id), and what connections, keys and deployment targets refer to.
41
+
42
+ :ivar str device_id: the device identifier.
40
43
  :ivar str name: display name of the edge device.
41
44
  :ivar str twin_name: name of the associated twin.
42
45
  :ivar uuid.UUID twin_id: technical id of the associated twin.
43
46
  :ivar EdgeDeviceStatus status: current status of the device.
44
- :ivar int status_code: numeric status code.
47
+ :ivar int status_code: last ``$edgeAgent`` ``lastDesiredStatus.code`` read (417, 200, 406...).
45
48
  :ivar EdgeDeviceType device_type: type of the edge device.
46
49
  :ivar EdgeDeviceProvisioning provisioning: how the device is installed.
47
50
  :ivar int created_at: creation time, epoch ms (the PostgreSQL registry, DEV-6015).
48
51
  :ivar int updated_at: last update time, epoch ms.
49
52
  :ivar str connection_state: IoT Hub connection state ('Connected' or 'Disconnected'), enriched from IoT Hub.
50
53
  :ivar str last_activity_time: last device activity timestamp from IoT Hub.
54
+ :ivar str applied_hash: digest of the manifest the platform last applied (DEV-6101).
55
+ :ivar int applied_version: ``$edgeAgent`` desired ``$version`` that apply produced.
56
+ :ivar int applied_at: when it was applied, epoch ms.
57
+ :ivar dict image_overrides: module -> image, winning over the release's images.
58
+ :ivar str runner_python_version: Python version of the edge's pipeline runner.
59
+ :ivar bool managed: the platform keeps this device's manifest ("deploy modules").
60
+ :ivar dict applied_modules: what was last applied: module -> {image, env names}.
61
+ :ivar str reconcile_error: why the last apply failed, until one succeeds.
62
+ :ivar int last_seen_at: last heartbeat of a manual edge, epoch ms (DEV-6109).
51
63
  """
52
64
 
53
65
  @classmethod
54
66
  def route(cls):
55
67
  return "edgedevices"
56
68
 
69
+ @classmethod
70
+ def get_id_type(cls) -> type:
71
+ """The device id (a string) is the edge's only identifier (DEV-6101)."""
72
+ return str
73
+
57
74
  @classmethod
58
75
  def from_dict(cls, data):
59
76
  obj = EdgeDevice()
@@ -61,7 +78,6 @@ class EdgeDevice(ApiDto):
61
78
  return obj
62
79
 
63
80
  def __init__(self,
64
- edge_device_id: uuid.UUID = None,
65
81
  device_id: str = None,
66
82
  name: str = None,
67
83
  twin_name: str = None,
@@ -73,11 +89,16 @@ class EdgeDevice(ApiDto):
73
89
  last_activity_time: str = None,
74
90
  provisioning: EdgeDeviceProvisioning = None,
75
91
  created_at: int = None,
76
- updated_at: int = None):
77
- if edge_device_id is None:
78
- self.edge_device_id = uuid.uuid4()
79
- else:
80
- self.edge_device_id = edge_device_id
92
+ updated_at: int = None,
93
+ applied_hash: str = None,
94
+ applied_version: int = None,
95
+ applied_at: int = None,
96
+ image_overrides: dict = None,
97
+ runner_python_version: str = None,
98
+ managed: bool = None,
99
+ applied_modules: dict = None,
100
+ reconcile_error: str = None,
101
+ last_seen_at: int = None):
81
102
  self.device_id = device_id
82
103
  self.name = name
83
104
  self.twin_name = twin_name
@@ -90,22 +111,29 @@ class EdgeDevice(ApiDto):
90
111
  self.provisioning = provisioning
91
112
  self.created_at = created_at
92
113
  self.updated_at = updated_at
114
+ self.applied_hash = applied_hash
115
+ self.applied_version = applied_version
116
+ self.applied_at = applied_at
117
+ self.image_overrides = image_overrides
118
+ self.runner_python_version = runner_python_version
119
+ self.managed = managed
120
+ self.applied_modules = applied_modules
121
+ self.reconcile_error = reconcile_error
122
+ self.last_seen_at = last_seen_at
93
123
 
94
124
  def api_id(self) -> str:
95
- return str(self.edge_device_id).upper()
125
+ return self.device_id
96
126
 
97
127
  def api_key(self) -> str:
98
128
  return self.device_id
99
129
 
100
130
  def set_id(self, id_value):
101
- self.edge_device_id = uuid.UUID(str(id_value))
131
+ self.device_id = str(id_value)
102
132
 
103
133
  def endpoint(self) -> str:
104
134
  return "EdgeDevices"
105
135
 
106
136
  def from_json(self, obj):
107
- if "id" in obj and obj["id"] is not None:
108
- self.edge_device_id = uuid.UUID(obj["id"])
109
137
  if "deviceId" in obj and obj["deviceId"] is not None:
110
138
  self.device_id = obj["deviceId"]
111
139
  if "name" in obj and obj["name"] is not None:
@@ -130,11 +158,29 @@ class EdgeDevice(ApiDto):
130
158
  self.created_at = int(obj["createdAt"])
131
159
  if "updatedAt" in obj and obj["updatedAt"] is not None:
132
160
  self.updated_at = int(obj["updatedAt"])
161
+ if "appliedHash" in obj and obj["appliedHash"] is not None:
162
+ self.applied_hash = obj["appliedHash"]
163
+ if "appliedVersion" in obj and obj["appliedVersion"] is not None:
164
+ self.applied_version = int(obj["appliedVersion"])
165
+ if "appliedAt" in obj and obj["appliedAt"] is not None:
166
+ self.applied_at = int(obj["appliedAt"])
167
+ if "imageOverrides" in obj and obj["imageOverrides"] is not None:
168
+ if not isinstance(obj["imageOverrides"], dict):
169
+ raise ValueError("imageOverrides must be an object: module -> image")
170
+ self.image_overrides = dict(obj["imageOverrides"])
171
+ if "runnerPythonVersion" in obj and obj["runnerPythonVersion"] is not None:
172
+ self.runner_python_version = str(obj["runnerPythonVersion"])
173
+ if "managed" in obj and obj["managed"] is not None:
174
+ self.managed = bool(obj["managed"])
175
+ if "appliedModules" in obj and obj["appliedModules"] is not None:
176
+ self.applied_modules = obj["appliedModules"]
177
+ if "reconcileError" in obj and obj["reconcileError"] is not None:
178
+ self.reconcile_error = obj["reconcileError"]
179
+ if "lastSeenAt" in obj and obj["lastSeenAt"] is not None:
180
+ self.last_seen_at = int(obj["lastSeenAt"])
133
181
 
134
182
  def to_json(self, target: str = None):
135
- obj = {
136
- "id": str(self.edge_device_id)
137
- }
183
+ obj = {}
138
184
  if self.device_id is not None:
139
185
  obj["deviceId"] = self.device_id
140
186
  if self.name is not None:
@@ -159,4 +205,21 @@ class EdgeDevice(ApiDto):
159
205
  obj["createdAt"] = self.created_at
160
206
  if self.updated_at is not None:
161
207
  obj["updatedAt"] = self.updated_at
162
- return obj
208
+ if self.applied_hash is not None:
209
+ obj["appliedHash"] = self.applied_hash
210
+ if self.applied_version is not None:
211
+ obj["appliedVersion"] = self.applied_version
212
+ if self.applied_at is not None:
213
+ obj["appliedAt"] = self.applied_at
214
+ if self.image_overrides is not None:
215
+ obj["imageOverrides"] = self.image_overrides
216
+ if self.runner_python_version is not None:
217
+ obj["runnerPythonVersion"] = self.runner_python_version
218
+ obj["managed"] = bool(self.managed)
219
+ if self.applied_modules is not None:
220
+ obj["appliedModules"] = self.applied_modules
221
+ if self.reconcile_error is not None:
222
+ obj["reconcileError"] = self.reconcile_error
223
+ if self.last_seen_at is not None:
224
+ obj["lastSeenAt"] = self.last_seen_at
225
+ return obj
@@ -0,0 +1,128 @@
1
+ from enum import Enum
2
+
3
+ from .api_dto import Dto
4
+
5
+
6
+ class EdgePackageOperation(Enum):
7
+ """
8
+ What a package for a manual edge does (DEV-6109). A manual edge serves a validated
9
+ (21 CFR Part 11) site: the platform never changes it, the site's operator runs each
10
+ package as a procedure.
11
+
12
+ - ``pipeline``: the pipelines it runs — triggers.json and the pipeline images;
13
+ - ``platform``: the platform version — docker-compose.yml, the module settings and
14
+ the profile files of a release;
15
+ - ``full``: everything, for the first install or any other change (connections,
16
+ writers, functions).
17
+ """
18
+ PIPELINE = "pipeline"
19
+ PLATFORM = "platform"
20
+ FULL = "full"
21
+
22
+
23
+ class EdgeRelease(Dto):
24
+ """
25
+ The edge images of a release, as Wizata.Jobs publishes them (DEV-6109).
26
+
27
+ ``edge_images.json`` in Wizata.Jobs stays where they are pinned. The Jobs consumer
28
+ writes them here at its start, so the DS API can compose a manual edge's platform
29
+ package with the images the environment runs.
30
+
31
+ :ivar str id: digest of the images: the same images are the same release.
32
+ :ivar str version: what published them, e.g. ``wizata-packages 2.2.0``.
33
+ :ivar dict images: module -> image; ``pipeline_runner`` maps a Python version to one.
34
+ :ivar int published_at: when last published, epoch ms.
35
+ """
36
+
37
+ def __init__(self, id=None, version=None, images=None, published_at=None):
38
+ self.id = id
39
+ self.version = version
40
+ self.images = images
41
+ self.published_at = published_at
42
+
43
+ @classmethod
44
+ def from_dict(cls, data: dict) -> "EdgeRelease":
45
+ return cls(id=data.get("id"), version=data.get("version"), images=data.get("images"),
46
+ published_at=data.get("publishedAt"))
47
+
48
+ def to_dict(self) -> dict:
49
+ return {"id": self.id, "version": self.version, "images": self.images,
50
+ "publishedAt": self.published_at}
51
+
52
+
53
+ class EdgePackage(Dto):
54
+ """
55
+ A procedure composed for a manual edge, and its record (DEV-6109): what it contains,
56
+ who generated and downloaded it, and when the edge reported it applied.
57
+
58
+ The package itself holds secrets (the module settings) and is never stored: it is
59
+ rendered again at each download, and refused if its contents changed since it was
60
+ generated. The record holds names and digests only.
61
+
62
+ :ivar str id: package id (UUID). The edge reports it once installed.
63
+ :ivar str edge_device_id: the device it is for.
64
+ :ivar EdgePackageOperation operation: pipeline, platform or full.
65
+ :ivar int sequence: 1, 2, 3... per device.
66
+ :ivar str release_id: the ``EdgeRelease`` its module images come from (platform, full).
67
+ :ivar str contents_hash: digest of the contents, secrets excluded.
68
+ :ivar dict contents: ``files`` (path -> sha256), ``settings`` (module -> variable
69
+ names), ``modules`` (module -> image), ``images`` (pipeline image ids),
70
+ ``warnings``.
71
+ :ivar dict diff: against what the edge runs (its applied packages): added, changed
72
+ and removed files, pipeline images, module images.
73
+ :ivar list procedure: the steps, each ``{"title", "command"}``.
74
+ :ivar str generated_by: who generated it.
75
+ :ivar int generated_at: epoch ms.
76
+ :ivar list downloads: each ``{"by", "at"}``.
77
+ :ivar int applied_at: when it was first reported installed, epoch ms.
78
+ :ivar str applied_by: ``edge`` (its heartbeat), or the admin who confirmed it for an
79
+ edge that cannot reach the platform.
80
+ """
81
+
82
+ def __init__(self, id=None, edge_device_id=None, operation=None, sequence=None,
83
+ release_id=None, contents_hash=None, contents=None, diff=None, procedure=None,
84
+ generated_by=None, generated_at=None, downloads=None, applied_at=None,
85
+ applied_by=None):
86
+ self.id = id
87
+ self.edge_device_id = edge_device_id
88
+ self.operation = operation
89
+ self.sequence = sequence
90
+ self.release_id = release_id
91
+ self.contents_hash = contents_hash
92
+ self.contents = contents
93
+ self.diff = diff
94
+ self.procedure = procedure
95
+ self.generated_by = generated_by
96
+ self.generated_at = generated_at
97
+ self.downloads = downloads
98
+ self.applied_at = applied_at
99
+ self.applied_by = applied_by
100
+
101
+ @classmethod
102
+ def from_dict(cls, data: dict) -> "EdgePackage":
103
+ operation = data.get("operation")
104
+ return cls(id=data.get("id"), edge_device_id=data.get("edgeDeviceId"),
105
+ operation=EdgePackageOperation(operation) if operation else None,
106
+ sequence=data.get("sequence"), release_id=data.get("releaseId"),
107
+ contents_hash=data.get("contentsHash"), contents=data.get("contents"),
108
+ diff=data.get("diff"), procedure=data.get("procedure"),
109
+ generated_by=data.get("generatedBy"), generated_at=data.get("generatedAt"),
110
+ downloads=data.get("downloads"), applied_at=data.get("appliedAt"),
111
+ applied_by=data.get("appliedBy"))
112
+
113
+ def to_dict(self) -> dict:
114
+ return {"id": self.id, "edgeDeviceId": self.edge_device_id,
115
+ "operation": self.operation.value if self.operation else None,
116
+ "sequence": self.sequence, "releaseId": self.release_id,
117
+ "contentsHash": self.contents_hash, "contents": self.contents,
118
+ "diff": self.diff, "procedure": self.procedure,
119
+ "generatedBy": self.generated_by, "generatedAt": self.generated_at,
120
+ "downloads": self.downloads or [], "appliedAt": self.applied_at,
121
+ "appliedBy": self.applied_by}
122
+
123
+ def marker(self) -> dict:
124
+ """What the package installs as ``conf/_packages/<id>.json`` and the edge reports."""
125
+ return {"id": self.id, "edgeDeviceId": self.edge_device_id,
126
+ "operation": self.operation.value if self.operation else None,
127
+ "sequence": self.sequence, "releaseId": self.release_id,
128
+ "contentsHash": self.contents_hash}
@@ -0,0 +1 @@
1
+ __version__ = "2.2.0.dev19"
@@ -446,7 +446,7 @@ class WizataDSAPIClient(ApiInterface, ApiDtoInterface):
446
446
  return self._query(request=obj)
447
447
  else:
448
448
  dto_class = self._get_class(entity=type(obj), operation=operation)
449
- if dto_class == PipelineImage:
449
+ if dto_class == PipelineImage or dto_class.get_id_type() == str:
450
450
  id = obj.api_id()
451
451
  else:
452
452
  id = uuid.UUID(obj.api_id())
@@ -582,6 +582,8 @@ class WizataDSAPIClient(ApiInterface, ApiDtoInterface):
582
582
  if dto_class.get_id_type() == int:
583
583
  obj.set_id(response.json()["id"])
584
584
  return response.json()["id"]
585
+ elif dto_class.get_id_type() == str:
586
+ return obj.api_id()
585
587
  else:
586
588
  return uuid.UUID(obj.api_id())
587
589
 
@@ -3002,13 +3004,14 @@ class WizataDSAPIClient(ApiInterface, ApiDtoInterface):
3002
3004
  return "edge/" + "/".join(quoted)
3003
3005
 
3004
3006
  def _edge_request(self, method: str, route: str, body=None, headers=None,
3005
- expected=(200,), stream=False):
3007
+ expected=(200,), stream=False, timeout=None):
3006
3008
  response = requests.request(
3007
3009
  method,
3008
3010
  self.__url() + route,
3009
3011
  headers={**self.__header(), **(headers or {})},
3010
3012
  data=json.dumps(body) if body is not None else None,
3011
3013
  stream=stream,
3014
+ timeout=timeout,
3012
3015
  )
3013
3016
  if response.status_code not in expected:
3014
3017
  raise self.__raise_error(response)
@@ -3078,6 +3081,21 @@ class WizataDSAPIClient(ApiInterface, ApiDtoInterface):
3078
3081
  os.remove(temporary)
3079
3082
  return path
3080
3083
 
3084
+ def send_edge_heartbeat(self, edge_device_id: str, packages: list = None,
3085
+ timeout=(5, 15)) -> dict:
3086
+ """
3087
+ A manual edge's heartbeat (DEV-6109): it is alive, and these packages (the ids in
3088
+ its ``conf/_packages``) are installed. The platform records when it was seen, and
3089
+ each package as installed the first time it is listed.
3090
+
3091
+ :param timeout: (connect, read) seconds — a site that blocks outbound traffic must
3092
+ not hold the caller.
3093
+ :return: ``{"lastSeenAt", "applied": [ids newly recorded], "unknown": [ids]}``.
3094
+ """
3095
+ response = self._edge_request("POST", self._edge_route(edge_device_id, "heartbeat"),
3096
+ body={"packages": list(packages or [])}, timeout=timeout)
3097
+ return response.json()
3098
+
3081
3099
 
3082
3100
  def api() -> WizataDSAPIClient:
3083
3101
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wizata_dsapi
3
- Version: 2.2.0.dev17
3
+ Version: 2.2.0.dev19
4
4
  Summary: Wizata Data Science Toolkit
5
5
  Author: Wizata S.A.
6
6
  Author-email: info@wizata.com
@@ -20,6 +20,7 @@ wizata_dsapi/dsapi_json_encoder.py
20
20
  wizata_dsapi/edge_config.py
21
21
  wizata_dsapi/edge_device.py
22
22
  wizata_dsapi/edge_module.py
23
+ wizata_dsapi/edge_package.py
23
24
  wizata_dsapi/evaluation.py
24
25
  wizata_dsapi/execution.py
25
26
  wizata_dsapi/execution_log.py
@@ -1 +0,0 @@
1
- __version__ = "2.2.0.dev17"