Skip to content

Commit 81ccee4

Browse files
committed
test publish destinations
1 parent a962c14 commit 81ccee4

5 files changed

+556
-44
lines changed

src/sasctl/_services/model_publish.py

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,32 @@
44
# Copyright © 2019, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
55
# SPDX-License-Identifier: Apache-2.0
66

7+
"""Enables publishing objects such as models to various destinations."""
8+
79
import re
810

911
from .service import Service
12+
from .. import services
1013

1114

1215
class ModelPublish(Service):
13-
"""The Model Publish API provides support for publishing objects (such as
14-
models) to CAS, Hadoop, SAS Micro Analytic Service, or Teradata.
16+
"""Enables publishing objects such as models to various destinations.
17+
18+
The service provides methods for managing publish destinations like CAS,
19+
Hadoop, Teradata, or SAS Micro Analytic Service
1520
"""
21+
1622
_SERVICE_ROOT = '/modelPublish'
1723

1824
@staticmethod
1925
def _publish_name(name):
2026
"""Create a valid module name from an input string.
2127
22-
Model Publishing only permits names that adhere to the following restrictions:
28+
Model Publishing only permits names that adhere to the following
29+
restrictions:
2330
- Name must start with a letter or an underscore.
24-
- Name may not contain any spaces or special characters other than underscore.
31+
- Name may not contain any spaces or special characters other than
32+
underscore.
2533
2634
Parameters
2735
----------
@@ -30,8 +38,8 @@ def _publish_name(name):
3038
Returns
3139
-------
3240
str
33-
"""
3441
42+
"""
3543
# Remove all non-word characters
3644
name = re.sub(r'[^\w]', '', str(name))
3745

@@ -41,31 +49,53 @@ def _publish_name(name):
4149

4250
return name
4351

44-
def list_models(self):
45-
return self.get('/models').get('items', [])
52+
@classmethod
53+
def list_models(cls):
54+
return cls.get('/models').get('items', [])
4655

47-
def list_destinations(self):
48-
return self.get('/destinations').get('items', [])
56+
list_destinations, get_destination, update_destination, \
57+
delete_destination = Service._crud_funcs('/destinations',
58+
'destination')
4959

