Skip to content

Commit b22a0d4

Browse files
authored
Merge pull request #85 from dataiku/feature/dss80-flow-zones
Feature/dss80 flow zones
2 parents 74840b8 + 60b146b commit b22a0d4

File tree

4 files changed

+283
-13
lines changed

4 files changed

+283
-13
lines changed

dataikuapi/dss/dataset.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ def __init__(self, client, project_key, dataset_name):
5959
self.project_key = project_key
6060
self.dataset_name = dataset_name
6161

62+
@property
63+
def id(self):
64+
return self.dataset_name
65+
6266
@property
6367
def name(self):
6468
return self.dataset_name
@@ -511,9 +515,27 @@ def get_metric_history(self, metric, partition=''):
511515
params={'metricLookup' : metric if isinstance(metric, str) or isinstance(metric, unicode) else json.dumps(metric)})
512516

513517
########################################################
514-
# Usages
518+
# Misc
515519
########################################################
516520

521+
def get_zone(self):
522+
"""
523+
Gets the flow zone of this dataset
524+
525+
:rtype: :class:`dataikuapi.dss.flow.DSSFlowZone`
526+
"""
527+
return self.project.get_flow().get_zone_of_object(self)
528+
529+
def move_to_zone(self, zone):
530+
"""
531+
Moves this object to a flow zone
532+
533+
:param object zone: a :class:`dataikuapi.dss.flow.DSSFlowZone` where to move the object
534+
"""
535+
if isinstance(zone, basestring):
536+
zone = self.project.get_flow().get_zone(zone)
537+
zone.add_item(self)
538+
517539
def get_usages(self):
518540
"""
519541
Get the recipes or analyses referencing this dataset
@@ -523,9 +545,6 @@ def get_usages(self):
523545
"""
524546
return self.client._perform_json("GET", "/projects/%s/datasets/%s/usages" % (self.project_key, self.dataset_name))
525547

