Skip to content

Commit 6c50654

Browse files
authored
Merge pull request #40 from dataiku/feature/dss520-project-folder
Project folder backend object (dev prj~1)
2 parents 050aae3 + 7ab1d04 commit 6c50654

File tree

3 files changed

+273
-9
lines changed

3 files changed

+273
-9
lines changed

dataikuapi/dss/project.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ def duplicate(self, target_project_key,
8585
export_saved_models=True,
8686
export_git_repository=True,
8787
export_insights_data=True,
88-
remapping={}):
88+
remapping={},
89+
target_project_folder=None):
8990
"""
9091
Duplicate the project
9192
@@ -97,6 +98,8 @@ def duplicate(self, target_project_key,
9798
:param bool export_git_repository:
9899
:param bool export_insights_data:
99100
:param dict remapping: dict of connections to be remapped for the new project
101+
:param target_project_folder: the project folder where to put the duplicated project
102+
:type target_project_folder: A :class:`dataikuapi.dss.projectfolder.DSSProjectFolder
100103
:returns: A dict containing the original and duplicated project's keys
101104
:rtype: :class:`ProjectDuplicateResult`
102105
"""
@@ -111,6 +114,8 @@ def duplicate(self, target_project_key,
111114
"exportInsightsData": export_insights_data,
112115
"remapping": remapping
113116
}
117+
if target_project_folder is not None:
118+
obj["targetProjectFolderId"] = target_project_folder.project_folder_id
114119

115120
ref = self.client._perform_json("POST", "/projects/%s/duplicate/" % self.project_key, body = obj)
116121
return ref