50-
def publish_model(self, model, destination, name=None, code=None,
60+
@classmethod
61+
def publish_model(cls, model, destination, name=None, code=None,
5162
notes=None):
63+
"""
5264
65+
Parameters
66+
----------
67+
model : str or dict
68+
The name or id of the model, or a dictionary representation of
69+
the model.
70+
destination : str or dict
71+
The name or id of the publishing destination, or a dictionary
72+
representation of the destination
73+
name : str, optional
74+
Name of the published model. Defaults to the model name.
75+
code : str, optional
76+
The code to be published.
77+
notes
78+
79+
Returns
80+
-------
81+
82+
"""
5383
code_types = {
5484
'ds2package': 'ds2',
5585
'datastep': 'datastep',
5686
'': ''
5787
}
5888

5989
model = services.model_repository.get_model(model)
60-
model_uri = services.model_repository.get_model_link(model, 'self')
90+
model_uri = services.model_repository.get_model_link(model, 'cls')
6191

6292
# Get score code from registry if no code specified
6393
if code is None:
6494
code_link = services.model_repository.get_model_link(model,
6595
'scoreCode',
6696
True)
6797
if code_link:
68-
code = get(code_link['href'])
98+
code = cls.get(code_link['href'])
6999

70100
request = dict(
71101
name=name or model.get('name'),
@@ -84,7 +114,6 @@ def publish_model(self, model, destination, name=None, code=None,
84114
}
85115

86116
request['modelContents'] = [modelContents]
87-
return self.post('/models', json=request, headers={
88-
'Content-Type': 'application/vnd.sas.models.publishing.request+json'})
89-
90-
117+
return cls.post('/models', json=request, headers={
118+
'Content-Type':
119+
'application/vnd.sas.models.publishing.request+json'})

src/sasctl/tasks.py

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,21 @@
44
# Copyright © 2019, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
55
# SPDX-License-Identifier: Apache-2.0
66

7+
"""Commonly used tasks in the analytics life cycle."""
8+
79
import json
810
import logging
911
import pickle
1012
import re
11-
import time
1213
import sys
1314
import warnings
1415

15-
from .utils.pymas import from_pickle, PyMAS
16-
from sasctl.services import model_management as mm, model_publish as mp, \
17-
model_repository as mr
1816
from . import utils
19-
from .core import get, get_link, request_link, RestObj
17+
from .core import RestObj, get, get_link, request_link
18+
from .services import model_management as mm, model_publish as mp, \
19+
model_repository as mr
20+
from .utils.pymas import PyMAS, from_pickle
21+
2022

2123
logger = logging.getLogger(__name__)
2224

@@ -40,35 +42,42 @@ def _sklearn_to_dict(model):
4042
scoreCodeType='ds2MultiType',
4143
trainCodeType='Python',
4244
function=mappings.get(model._estimator_type, model._estimator_type),
43-
tool='Python {}.{}'.format(sys.version_info.major, sys.version_info.minor),
44-
properties=[{'name': k, 'value': v} for k, v in model.get_params().items()]
45+
tool='Python %s.%s'
46+
% (sys.version_info.major, sys.version_info.minor),
47+
properties=[{'name': k, 'value': v}
48+
for k, v in model.get_params().items()]
4549
)
4650

4751
return result
4852

4953

50-
def register_model(model, name, project, repository=None, input=None, version='latest', files=None, force=False):
54+
def register_model(model, name, project, repository=None, input=None,
55+
version='latest', files=None, force=False):
5156
"""Register a model in the model repository.
5257
5358
Parameters
5459
----------
5560
model : swat.CASTable or sklearn.BaseEstimator
56-
The model to register. If an instance of ``swat.CASTable`` the table is assumed to hold an ASTORE, which will
57-
be downloaded and used to construct the model to register. If a scikit-learn estimator, the model will be
58-
pickled and uploaded to the registry and score code will be generated for publishing the model to MAS.
61+
The model to register. If an instance of ``swat.CASTable`` the table
62+
is assumed to hold an ASTORE, which will be downloaded and used to
63+
construct the model to register. If a scikit-learn estimator, the
64+
model will be pickled and uploaded to the registry and score code will
65+
be generated for publishing the model to MAS.
5966
name : str
6067
Designated name for the model in the repository.
6168
project : str or dict
62-
The name or id of the project, or a dictionary representation of the project.
69+
The name or id of the project, or a dictionary representation of
70+
the project.
6371
repository : str or dict, optional
64-
The name or id of the repository, or a dictionary representation of the repository. If omitted, the default
65-
repository will be used.
72+
The name or id of the repository, or a dictionary representation of
73+
the repository. If omitted, the default repository will be used.
6674
input
6775
version : {'new', 'latest', int}, optional
6876
Version number of the project in which the model should be created.
6977
files :
7078
force : bool, optional
71-
Create dependencies such as projects and repositories if they do not already exist.
79+
Create dependencies such as projects and repositories if they do not
80+
already exist.
7281
7382
Returns
7483
-------
@@ -77,17 +86,15 @@ def register_model(model, name, project, repository=None, input=None, version='l
7786
7887
Notes
7988
-----
80-
If the specified model is a CAS table the model data and metadata will be written to a temporary zip file and then
81-
imported using model_repository.import_model_from_zip.
82-
83-
If the specified model is from the Scikit-Learn package, the model will be created using
84-
model_repository.create_model and any additional files will be uploaded as content.
89+
If the specified model is a CAS table the model data and metadata will be
90+
written to a temporary zip file and then imported using
91+
model_repository.import_model_from_zip.
8592
86-
Examples
87-
--------
93+
If the specified model is from the Scikit-Learn package, the model will be
94+
created using model_repository.create_model and any additional files will
95+
be uploaded as content.
8896
8997
"""
90-
9198
# TODO: Create new version if model already exists
9299
# TODO: Allow file info to be specified
93100
# TODO: Performance stats
@@ -103,7 +110,10 @@ def register_model(model, name, project, repository=None, input=None, version='l
103110
if p is None and not create_project:
104111
raise ValueError("Project '{}' not found".format(project))
105112

106-
repository = mr.default_repository() if repository is None else mr.get_repository(repository)
113+
if repository is None:
114+
repository = mr.default_repository()
115+
else:
116+
repository = mr.get_repository(repository)
107117

108118
# Unable to find or create the repo.
109119
if repository is None:
@@ -182,7 +192,8 @@ def register_model(model, name, project, repository=None, input=None, version='l
182192
return model
183193

184194

185-
def publish_model(model, destination, code=None, max_retries=60, **kwargs):
195+
def publish_model(model, destination, code=None, max_retries=60,
196+
replace=False, **kwargs):
186197
"""Publish a model to a configured publishing destination.
187198
188199
Parameters
@@ -192,6 +203,9 @@ def publish_model(model, destination, code=None, max_retries=60, **kwargs):
192203
destination : str
193204
code : optional
194205
max_retries : int, optional
206+
replace : bool, optional
207+
Whether to overwrite the model if it already exists in
208+
the `destination`
195209
kwargs : optional
196210
additional arguments will be passed to the underlying publish functions.
197211
@@ -200,17 +214,18 @@ def publish_model(model, destination, code=None, max_retries=60, **kwargs):
200214
201215
Notes
202216
-----
203-
If no code is specified, the model is assumed to be already registered in the model repository and Model Manager's
204-
publishing functionality will be used.
217+
If no code is specified, the model is assumed to be already registered in
218+
the model repository and Model Manager's publishing functionality will be
219+
used.
205220
206221
Otherwise, the model publishing API will be used.
207222
208223
See Also
209224
--------
210225
:meth:`model_management.publish_model <.ModelManagement.publish_model>`
211226
:meth:`model_publish.publish_model <.ModelPublish.publish_model>`
212-
"""
213227
228+
"""
214229
# Submit a publishing request
215230
if code is None:
216231
publish_req = mm.publish_model(model, destination, **kwargs)

0 commit comments

Comments
 (0)