Skip to content

Commit d931289

Browse files
authored
Task/dss 12.4.0 bump dataikuapi (#287)
* Sync dataikuapi sources for 12.4.0 * 12.4.0 metadata
1 parent defda98 commit d931289

27 files changed

+887
-116
lines changed

Diff for: HISTORY.txt

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ Changelog
22
==========
33

44

5+
12.4.0 (2023-12-06)
6+
---------------------
7+
8+
* Initial release for DSS 12.4.0
9+
510
12.3.2 (2023-11-16)
611
---------------------
712

Diff for: dataikuapi/apinode_admin_client.py

+14
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,17 @@ def import_code_env_in_cache(self, file_dir, language):
7272
"fileDir": file_dir,
7373
"language": language
7474
})
75+
76+
def register_code_env_in_cache(self, exported_env_dir, built_env_dir, language):
77+
"""
78+
Import a code env in global cache from an exported code env base folder
79+
80+
:param exported_env_dir: path of an exported code env base folder
81+
:param built_env_dir: path where the code env was built and is available
82+
:param language: language of the code env (`python` or `R`)
83+
"""
84+
return self._perform_json("POST", "register-global-code-env", params={
85+
"exportedEnvDir": exported_env_dir,
86+
"builtEnvDir": built_env_dir,
87+
"language": language
88+
})

Diff for: dataikuapi/apinode_client.py

+53-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from .utils import DataikuException
22
from .base_client import DSSBaseClient
33

4+
45
class APINodeClient(DSSBaseClient):
56
"""Entry point for the DSS API Node client
67
This is an API client for the user-facing API of DSS API Node server (user facing API)
@@ -17,6 +18,13 @@ def __init__(self, uri, service_id, api_key=None, bearer_token=None):
1718
"""
1819
DSSBaseClient.__init__(self, "%s/%s" % (uri, "public/api/v1/%s" % service_id), api_key=api_key, bearer_token=bearer_token)
1920

21+
@staticmethod
22+
def _set_dispatch(obj, forced_generation, dispatch_key):
23+
if forced_generation is not None:
24+
obj["dispatch"] = {"forcedGeneration": forced_generation}
25+
elif dispatch_key is not None:
26+
obj["dispatch"] = {"dispatchKey": dispatch_key}
27+
2028
def predict_record(self, endpoint_id, features, forced_generation=None, dispatch_key=None, context=None,
2129
with_explanations=None, explanation_method=None, n_explanations=None, n_explanations_mc_steps=None):
2230
"""
@@ -45,12 +53,9 @@ def predict_record(self, endpoint_id, features, forced_generation=None, dispatch
4553
}
4654
}
4755

56+
self._set_dispatch(obj, forced_generation, dispatch_key)
4857
if context is not None:
4958
obj["context"] = context
50-
if forced_generation is not None:
51-
obj["dispatch"] = {"forcedGeneration" : forced_generation }
52-
elif dispatch_key is not None:
53-
obj["dispatch"] = {"dispatchKey" : dispatch_key }
5459

5560
return self._perform_json("POST", "%s/predict" % endpoint_id, body = obj)
5661

@@ -86,10 +91,7 @@ def predict_records(self, endpoint_id, records, forced_generation=None, dispatch
8691
}
8792
}
8893

89-
if forced_generation is not None:
90-
obj["dispatch"] = {"forcedGeneration" : forced_generation }
91-
elif dispatch_key is not None:
92-
obj["dispatch"] = {"dispatchKey" : dispatch_key }
94+
self._set_dispatch(obj, forced_generation, dispatch_key)
9395

9496
return self._perform_json("POST", "%s/predict-multi" % endpoint_id, body = obj)
9597

@@ -145,13 +147,52 @@ def forecast(self, endpoint_id, records, forced_generation=None, dispatch_key=No
145147

146148
obj = {"items": records}
147149

148-
if forced_generation is not None:
149-
obj["dispatch"] = {"forcedGeneration": forced_generation}
150-
elif dispatch_key is not None:
151-
obj["dispatch"] = {"dispatchKey": dispatch_key}
150+
self._set_dispatch(obj, forced_generation, dispatch_key)
152151

153152
return self._perform_json("POST", "{}/forecast".format(endpoint_id), body=obj)
154153

154+
155+
def predict_effect(self, endpoint_id, features, forced_generation=None, dispatch_key=None):
156+
"""
157+
Predicts the treatment effect of a single record on a DSS API node endpoint (standard causal prediction)
158+
159+
:param str endpoint_id: Identifier of the endpoint to query
160+
:param features: Python dictionary of features of the record
161+
:param forced_generation: See documentation about multi-version prediction
162+
:param dispatch_key: See documentation about multi-version prediction
163+
164+
:return: a Python dict of the API answer. The answer contains a "result" key (itself a dict)
165+
"""
166+
obj = {
167+
"features": features,
168+
}
169+
self._set_dispatch(obj, forced_generation, dispatch_key)
170+
171+
return self._perform_json("POST", "%s/predict-effect" % endpoint_id, body=obj)
172+
173+
def predict_effects(self, endpoint_id, records, forced_generation=None, dispatch_key=None):
174+
"""
175+
Predicts the treatment effects on a batch of records on a DSS API node endpoint (standard causal prediction)
176+
177+
:param str endpoint_id: Identifier of the endpoint to query
178+
:param records: Python list of records. Each record must be a Python dict. Each record must contain a "features" dict (see predict_record) and optionally a "context" dict.
179+
:param dispatch_key: See documentation about multi-version prediction
180+
181+
:return: a Python dict of the API answer. The answer contains a "results" key (which is an array of result objects)
182+
"""
183+
184+
for record in records:
185+
if not "features" in record:
186+
raise ValueError("Each record must contain a 'features' dict")
187+
188+
obj = {
189+
"items": records,
190+
}
191+
192+
self._set_dispatch(obj, forced_generation, dispatch_key)
193+
194+
return self._perform_json("POST", "%s/predict-effect-multi" % endpoint_id, body = obj)
195+
155196
def sql_query(self, endpoint_id, parameters):
156197
"""
157198
Queries a "SQL query" endpoint on a DSS API node

