Skip to content

Commit d23e786

Browse files
committed
Merge remote-tracking branch 'origin/master' into feature/proxy-user
2 parents 0b42420 + d3e2455 commit d23e786

File tree

16 files changed

+3083
-271
lines changed

16 files changed

+3083
-271
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
*.pyc
22
.idea
3-
.iml
3+
*.iml

dataikuapi/apinode_client.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ def __init__(self, uri, service_id, api_key=None):
1616
"""
1717
DSSBaseClient.__init__(self, "%s/%s" % (uri, "public/api/v1/%s" % service_id), api_key)
1818

19-
def predict_record(self, endpoint_id, features, forced_generation=None, dispatch_key=None, context=None):
19+
def predict_record(self, endpoint_id, features, forced_generation=None, dispatch_key=None, context=None,
20+
with_explanations=None, explanation_method=None, n_explanations=None, n_explanations_mc_steps=None):
2021
"""
2122
Predicts a single record on a DSS API node endpoint (standard or custom prediction)
2223
@@ -25,12 +26,24 @@ def predict_record(self, endpoint_id, features, forced_generation=None, dispatch
2526
:param forced_generation: See documentation about multi-version prediction
2627
:param dispatch_key: See documentation about multi-version prediction
2728
:param context: Optional, Python dictionary of additional context information. The context information is logged, but not directly used.
29+
:param with_explanations: Optional, whether individual explanations should be computed for each record. The prediction endpoint must be compatible. If None, will use the value configured in the endpoint.
30+
:param explanation_method: Optional, method to compute explanations. Valid values are 'SHAPLEY' or 'ICE'. If None, will use the value configured in the endpoint.
31+
:param n_explanations: Optional, number of explanations to output per prediction. If None, will use the value configured in the endpoint.
32+
:param n_explanations_mc_steps: Optional, precision parameter for SHAPLEY method, higher means more precise but slower (between 25 and 1000).
33+
If None, will use the value configured in the endpoint.
2834
2935
:return: a Python dict of the API answer. The answer contains a "result" key (itself a dict)
3036
"""
31-
obj = {
32-
"features" :features
37+
obj = {
38+
"features": features,
39+
"explanations": {
40+
"enabled": with_explanations,
41+
"method": explanation_method,
42+
"nExplanations": n_explanations,
43+
"nMonteCarloSteps": n_explanations_mc_steps
44+
}
3345
}
46+
3447
if context is not None:
3548
obj["context"] = context
3649
if forced_generation is not None:
@@ -40,14 +53,20 @@ def predict_record(self, endpoint_id, features, forced_generation=None, dispatch
4053

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

43-
def predict_records(self, endpoint_id, records, forced_generation=None, dispatch_key=None):
56+
def predict_records(self, endpoint_id, records, forced_generation=None, dispatch_key=None, with_explanations=None,
57+
explanation_method=None, n_explanations=None, n_explanations_mc_steps=None):
4458
"""
4559
Predicts a batch of records on a DSS API node endpoint (standard or custom prediction)
4660
4761
:param str endpoint_id: Identifier of the endpoint to query
4862
: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.
4963
:param forced_generation: See documentation about multi-version prediction
5064
:param dispatch_key: See documentation about multi-version prediction
65+
:param with_explanations: Optional, whether individual explanations should be computed for each record. The prediction endpoint must be compatible. If None, will use the value configured in the endpoint.
66+
:param explanation_method: Optional, method to compute explanations. Valid values are 'SHAPLEY' or 'ICE'. If None, will use the value configured in the endpoint.
67+
:param n_explanations: Optional, number of explanations to output per prediction. If None, will use the value configured in the endpoint.
68+
:param n_explanations_mc_steps: Optional, precision parameter for SHAPLEY method, higher means more precise but slower (between 25 and 1000).
69+
If None, will use the value configured in the endpoint.
5170
5271
:return: a Python dict of the API answer. The answer contains a "results" key (which is an array of result objects)
5372
"""
@@ -57,7 +76,13 @@ def predict_records(self, endpoint_id, records, forced_generation=None, dispatch
5776
raise ValueError("Each record must contain a 'features' dict")
5877

5978
obj = {
60-
"items" : records
79+
"items": records,
80+
"explanations": {
81+
"enabled": with_explanations,
82+
"method": explanation_method,
83+
"nExplanations": n_explanations,
84+
"nMonteCarloSteps": n_explanations_mc_steps
85+
}
6186
}
6287

6388
if forced_generation is not None:

dataikuapi/dss/app.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import sys
2+
import re
3+
import os.path as osp
4+
from .future import DSSFuture
5+
from dataikuapi.utils import DataikuException
6+
import random, string
7+
8+
def random_string(length):
9+
return ''.join(random.choice(string.ascii_letters) for _ in range(length))
10+
11+
class DSSApp(object):
12+
"""
13+
A handle to interact with a app on the DSS instance.
14+
Do not create this class directly, instead use :meth:`dataikuapi.DSSClient.get_app``
15+
"""
16+
def __init__(self, client, app_id):
17+
self.client = client
18+
self.app_id = app_id
19+
20+
########################################################
21+
# Instances
22+
########################################################
23+
24+
def create_instance(self, instance_key, instance_name, wait=True):
25+
"""
26+
Creates a new instance of this app. Each instance. must have a globally unique
27+
instance key, separate from any project key across the whole DSS instance
28+
29+
:return:
30+
"""
31+
future_resp = self.client._perform_json(
32+
"POST", "/apps/%s/instances" % self.app_id, body={
33+
"targetProjectKey" : instance_key,
34+
"targetProjectName" : instance_name
35+
})
36+
future = DSSFuture(self.client, future_resp.get("jobId", None), future_resp)
37+
if wait:
38+
result = future.wait_for_result()
39+
return DSSAppInstance(self.client, instance_key)
40+
else:
41+
return future
42+
43+
def make_random_project_key(self):
44+
slugified_app_id = re.sub(r'[^A-Za-z_0-9]+', '_', self.app_id)
45+
return "%s_tmp_%s" % (slugified_app_id, random_string(10))
46+
47+
def create_temporary_instance(self):
48+
"""
49+
Creates a new temporary instance of this app.
50+
The return value should be used as a Python context manager. Upon exit, the temporary app
51+
instance is deleted
52+
:return a :class:`TemporaryDSSAppInstance`
53+
"""
54+
key = self.make_random_project_key()
55+
instance = self.create_instance(key, key, True)
56+
return TemporaryDSSAppInstance(self.client, key)
57+
58+
def list_instance_keys(self):
59+
"""
60+
List the existing instances of this app
61+
62+
:return a list of instance keys, each as a string
63+
"""
64+
return [x["projectKey"] for x in self.list_instances()]
65+
66+
def list_instances(self):
67+
"""
68+
List the existing instances of this app
69+
70+
:rtype: list of dicts
71+
:return a list of instances, each as a dict containing at least a "projectKey" field
72+
"""
73+
return self.client._perform_json(
74+
"GET", "/apps/%s/instances/" % self.app_id)
75+
76+
def get_instance(self, instance_key):
77+
return DSSAppInstance(self.client, instance_key)
78+
79+
80+
def get_manifest(self):
81+
raw_data = self.client._perform_json("GET", "/apps/%s/" % self.app_id)
82+
project_key = self.app_id[8:] if self.app_id.startswith('PROJECT_') else None
83+
return DSSAppManifest(self.client, raw_data, project_key)
84+
85+
86+
class DSSAppManifest(object):
87+
88+
def __init__(self, client, raw_data, project_key=None):
89+
"""The manifest for an app. Do not create this class directly"""
90+
self.client = client
91+
self.raw_data = raw_data
92+
self.project_key = project_key
93+
94+
def get_raw(self):
95+
return self.raw_data
96+
97+
def get_all_actions(self):
98+
return [x for section in self.raw_data["homepageSections"] for x in section["tiles"]]
99+
100+
def get_runnable_scenarios(self):
101+
"""Return the scenario identifiers that are declared as actions for this app"""
102+
return [x["scenarioId"] for x in self.get_all_actions() if x["type"] == "SCENARIO_RUN"]
103+
104+
def save(self):
105+
"""Saves the changes to this manifest object back to the template project"""
106+
if self.project_key is None:
107+
raise Exception("This manifest object wasn't created from a project, cannot be saved back")
108+
self.client._perform_empty("PUT", "/projects/%s/app-manifest" % self.project_key, body=self.raw_data)
109+
110+
111+
class DSSAppInstance(object):
112+
113+
def __init__(self, client, project_key):
114+
self.client = client
115+
self.project_key = project_key
116+
117+
def get_as_project(self):
118+
"""
119+
Get the :class:`dataikuapi.dss.project DSSProject` corresponding to this app instance
120+
"""
121+
return self.client.get_project(self.project_key)
122+
123+
def get_manifest(self):
124+
"""
125+
Get the app manifest for this instance, as a :class:`DSSAppManifest`
126+
"""
127+
raw_data = self.client._perform_json("GET", "/projects/%s/app-manifest" % self.project_key)
128+
return DSSAppManifest(self.client, raw_data)
129+
130+
class TemporaryDSSAppInstance(DSSAppInstance):
131+
"""internal class"""
132+
133+
def __init__(self, client, project_key):
134+
DSSAppInstance.__init__(self, client,project_key)
135+
136+
137+
def close(self):
138+
self.get_as_project().delete(drop_data=True)
139+
140+
def __enter__(self,):
141+
return self
142+
143+
def __exit__(self, type, value, traceback):
144+
self.close()

0 commit comments

Comments
 (0)