Skip to content

Commit ab0c7ef

Browse files
committed
Merge branch 'release/10.0' into chore/dss10-clarify-get-variable-apis
2 parents 8d7942f + 28fe7cb commit ab0c7ef

21 files changed

+2646
-188
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
*.pyc
22
.idea
33
*.iml
4+
egg-info

dataikuapi/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from .dssclient import DSSClient
2+
from .fmclient import FMClientAWS, FMClientAzure
23

34
from .apinode_client import APINodeClient
45
from .apinode_admin_client import APINodeAdminClient
56

67
from .dss.recipe import GroupingRecipeCreator, JoinRecipeCreator, StackRecipeCreator, WindowRecipeCreator, SyncRecipeCreator, SamplingRecipeCreator, SQLQueryRecipeCreator, CodeRecipeCreator, SplitRecipeCreator, SortRecipeCreator, TopNRecipeCreator, DistinctRecipeCreator, DownloadRecipeCreator, PredictionScoringRecipeCreator, ClusteringScoringRecipeCreator
78

8-
from .dss.admin import DSSUserImpersonationRule, DSSGroupImpersonationRule
9+
from .dss.admin import DSSUserImpersonationRule, DSSGroupImpersonationRule

dataikuapi/dss/admin.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,19 @@ def set_definition(self, env):
710710
"""
711711
return self.client._perform_json(
712712
"PUT", "/admin/code-envs/%s/%s" % (self.env_lang, self.env_name), body=env)
713-
713+
714+
def get_version_for_project(self, project_key):
715+
"""
716+
Resolve the code env version for a given project
717+
718+
Note: version will only be non-empty for versioned code envs actually used by the project
719+
720+
:returns: the code env reference, with a version field
721+
"""
722+
return self.client._perform_json(
723+
"GET", "/admin/code-envs/%s/%s/%s/version" % (self.env_lang, self.env_name, project_key))
724+
725+
714726
########################################################
715727
# Code env actions
716728
########################################################
@@ -747,6 +759,52 @@ def update_packages(self, force_rebuild_env=False):
747759
raise Exception('Env update failed : %s' % (json.dumps(resp.get('messages', {}).get('messages', {}))))
748760
return resp
749761

762+
def update_images(self, env_version=None):
763+
"""
764+
Rebuild the docker image of the code env
765+
766+
Note: this call requires an API key with admin rights
767+
"""
768+
resp = self.client._perform_json(
769+
"POST", "/admin/code-envs/%s/%s/images" % (self.env_lang, self.env_name),
770+
params={"envVersion": env_version})
771+
if resp is None:
772+
raise Exception('Env image build returned no data')
773+
if resp.get('messages', {}).get('error', False):
774+
raise Exception('Env image build failed : %s' % (json.dumps(resp.get('messages', {}).get('messages', {}))))
775+
return resp
776+
777+
def list_usages(self, env_version=None):
778+
"""
779+
List usages of the code env in the instance
780+
781+
:return: a list of objects where the code env is used
782+
"""
783+
return self.client._perform_json(
784+
"GET", "/admin/code-envs/%s/%s/usages" % (self.env_lang, self.env_name), params={"envVersion": env_version})
785+
786+
def list_logs(self, env_version=None):
787+
"""
788+
List logs of the code env in the instance
789+
790+
:return: a list of log descriptions
791+
"""
792+
return self.client._perform_json(
793+
"GET", "/admin/code-envs/%s/%s/logs" % (self.env_lang, self.env_name), params={"envVersion": env_version})
794+
795+
def get_log(self, log_name):
796+
"""
797+
Get the logs of the code env
798+
799+
Args:
800+
log_name: name of the log to fetch
801+
802+
Returns:
803+
the log, as a string
804+
"""
805+
return self.client._perform_text(
806+
"GET", "/admin/code-envs/%s/%s/logs/%s" % (self.env_lang, self.env_name, log_name))
807+
750808

751809
class DSSGlobalApiKey(object):
752810
"""