Diff for: dataikuapi/dss/admin.py

+17-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from .future import DSSFuture
22
import json, warnings
33
from datetime import datetime
4+
from ..utils import _timestamp_ms_to_zoned_datetime
45

56

67
class DSSConnectionListItem(dict):
@@ -86,6 +87,15 @@ def get_params(self):
8687
"""
8788
return self["params"]
8889

90+
def get_resolved_params(self):
91+
"""
92+
Get the resolved parameters of the connection, as a dict. May be null depending on the connection type.
93+
94+
:return: the resolved parameters, as a dict. Each connection type has different sets of fields.
95+
:rtype: dict
96+
"""
97+
return self["resolvedParams"]
98+
8999
def get_basic_credential(self):
90100
"""
91101
Get the basic credential (user/password pair) for this connection, if available
@@ -835,7 +845,7 @@ def creation_date(self):
835845
:rtype: :class:`datetime.datetime` or None
836846
"""
837847
timestamp = self.settings["creationDate"] if "creationDate" in self.settings else None
838-
return datetime.fromtimestamp(timestamp / 1000) if timestamp else None
848+
return _timestamp_ms_to_zoned_datetime(timestamp)
839849

840850
def save(self):
841851
"""
@@ -902,7 +912,7 @@ def last_successful_login(self):
902912
:rtype: :class:`datetime.datetime` or None
903913
"""
904914
timestamp = self.activity["lastSuccessfulLogin"]
905-
return datetime.fromtimestamp(timestamp / 1000) if timestamp > 0 else None
915+
return _timestamp_ms_to_zoned_datetime(timestamp)
906916

907917
@property
908918
def last_failed_login(self):
@@ -915,7 +925,7 @@ def last_failed_login(self):
915925
:rtype: :class:`datetime.datetime` or None
916926
"""
917927
timestamp = self.activity["lastFailedLogin"]
918-
return datetime.fromtimestamp(timestamp / 1000) if timestamp > 0 else None
928+
return _timestamp_ms_to_zoned_datetime(timestamp)
919929