dataikuapi/dss/projectfolder.py

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
from .project import DSSProject
2+
3+
class DSSProjectFolder(object):
4+
"""
5+
A handle to interact with a project folder on the DSS instance.
6+
7+
Do not create this class directly, instead use :meth:`dataikuapi.DSSClient.get_project_folder` or :meth:`dataikuapi.DSSClient.get_root_project_folder`
8+
"""
9+
def __init__(self, client, project_folder_id):
10+
self.client = client
11+
self.project_folder_id = project_folder_id
12+
13+
########################################################
14+
# Project folder basics
15+
########################################################
16+
def get_name(self):
17+
"""
18+
Get this project folder's name or None if it is the root project folder
19+
20+
:returns str: the name of this project folders or None for the root project folder
21+
"""
22+
return self.client._perform_json("GET", "/project-folders/%s" % self.project_folder_id).get("name", None)
23+
24+
def get_path(self):
25+
"""
26+
Get this project fodler's path based on the root project folder
27+
28+
:returns str: the path of this project folder
29+
"""
30+
definition = self.client._perform_json("GET", "/project-folders/%s" % self.project_folder_id)
31+
parent_id = definition.get("parentId", None)
32+
if parent_id is not None:
33+
parent = DSSProjectFolder(self.client, parent_id)
34+
path = parent.get_path()
35+
return ("" if path == "/" else path) + "/" + definition.get("name", "")
36+
else:
37+
return "/"
38+
39+
def get_parent(self):
40+
"""
41+
Get this project folder's parent or None if it is the root project folder
42+
43+
:returns: A :class:`dataikuapi.dss.projectfolders.DSSProjectFolder` to interact with its parent or None for the root project folder
44+
"""
45+
parent_id = self.client._perform_json("GET", "/project-folders/%s" % self.project_folder_id).get("parentId", None)
46+
if parent_id is None:
47+
return None
48+
else:
49+
return DSSProjectFolder(self.client, parent_id)
50+
51+
def list_child_folders(self):
52+
"""
53+
List the child project folders inside this project folder
54+
55+
:returns list: A list of :class:`dataikuapi.dss.projectfolders.DSSProjectFolder` to interact with its sub-folders
56+
"""
57+
children = self.client._perform_json("GET", "/project-folders/%s" % self.project_folder_id).get("childrenIds", [])
58+
return [DSSProjectFolder(self.client, child) for child in children]
59+
60+
def list_project_keys(self):
61+
"""
62+
List the project keys of the projects that are stored in this project folder
63+
64+
:returns list: A list of project keys
65+
"""
66+
return self.client._perform_json("GET", "/project-folders/%s" % self.project_folder_id).get("projectKeys", [])
67+
68+
def list_projects(self):
69+
"""
70+
List the projects that are stored in this project folder
71+
72+
:returns list: A list of :class:`dataikuapi.dss.project.DSSProject` to interact with its projects
73+
"""
74+
project_keys = self.client._perform_json("GET", "/project-folders/%s" % self.project_folder_id).get("projectKeys", [])
75+
return [DSSProject(self.client, pkey) for pkey in project_keys]
76+
77+
########################################################
78+
# Project folder deletion
79+
########################################################
80+
def delete(self):
81+
"""
82+
Delete the project folder
83+
Note: it must be empty (cannot contain any sub-project folders or projects), you must move or remove all its content before deleting it
84+
85+
This call requires an API key with admin rights
86+
"""
87+
return self.client._perform_empty(
88+
"DELETE", "/project-folders/%s" % self.project_folder_id)
89+
90+
########################################################
91+
# Project folder settings
92+
########################################################
93+
def get_settings(self):
94+
"""
95+
:returns: A :class:`dataikuapi.dss.projectfolder.DSSProjectFolderSettings` to interact with this project folder settings
96+
"""
97+
settings = self.client._perform_json("GET", "/project-folders/%s/settings" % self.project_folder_id)
98+
return DSSProjectFolderSettings(self.client, self.project_folder_id, settings)
99+
100+
########################################################
101+
# Project folder sub-folder creation
102+
########################################################
103+
def create_sub_folder(self, name):
104+
"""
105+
Create a sub-folder into the current project folder
106+
107+
:param str name: the name of the project folder to create
108+
:returns: A :class:`dataikuapi.dss.projectfolder.DSSProjectFolder` to interact with the newly created project folder
109+
"""
110+
params = {
111+
"name": name
112+
}
113+
pf = self.client._perform_json("POST", "/project-folders/%s/children" % self.project_folder_id, params=params)
114+
return DSSProjectFolder(self.client, pf["id"])
115+
116+
########################################################
117+
# Project creation
118+
########################################################
119+
def create_project(self, project_key, name, owner, description=None, settings=None):
120+
"""
121+
Creates a new project within this project folder, and return a project handle to interact with it.
122+
123+
Note: this call requires an API key with admin rights or the rights to create a project
124+
125+
:param str project_key: the identifier to use for the project. Must be globally unique
126+
:param str name: the display name for the project.
127+
:param str owner: the login of the owner of the project.
128+
:param str description: a description for the project.
129+
:param dict settings: Initial settings for the project (can be modified later). The exact possible settings are not documented.
130+
131+
:returns: A class:`dataikuapi.dss.project.DSSProject` project handle to interact with this project
132+
"""
133+
return self.client.create_project(project_key, name, owner, description=description, settings=settings, project_folder_id=self.project_folder_id)
134+
135+
########################################################
136+
# Project folder move
137+
########################################################
138+
def move_to(self, destination):
139+
"""
140+
Move this project folder into another project folder (aka. destination)
141+
142+
:param destination: the project folder to put this project folder into
143+
:type destination: A :class:`dataikuapi.dss.projectfolders.DSSProjectFolder`
144+
"""
145+
params = {
146+
"destination": destination.project_folder_id
147+
}
148+
self.client._perform_empty("POST", "/project-folders/%s/move" % self.project_folder_id, params=params)
149+
150+
########################################################
151+
# Project move
152+
########################################################
153+
def move_project_to(self, project_key, destination):
154+
"""
155+
Move a project within this project folder into another project folder (aka. destination)
156+
157+
:param str project_key: the project key associated with the project to move
158+
:param destination: the project folder to put this project into
159+
:type destination: A :class:`dataikuapi.dss.projectfolders.DSSProjectFolder`
160+
"""
161+
params = {
162+
"destination": destination.project_folder_id
163+
}
164+
self.client._perform_empty("POST", "/project-folders/%s/projects/%s/move" % (self.project_folder_id, project_key), params=params)
165+
166+
class DSSProjectFolderSettings(object):
167+
"""
168+
A handle to interact with project folder settings
169+
170+
Do not create this class directly, instead use :meth:`dataikuapi.dss.projectfolder.DSSProjectFolder.get_settings`
171+
"""
172+
def __init__(self, client, project_folder_id, settings):
173+
self.client = client
174+
self.project_folder_id = project_folder_id
175+
self.settings = settings
176+
177+
def get_raw(self):
178+
"""Gets all settings as a raw dictionary. This returns a reference to the raw retrieved settings, not a copy,
179+
so changes made to the returned object will be reflected when saving.
180+
181+
:rtype: dict
182+
"""
183+
return self.settings
184+
185+
def get_name(self):
186+
"""Get the name of the project folder
187+
188+
:returns str: the current name of the project folder
189+
"""
190+
return self.settings.get("name", None)
191+
192+
def set_name(self, name):
193+
"""Set the name of the project folder
194+
195+
:param str name: the new name of the project folder
196+
"""
197+
self.settings["name"] = name
198+
199+
def get_owner(self):
200+
"""Get the login of the owner of the project folder
201+
202+
:returns str: the current login owner of the project folder
203+
"""
204+
return self.settings.get("owner", None)
205+
206+
def set_owner(self, owner):
207+
"""Set the owner of the project folder
208+
209+
:param str owner: the new owner login of the project folder
210+
"""
211+
self.settings["owner"] = owner
212+
213+
def get_permissions(self):
214+
"""Get the permissions of the project folder. This returns a reference to the retrieved permissions, not a copy,
215+
so changes made to the returned list will be reflected when saving.
216+
217+
:return list: the current permissions of the project folder
218+
"""
219+
return self.settings["permissions"]
220+
221+
def save(self):
222+
"""Saves back the settings to the project folder"""
223+
self.client._perform_empty("PUT", "/project-folders/%s/settings" % (self.project_folder_id), body = self.settings)
224+