dataikuapi/dss/apideployer.py

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def get_deployment(self, deployment_id):
3636
"""
3737
return DSSAPIDeployerDeployment(self.client, deployment_id)
3838

39-
def create_deployment(self, deployment_id, service_id, infra_id, version):
39+
def create_deployment(self, deployment_id, service_id, infra_id, version, ignore_warnings=False):
4040
"""
4141
Creates a deployment and returns the handle to interact with it. The returned deployment
4242
is not yet started and you need to call :meth:`~DSSAPIDeployerDeployment.start_update`
@@ -45,6 +45,7 @@ def create_deployment(self, deployment_id, service_id, infra_id, version):
4545
:param str service_id: Identifier of the API Service to target
4646
:param str infra_id: Identifier of the deployment infrastructure to use
4747
:param str version_id: Identifier of the API Service version to deploy
48+
:param boolean ignore_warnings: ignore warnings concerning the governance status of the model version(s) to deploy
4849
:rtype: :class:`DSSAPIDeployerDeployment`
4950
"""
5051
settings = {
@@ -53,7 +54,7 @@ def create_deployment(self, deployment_id, service_id, infra_id, version):
5354
"infraId" : infra_id,
5455
"version" : version
5556
}
56-
self.client._perform_json("POST", "/api-deployer/deployments", body=settings)
57+
self.client._perform_json("POST", "/api-deployer/deployments", params={"ignoreWarnings": ignore_warnings}, body=settings)
5758
return self.get_deployment(deployment_id)
5859

5960
def list_stages(self):
@@ -81,19 +82,21 @@ def list_infras(self, as_objects=True):
8182
else:
8283
return l
8384

84-
def create_infra(self, infra_id, stage, type):
85+
def create_infra(self, infra_id, stage, type, govern_check_policy="NO_CHECK"):
8586
"""
8687
Creates a new infrastructure on the API Deployer and returns the handle to interact with it.
8788
8889
:param str infra_id: Unique Identifier of the infra to create
8990
:param str stage: Infrastructure stage. Stages are configurable on each API Deployer
9091
:param str type: STATIC or KUBERNETES
92+
:param str govern_check_policy: PREVENT, WARN, or NO_CHECK depending if the deployer will check wether the saved model versions deployed on this infrastructure has to be managed and approved in Dataiku Govern
9193
:rtype: :class:`DSSAPIDeployerInfra`
9294
"""
9395
settings = {
9496
"id": infra_id,
9597
"stage": stage,
9698
"type": type,
99+
"govern_check_policy": govern_check_policy,
97100
}
98101
self.client._perform_json("POST", "/api-deployer/infras", body=settings)
99102
return self.get_infra(infra_id)
@@ -308,6 +311,16 @@ def get_status(self):
308311

309312
return DSSAPIDeployerDeploymentStatus(self.client, self.deployment_id, light, heavy)
310313

314+
def get_governance_status(self, version=""):
315+
"""
316+
Returns the governance status about this deployment if applicable
317+
It covers all the embedded model versions
318+
319+
:param str version: (Optional) The specific package version of the published service to get status from. If empty, consider all the versions used in the deployment generation mapping.
320+
:rtype: dict InforMessages containing the governance status
321+
"""
322+
return self.client._perform_json("GET", "/api-deployer/deployments/%s/governance-status" % (self.deployment_id), params={ "version": version })
323+
311324
def get_settings(self):
312325
"""
313326
Gets the settings of this deployment. If you want to modify the settings, you need to
@@ -334,15 +347,29 @@ def start_update(self):
334347

335348
return DSSFuture(self.client, future_response.get('jobId', None), future_response)
336349

337-
def delete(self):
350+
def delete(self, disable_first=False):
338351
"""
339-
Deletes this deployment
352+
Deletes this deployment. The disable_first flag automatically disables the deployment
353+
before its deletion.
354+
355+
:param boolean disable_first: If True, automatically disables this deployment before deleting it.
356+
If False, will raise an Exception if this deployment is enabled.
340357
341-
You may only delete a deployment if it is disabled and has been updated after disabling it.
342358
"""
343-
return self.client._perform_empty(
344-
"DELETE", "/api-deployer/deployments/%s" % (self.deployment_id))
345359

360+
# Check if the deployment is disabled
361+
is_enabled = self.get_status().light_status["deploymentBasicInfo"].get("enabled")
362+
if is_enabled and not disable_first:
363+
raise Exception("Deployment {} deletion failed: deployment must be disabled first.".format(self.deployment_id))
364+
if is_enabled:
365+
settings = self.get_settings()
366+
settings.set_enabled(enabled=False)
367+
settings.save()
368+
self.client._perform_empty(
369+
"DELETE", "/api-deployer/deployments/%s" % (self.deployment_id))
370+
371+
372+
346373