920930
@property
921931
def last_session_activity(self):
@@ -931,7 +941,7 @@ def last_session_activity(self):
931941
:rtype: :class:`datetime.datetime` or None
932942
"""
933943
timestamp = self.activity["lastSessionActivity"]
934-
return datetime.fromtimestamp(timestamp / 1000) if timestamp > 0 else None
944+
return _timestamp_ms_to_zoned_datetime(timestamp)
935945

936946

937947
class DSSAuthorizationMatrix(object):
@@ -1916,7 +1926,7 @@ def created_on(self):
19161926
:rtype: :class:`datetime.datetime`
19171927
"""
19181928
timestamp = self["createdOn"]
1919-
return datetime.fromtimestamp(timestamp / 1000) if timestamp > 0 else None
1929+
return _timestamp_ms_to_zoned_datetime(timestamp)
19201930

19211931
@property
19221932
def created_by(self):
@@ -2040,7 +2050,7 @@ def created_on(self):
20402050
:rtype: :class:`datetime.datetime`
20412051
"""
20422052
timestamp = self["createdOn"]
2043-
return datetime.fromtimestamp(timestamp / 1000) if timestamp > 0 else None
2053+
return _timestamp_ms_to_zoned_datetime(timestamp)
20442054

20452055
@property
20462056
def created_by(self):
@@ -2514,10 +2524,7 @@ def last_built(self):
25142524
:rtype: :class:`datetime.datetime`
25152525
"""
25162526
ts = self._data.get("lastBuilt", 0)
2517-
if ts > 0:
2518-
return datetime.fromtimestamp(ts / 1000)
2519-
else:
2520-
return None
2527+
return _timestamp_ms_to_zoned_datetime(ts)
25212528

25222529
class DSSCodeStudioTemplate(object):
25232530
"""

Diff for: dataikuapi/dss/analysis.py

+34
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,40 @@ def create_timeseries_forecasting_ml_task(self, target_variable,
260260
mltask.wait_guess_complete()
261261
return mltask
262262

263+
def create_causal_prediction_ml_task(self,
264+
outcome_variable,
265+
treatment_variable,
266+
prediction_type=None,
267+
wait_guess_complete=True):
268+
"""Creates a new causal prediction task in this visual analysis lab
269+
for a dataset.
270+
271+
:param string outcome_variable: Variable to predict
272+
:param string treatment_variable: Treatment variable
273+
:param string prediction_type: The type of prediction problem, guessed if not provided. Valid values: CAUSAL_BINARY_CLASSIFICATION, CAUSAL_REGRESSION
274+
:param boolean wait_guess_complete: if False, the returned ML task will be in 'guessing' state, i.e. analyzing the input dataset to determine feature handling and algorithms.
275+
You should wait for the guessing to be completed by calling
276+
``wait_guess_complete`` on the returned object before doing anything
277+
else (in particular calling ``train`` or ``get_settings``)
278+
:return :class dataiku.dss.ml.DSSMLTask
279+
"""
280+
281+
obj = {
282+
"taskType": "PREDICTION",
283+
"targetVariable": outcome_variable,
284+
"treatmentVariable": treatment_variable,
285+
"backendType": "PY_MEMORY",
286+
"guessPolicy": "CAUSAL_PREDICTION"
287+
}
288+
if prediction_type is not None:
289+
obj["predictionType"] = prediction_type
290+
ref = self.client._perform_json("POST", "/projects/%s/lab/%s/models/" % (self.project_key, self.analysis_id), body=obj)
291+
mltask = DSSMLTask(self.client, self.project_key, self.analysis_id, ref["mlTaskId"])
292+
293+
if wait_guess_complete:
294+
mltask.wait_guess_complete()
295+
return mltask
296+
263297
def list_ml_tasks(self):
264298
"""
265299
List the ML tasks in this visual analysis

Diff for: dataikuapi/dss/apideployer.py