526-
########################################################
527-
# Discussions
528-
########################################################
529548
def get_object_discussions(self):
530549
"""
531550
Get a handle to manage discussions on the dataset

dataikuapi/dss/flow.py

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from .utils import AnyLoc
22
from .dataset import DSSDataset
3+
from .managedfolder import DSSManagedFolder
4+
from .savedmodel import DSSSavedModel
35
from .recipe import DSSRecipe, DSSRecipeDefinitionAndPayload
46
from .future import DSSFuture
57
import logging, json
@@ -13,6 +15,62 @@ def get_graph(self):
1315
data = self.client._perform_json("GET", "/projects/%s/flow/graph/" % (self.project.project_key))
1416
return DSSProjectFlowGraph(self, data)
1517

18+
def create_zone(self, name, color="#2ab1ac"):
19+
"""
20+
Creates a new flow zone
21+
22+
:returns the newly created zone
23+
:rtype: :class:`DSSFlowZone`
24+
"""
25+
data = self.client._perform_json("POST", "/projects/%s/flow/zones" % (self.project.project_key), body={
26+
"name": name,
27+
"color":color
28+
})
29+
return DSSFlowZone(self, data)
30+
31+
def get_zone(self, id):
32+
"""
33+
Gets a single Flow zone by id
34+
:rtype: :class:`DSSFlowZone`
35+
"""
36+
data = self.client._perform_json("GET", "/projects/%s/flow/zones/%s" % (self.project.project_key, id))
37+
return DSSFlowZone(self, data)
38+
39+
def get_default_zone(self):
40+
"""
41+
Returns the default zone of the Flow
42+
:rtype: :class:`DSSFlowZone`
43+
"""
44+
return self.get_zone("default")
45+
46+
def list_zones(self):
47+
"""
48+
Lists all zones in the Flow
49+
:rtype: list of :class:`DSSFlowZone`
50+
"""
51+
data = self.client._perform_json("GET", "/projects/%s/flow/zones" % (self.project.project_key))
52+
return [DSSFlowZone(self, z) for z in data]
53+
54+
def get_zone_of_object(self, obj):
55+
"""
56+
Finds the zone to which this object belongs.
57+
58+
If the object is not found in any specific zone, it belongs to the default zone, and the default
59+
zone is returned
60+
61+
:param object obj: A :class:`dataikuapi.dss.dataset.DSSDataset`, :class:`dataikuapi.dss.managedfolder.DSSManagedFolder`,
62+
or :class:`dataikuapi.dss.savedmodel.DSSSavedModel` to search
63+
64+
:rtype: :class:`DSSFlowZone`
65+
"""
66+
sr = self._to_smart_ref(obj)
67+
68+
for zone in self.list_zones():
69+
for item in zone._raw["items"]:
70+
if json.dumps(sr) == json.dumps(item):
71+
return zone
72+
return self.get_default_zone()
73+
1674
def replace_input_computable(self, current_ref, new_ref, type="DATASET"):
1775
"""
1876
This method replaces all references to a "computable" (Dataset, Managed Folder or Saved Model)
@@ -86,6 +144,164 @@ def propagate_schema(self, dataset_name, rebuild=False, recipe_update_options={}
86144
return DSSFuture(self.client,update_future.get('jobId', None), update_future)
87145

88146

147+
def _to_smart_ref(self, obj):
148+
if isinstance(obj, DSSDataset):
149+
ot = "DATASET"
150+
elif isinstance(obj, DSSManagedFolder):
151+
ot = "MANAGED_FOLDER"
152+
elif isinstance(obj, DSSSavedModel):
153+
ot = "SAVED_MODEL"
154+
elif isinstance(obj, DSSRecipe):
155+
ot = "RECIPE"
156+
else:
157+
raise ValueError("Cannot transform to DSS object ref: %s" % obj)
158+
159+
if obj.project_key == self.project.project_key:
160+
return {
161+
"objectId" : obj.id,
162+
"objectType": ot
163+
}
164+
else:
165+
return {
166+
"projectKey" : obj.project_key,
167+
"objectId" : obj.id,
168+
"objectType": ot
169+
}
170+
171+
class DSSFlowZone(object):
172+
"""
173+
A zone in the Flow. Do not create this object manually, use :meth:`DSSProjectFlow.get_zone`
174+
or :meth:`DSSProjectFlow.list_zones`
175+
"""
176+
def __init__(self, flow, data):
177+
self.flow = flow
178+
self.client = flow.client
179+
self._raw = data
180+
181+
@property
182+
def id(self):
183+
return self._raw["id"]
184+
185+
@property
186+
def name(self):
187+
return self._raw["name"]
188+
189+
def __repr__(self):
190+
return "<dataikuapi.dss.flow.DSSFlowZone (id=%s, name=%s)>" % (self.id, self.name)
191+
192+
def get_settings(self):
193+
"""Gets the settings of this zone in order to modify them
194+
195+
:rtype: :class:`DSSFlowZoneSettings`
196+
"""
197+
return DSSFlowZoneSettings(self)
198+
199+
def _to_native_obj(self, zone_item):
200+
if not "projectKey" in zone_item or zone_item["projectKey"] == self.flow.project.project_key:
201+
p = self.flow.project
202+
else:
203+
p = self.client.get_project(zone_item["projectKey"])
204+
205+
if zone_item["objectType"] == "DATASET":
206+
return p.get_dataset(zone_item["objectId"])
207+
elif zone_item["objectType"] == "MANAGED_FOLDER":
208+
return p.get_managed_folder(zone_item["objectId"])
209+
elif zone_item["objectType"] == "SAVED_MODEL":
210+
return p.get_saved_model(zone_item["objectId"])
211+
elif zone_item["objectType"] == "RECIPE":
212+
return p.get_recipe(zone_item["objectId"])
213+
else:
214+
raise ValueError("Cannot transform to DSS object: %s" % zone_item)
215+
216+
def add_item(self, obj):
217+
"""
218+
Adds an item to this zone.
219+
220+
The item will automatically be moved from its existing zone. Additional items may be moved to this zone
221+
as a result of the operation (notably the recipe generating `obj`).
222+
223+
:param object obj: A :class:`dataikuapi.dss.dataset.DSSDataset`, :class:`dataikuapi.dss.managedfolder.DSSManagedFolder`,
224+
or :class:`dataikuapi.dss.savedmodel.DSSSavedModel` to add to the zone
225+
"""
226+
self.client._perform_empty("POST", "/projects/%s/flow/zones/%s/items" % (self.flow.project.project_key, self.id),
227+
body=self.flow._to_smart_ref(obj))
228+
229+
#. TBD: if we make "add to default" work propertly, then we don't need thjis
230+
#def remove_item(self, obj):
231+
# """
232+
# Removes an item to this zone.#
233+
#
234+
# :param object obj: A :class:`dataikuapi.dss.dataset.DSSDataset`, :class:`dataikuapi.dss.managedfolder.DSSManagedFolder`,
235+
# or :class:`dataikuapi.dss.savedmodel.DSSSavedModel` to add to the zone
236+
# """
237+
# sr = self._to_smart_ref(obj)
238+
#
239+
# self.client._perform_empty("DELETE", "/projects/%s/flow/zones/%s/items/%s/%s" % (self.flow.project.project_key,
240+
# self.id, sr["objectType"], sr["objectId"]))
241+
242+
@property
243+
def items(self):
244+
"""
245+
The list of items explicitly belonging to this zone.
246+
247+
This list is read-only, to modify it, use :meth:`add_item` and :meth:`remove_item`.
248+
249+
Note that the "default" zone never has any items, as it contains all items that are not
250+
explicitly in a zone. To get the full list of items in a zone, including in the "default" zone, use
251+
the :meth:`get_graph` method.
252+
253+
@rtype list of zone items, either :class:`dataikuapi.dss.dataset.DSSDataset`,
254+
:class:`dataikuapi.dss.managedfolder.DSSManagedFolder`,
255+
or :class:`dataikuapi.dss.savedmodel.DSSSavedModel` or :class:`dataiuapi.dss.recipe.DSSRecipe`
256+
"""
257+
return [self._to_native_obj(i) for i in self._raw["items"]]
258+
259+
@property
260+
def shared(self):
261+
"""
262+
The list of items that have been explicitly pre-shared to this zone.
263+
264+
This list is read-only, to modify it, use :meth:`add_shared` and :meth:`remove_shared`
265+
266+
@rtype list of shared zone items, either :class:`dataikuapi.dss.dataset.DSSDataset`,
267+
:class:`dataikuapi.dss.managedfolder.DSSManagedFolder`,
268+
or :class:`dataikuapi.dss.savedmodel.DSSSavedModel` or :class:`dataiuapi.dss.recipe.DSSRecipe`
269+
"""
270+
return [self._to_native_obj(i) for i in self._raw["shared"]]
271+
272+
def get_graph(self):
273+
data = self.client._perform_json("GET", "/projects/%s/flow/zones/%s/graph" % (self.flow.project.project_key, self.id))
274+
return DSSProjectFlowGraph(self.flow, data)
275+
276+
277+
class DSSFlowZoneSettings(object):
278+
"""The settings of a flow zone. Do not create this directly, use :meth:`DSSFlowZone.get_settings`"""
279+
def __init__(self, zone):
280+
self._zone = zone
281+
self._raw = zone._raw
282+
283+
def get_raw(self):
284+
"""
285+
Gets the raw settings of the zone.
286+
287+
You cannot modify the `items` and `shared` elements through this class. Instead, use :meth:`DSSFlowZone.add_item` and
288+
others
289+
"""
290+
return self._raw
291+
292+
@property
293+
def name(self):
294+
return self._raw["name"]
295+
296+
@name.setter
297+
def name(self, new_name):
298+
self._raw["name"] = new_name
299+
300+
def save(self):
301+
"""Saves the settings of the zone"""
302+
self._zone.client._perform_empty("PUT", "/projects/%s/flow/zones/%s" % (self._zone.flow.project.project_key, self._zone.id),
303+
body=self._raw)
304+
89305
class DSSProjectFlowGraph(object):
90306

91307
def __init__(self, flow, data):

dataikuapi/dss/managedfolder.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class DSSManagedFolder(object):
1313
"""
1414
def __init__(self, client, project_key, odb_id):
1515
self.client = client
16+
self.project = client.get_project(project_key)
1617
self.project_key = project_key
1718
self.odb_id = odb_id
1819

@@ -155,9 +156,27 @@ def get_metric_history(self, metric):
155156

156157

157158
########################################################
158-
# Usages
159+
# Misc
159160
########################################################
160161

162+
def get_zone(self):
163+
"""
164+
Gets the flow zone of this managed folder
165+
166+
:rtype: :class:`dataikuapi.dss.flow.DSSFlowZone`
167+
"""
168+
return self.project.get_flow().get_zone_of_object(self)
169+
170+
def move_to_zone(self, zone):
171+
"""
172+
Moves this object to a flow zone
173+
174+
:param object zone: a :class:`dataikuapi.dss.flow.DSSFlowZone` where to move the object
175+
"""
176+
if isinstance(zone, basestring):
177+
zone = self.project.get_flow().get_zone(zone)
178+
zone.add_item(self)
179+
161180
def get_usages(self):
162181
"""
163182
Get the recipes referencing this folder
@@ -167,9 +186,6 @@ def get_usages(self):
167186
"""
168187
return self.client._perform_json("GET", "/projects/%s/managedfolders/%s/usages" % (self.project_key, self.odb_id))
169188

170-
########################################################
171-
# Discussions
172-
########################################################
173189
def get_object_discussions(self):
174190
"""
175191
Get a handle to manage discussions on the managed folder

dataikuapi/dss/savedmodel.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ class DSSSavedModel(object):
1212
"""
1313
def __init__(self, client, project_key, sm_id):
1414
self.client = client
15+
self.project = client.get_project(project_key)
1516
self.project_key = project_key
1617
self.sm_id = sm_id
18+
19+
@property
20+
def id(self):
21+
return self.sm_id
1722

1823
def get_definition(self):
1924
return self.client._perform_json(
@@ -119,9 +124,27 @@ def get_metric_values(self, version_id):
119124

120125

121126
########################################################
122-
# Usages
127+
# Misc
123128
########################################################
124129

130+
def get_zone(self):
131+
"""
132+
Gets the flow zone of this saved model
133+
134+
:rtype: :class:`dataikuapi.dss.flow.DSSFlowZone`
135+
"""
136+
return self.project.get_flow().get_zone_of_object(self)
137+
138+
def move_to_zone(self, zone):
139+
"""
140+
Moves this object to a flow zone
141+
142+
:param object zone: a :class:`dataikuapi.dss.flow.DSSFlowZone` where to move the object
143+
"""
144+
if isinstance(zone, basestring):
145+
zone = self.project.get_flow().get_zone(zone)
146+
zone.add_item(self)
147+
125148
def get_usages(self):
126149
"""
127150
Get the recipes referencing this model
@@ -131,10 +154,6 @@ def get_usages(self):
131154
"""
132155
return self.client._perform_json("GET", "/projects/%s/savedmodels/%s/usages" % (self.project_key, self.sm_id))
133156

134-
135-
########################################################
136-
# Discussions
137-
########################################################
138157
def get_object_discussions(self):
139158
"""
140159
Get a handle to manage discussions on the saved model

0 commit comments

Comments
 (0)