347374
class DSSAPIDeployerDeploymentSettings(object):
348375
"""
@@ -381,12 +408,15 @@ def set_single_version(self, version):
381408
"generation": version
382409
}
383410

384-
def save(self):
411+
def save(self, ignore_warnings=False):
385412
"""
386413
Saves back these settings to the deployment
414+
415+
:param boolean ignore_warnings: ignore warnings concerning the governance status of the model version(s) to deploy
387416
"""
388417
self.client._perform_empty(
389418
"PUT", "/api-deployer/deployments/%s/settings" % (self.deployment_id),
419+
params = { "ignoreWarnings" : ignore_warnings },
390420
body = self.settings)
391421

392422

@@ -518,7 +548,7 @@ def delete(self):
518548
519549
You may only delete a service if it has no deployments on it anymore.
520550
"""
521-
return self.client._perform_empty(
551+
self.client._perform_empty(
522552
"DELETE", "/api-deployer/services/%s" % (self.service_id))
523553

524554

dataikuapi/dss/metrics.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ class ComputedMetrics(object):
44
def __init__(self, raw):
55
self.raw = raw
66

7+
def get_raw(self):
8+
return self.raw
9+
710
def get_metric_by_id(self, id):
811
all_ids = []
912
for metric in self.raw["metrics"]:

dataikuapi/dss/ml.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1804,6 +1804,10 @@ def get_raw(self):
18041804
"""
18051805
return self.details
18061806

1807+
@property
1808+
def full_id(self):
1809+
return self.details["fullModelId"]
1810+
18071811
def get_raw_snippet(self):
18081812
"""
18091813
Gets the raw dictionary of trained model snippet.
@@ -1863,7 +1867,7 @@ def get_diagnostics(self):
18631867
:returns: list of diagnostics
18641868
:rtype: list of type `dataikuapi.dss.ml.DSSMLDiagnostic`
18651869
"""
1866-
diagnostics = self.details.get("trainDiagnostics", {})
1870+
diagnostics = self.details.get("mlDiagnostics", {})
18671871
return [DSSMLDiagnostic(d) for d in diagnostics.get("diagnostics", [])]
18681872

18691873
def generate_documentation(self, folder_id=None, path=None):
@@ -3348,7 +3352,7 @@ def get_settings(self):
33483352
else:
33493353
return DSSClusteringMLTaskSettings(self.client, self.project_key, self.analysis_id, self.mltask_id, settings)
33503354

3351-
def train(self, session_name=None, session_description=None):
3355+
def train(self, session_name=None, session_description=None, run_queue=False):
33523356
"""
33533357
Trains models for this ML Task
33543358
@@ -3366,7 +3370,7 @@ def train(self, session_name=None, session_description=None):
33663370
:return: A list of model identifiers
33673371
:rtype: list of strings
33683372
"""
3369-
train_ret = self.start_train(session_name, session_description)
3373+
train_ret = self.start_train(session_name, session_description, run_queue)
33703374
self.wait_train_complete()
33713375
return self.get_trained_models_ids(session_id = train_ret["sessionId"])
33723376

@@ -3395,7 +3399,7 @@ def ensemble(self, model_ids=None, method=None):
33953399
return train_ret
33963400

33973401

3398-
def start_train(self, session_name=None, session_description=None):
3402+
def start_train(self, session_name=None, session_description=None, run_queue=False):
33993403
"""
34003404
Starts asynchronously a new train session for this ML Task.
34013405
@@ -3406,7 +3410,8 @@ def start_train(self, session_name=None, session_description=None):
34063410
"""
34073411
session_info = {
34083412
"sessionName" : session_name,
3409-
"sessionDescription" : session_description
3413+
"sessionDescription" : session_description,
3414+
"runQueue": run_queue
34103415
}
34113416

34123417
return self.client._perform_json(
@@ -3521,6 +3526,16 @@ def delete_trained_model(self, model_id):
35213526
self.client._perform_empty(
35223527
"DELETE", "/projects/%s/models/lab/%s/%s/models/%s" % (self.project_key, self.analysis_id, self.mltask_id, model_id))
35233528

3529+
def train_queue(self):
3530+
"""
3531+
Trains this MLTask's queue
3532+
3533+
:return: A dict including the next sessionID to be trained in the queue
3534+
:rtype dict
3535+
"""
3536+
return self.client._perform_json(
3537+
"POST", "/projects/%s/models/lab/%s/%s/actions/train-queue" % (self.project_key, self.analysis_id, self.mltask_id))
3538+
35243539
def deploy_to_flow(self, model_id, model_name, train_dataset, test_dataset=None, redo_optimization=True):
35253540
"""
35263541
Deploys a trained model from this ML Task to a saved model + train recipe in the Flow.
@@ -3606,3 +3621,17 @@ def guess(self, prediction_type=None, reguess_level=None):
36063621
"PUT",
36073622
"/projects/%s/models/lab/%s/%s/guess" % (self.project_key, self.analysis_id, self.mltask_id),
36083623
params = obj)
3624+
3625+
3626+
class DSSMLTaskQueues(object):
3627+
"""
3628+
Iterable listing of MLTask queues
3629+
"""
3630+
def __init__(self, data):
3631+
self.data = data
3632+
3633+
def __iter__(self):
3634+
return self.data["queues"].__iter__()
3635+
3636+
def get_raw(self):
3637+
return self.data

0 commit comments

Comments
 (0)