+9-7
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,25 @@ 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, ignore_warnings=False):
39+
def create_deployment(self, deployment_id, service_id, infra_id, version, endpoint_id=None, 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`
4343
4444
:param str deployment_id: Identifier of the deployment to create
4545
:param str service_id: Identifier of the API Service to target
4646
:param str infra_id: Identifier of the deployment infrastructure to use
47-
:param str version_id: Identifier of the API Service version to deploy
47+
:param str version: Identifier of the API Service version to deploy
48+
:param str endpoint_id: Identifier of the endpoint to deploy if you use a Deploy Anywhere infra. Ignored otherwise
4849
:param boolean ignore_warnings: ignore warnings concerning the governance status of the model version(s) to deploy
4950
:rtype: :class:`DSSAPIDeployerDeployment`
5051
"""
5152
settings = {
52-
"deploymentId" : deployment_id,
53-
"publishedServiceId" : service_id,
54-
"infraId" : infra_id,
55-
"version" : version
53+
"deploymentId": deployment_id,
54+
"publishedServiceId": service_id,
55+
"infraId": infra_id,
56+
"version": version,
57+
"endpointId": endpoint_id
5658
}
5759
self.client._perform_json("POST", "/api-deployer/deployments", params={"ignoreWarnings": ignore_warnings}, body=settings)
5860
return self.get_deployment(deployment_id)
@@ -88,7 +90,7 @@ def create_infra(self, infra_id, stage, type, govern_check_policy="NO_CHECK"):
8890
8991
:param str infra_id: Unique Identifier of the infra to create
9092
:param str stage: Infrastructure stage. Stages are configurable on each API Deployer
91-
:param str type: STATIC or K8S
93+
:param str type: STATIC, K8S, AZURE_ML, SAGEMAKER or VERTEX_AI
9294
:param str govern_check_policy: PREVENT, WARN, or NO_CHECK depending if the deployer will check whether the saved model versions deployed on this infrastructure has to be managed and approved in Dataiku Govern
9395
:rtype: :class:`DSSAPIDeployerInfra`
9496
"""

Diff for: dataikuapi/dss/apiservice.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ def endpoints(self):
123123
:rtype: list[dict]
124124
"""
125125
return self.settings["endpoints"]
126-
127126

128127
def add_prediction_endpoint(self, endpoint_id, saved_model_id):
129128
"""
@@ -164,6 +163,22 @@ def add_forecasting_endpoint(self, endpoint_id, saved_model_id):
164163
"modelRef": saved_model_id
165164
})
166165

166+
def add_causal_prediction_endpoint(self, endpoint_id, saved_model_id, compute_propensity=False):
167+
"""
168+
Add a new "visual causal prediction" endpoint to this API service.
169+
170+
:param string endpoint_id: identifier of the new endpoint to create
171+
:param string saved_model_id: identifier of the saved model (that is currently deployed to the Flow) to use
172+
:param bool compute_propensity: whether propensity should be computed, if True, the model must have a trained propensity model
173+
"""
174+
self.settings["endpoints"].append({
175+
"id": endpoint_id,
176+
"type": "STD_CAUSAL_PREDICTION",
177+
"modelRef": saved_model_id,
178+
"computePropensity": compute_propensity,
179+
"useJava": False # Not supported for causal predictions
180+
})
181+
167182
def save(self):
168183
"""
169184
Save back these settings to the API Service.

Diff for: dataikuapi/dss/codestudio.py

+13-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from ..utils import DataikuException
1+
from ..utils import DataikuException, _timestamp_ms_to_zoned_datetime
22
import json
33
from datetime import datetime
44
from .future import DSSFuture
@@ -213,6 +213,17 @@ def push_to_code_studio(self, zone):
213213
"""
214214
return self.client._perform_json("GET", "/projects/%s/code-studios/%s/push/%s" % (self.project_key, self.code_studio_id, zone))
215215

216+
def change_owner(self, new_owner):
217+
"""
218+
Allows to change the owner of the Code Studio
219+
.. note::
220+
221+
only admins are allowed to change the owner of a code studio.
222+
223+
:param str new_owner: the id of the new owner
224+
"""
225+
self.client._perform_json("POST", "/projects/%s/code-studios/%s/change-owner?newOwner=%s" % (self.project_key, self.code_studio_id, new_owner))
226+
216227
class DSSCodeStudioObjectConflicts(dict):
217228
"""
218229
Summary of the conflicts on zones of a code studio.
@@ -428,10 +439,7 @@ def last_state_change(self):
428439
:rtype: `datetime.datetime`
429440
"""
430441
ts = self.status.get("lastStateChange", 0)
431-
if ts > 0:
432-
return datetime.fromtimestamp(ts / 1000)
433-
else:
434-
return None
442+
return _timestamp_ms_to_zoned_datetime(ts)
435443

436444
def get_zones(self, as_type="names"):
437445
"""

0 commit comments

Comments
 (0)