dataikuapi/dssclient.py

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from requests.auth import HTTPBasicAuth
55

66
from .dss.future import DSSFuture
7+
from .dss.projectfolder import DSSProjectFolder
78
from .dss.project import DSSProject
89
from .dss.plugin import DSSPlugin
910
from .dss.admin import DSSUser, DSSGroup, DSSConnection, DSSGeneralSettings, DSSCodeEnv, DSSGlobalApiKey, DSSCluster
@@ -101,6 +102,26 @@ def list_running_notebooks(self, as_objects=True):
101102
else:
102103
return list
103104

105+
########################################################
106+
# Project folders
107+
########################################################
108+
def get_root_project_folder(self):
109+
"""
110+
Get a handle to interact with the root project folder.
111+
112+
:returns: A :class:`dataikuapi.dss.projectfolder.DSSProjectFolder`to interact with this project folder
113+
"""
114+
project_folder_id = self._perform_json("GET", "/project-folders/")["id"]
115+
return DSSProjectFolder(self, project_folder_id)
116+
117+
def get_project_folder(sefl, project_folder_id):
118+
"""
119+
Get a handle to interact with a project folder.
120+
121+
:param str project_folder_id: the project folder ID of the desired project folder
122+
:returns: A :class:`dataikuapi.dss.projectfolder.DSSProjectFolder`to interact with this project folder
123+
"""
124+
return DSSProjectFolder(self, project_folder_id)
104125

105126
########################################################
106127
# Projects
@@ -133,7 +154,7 @@ def get_project(self, project_key):
133154
"""
134155
return DSSProject(self, project_key)
135156

136-
def create_project(self, project_key, name, owner, description=None, settings=None):
157+
def create_project(self, project_key, name, owner, description=None, settings=None, project_folder_id=None):
137158
"""
138159
Creates a new project, and return a project handle to interact with it.
139160
@@ -144,17 +165,21 @@ def create_project(self, project_key, name, owner, description=None, settings=No
144165
:param str owner: the login of the owner of the project.
145166
:param str description: a description for the project.
146167
:param dict settings: Initial settings for the project (can be modified later). The exact possible settings are not documented.
168+
:param str project_folder_id: the project folder ID in which the project will be created (root project folder if not specified)
147169
148170
:returns: A class:`dataikuapi.dss.project.DSSProject` project handle to interact with this project
149171
"""
172+
params = {}
173+
if project_folder_id is not None:
174+
params["projectFolderId"] = project_folder_id
150175
resp = self._perform_text(
151176
"POST", "/projects/", body={
152177
"projectKey" : project_key,
153178
"name" : name,
154179
"owner" : owner,
155180
"settings" : settings,
156181
"description" : description
157-
})
182+
}, params=params)
158183
return DSSProject(self, project_key)
159184

160185
########################################################
@@ -657,27 +682,37 @@ def get_general_settings(self):
657682
# Bundles / Import (Automation node)
658683
########################################################
659684

660-
def create_project_from_bundle_local_archive(self, archive_path):
685+
def create_project_from_bundle_local_archive(self, archive_path, project_folder=None):
661686
"""
662687
Create a project from a bundle archive.
663688
Warning: this method can only be used on an automation node.
664689
665690
:param string archive_path: Path on the local machine where the archive is
691+
:param project_folder: the project folder in which the project will be created or None for root project folder
692+
:type project_folder: A :class:`dataikuapi.dss.projectfolder.DSSProjectFolder`
666693
"""
667-
return self._perform_json("POST",
668-
"/projectsFromBundle/fromArchive",
669-
params = { "archivePath" : osp.abspath(archive_path) })
694+
params = {
695+
"archivePath" : osp.abspath(archive_path)
696+
}
697+
if project_folder is not None:
698+
params["projectFolderId"] = project_folder.project_folder_id
699+
return self._perform_json("POST", "/projectsFromBundle/fromArchive", params=params)
670700

671-
def create_project_from_bundle_archive(self, fp):
701+
def create_project_from_bundle_archive(self, fp, project_folder=None):
672702
"""
673703
Create a project from a bundle archive (as a file object)
674704
Warning: this method can only be used on an automation node.
675705
676706
:param string fp: A file-like object pointing to a bundle archive zip
707+
:param project_folder: the project folder in which the project will be created or None for root project folder
708+
:type project_folder: A :class:`dataikuapi.dss.projectfolder.DSSProjectFolder`
677709
"""
710+
params = {}
711+
if project_folder is not None:
712+
params['projectFolderId'] = project_folder.project_folder_id
678713
files = {'file': fp }
679714
return self._perform_json("POST",
680-
"/projectsFromBundle/", files=files)
715+
"/projectsFromBundle/", files=files, params=params)
681716

682717
def prepare_project_import(self, f):
683718
"""

0 commit comments

Comments
 (0)