Skip to content

Commit dcf03c2

Browse files
committed
import model from score code table
1 parent 512cf21 commit dcf03c2

File tree

5 files changed

+740
-564
lines changed

5 files changed

+740
-564
lines changed

src/sasctl/tasks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ def get_version(x):
187187
# If model is a CASTable then assume it holds an ASTORE model.
188188
# Import these via a ZIP file.
189189
if 'swat.cas.table.CASTable' in str(type(model)):
190-
zipfile = utils.create_package_from_astore(model)
190+
zipfile = utils.create_package(model)
191191

192192
if create_project:
193193
project = mr.create_project(project, repository)

src/sasctl/utils/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
# Copyright © 2019, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
55
# SPDX-License-Identifier: Apache-2.0
66

7-
from .astore import create_package_from_astore
7+
from .astore import create_package, create_package_from_astore
88

99

src/sasctl/utils/astore.py

Lines changed: 105 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,77 @@
1212
import uuid
1313
import zipfile
1414

15+
import six
16+
1517
try:
1618
import swat
1719
except ImportError:
1820
swat = None
1921

2022

23+
def create_package(table):
24+
"""Create an importable model package from a CAS table.
25+
26+
Parameters
27+
----------
28+
table : swat.CASTable
29+
The CAS table containing an ASTORE or score code.
30+
31+
Returns
32+
-------
33+
BytesIO
34+
A byte stream representing a ZIP archive which can be imported.
35+
36+
See Also
37+
--------
38+
:meth:`model_repository.import_model_from_zip <.ModelRepository.import_model_from_zip>`
39+
40+
"""
41+
if swat is None:
42+
raise RuntimeError(
43+
"The 'swat' package is required to work with SAS models.")
44+
45+
assert isinstance(table, swat.CASTable)
46+
47+
if 'DataStepSrc' in table.columns:
48+
return create_package_from_datastep(table)
49+
else:
50+
return create_package_from_astore(table)
51+
52+
53+
def create_package_from_datastep(table):
54+
"""Create an importable model package from a score code table.
55+
56+
Parameters
57+
----------
58+
table : swat.CASTable
59+
The CAS table containing the score code.
60+
61+
Returns
62+
-------
63+
BytesIO
64+
A byte stream representing a ZIP archive which can be imported.
65+
66+
See Also
67+
--------
68+
:meth:`model_repository.import_model_from_zip <.ModelRepository.import_model_from_zip>`
69+
70+
"""
71+
assert 'DataStepSrc' in table.columns
72+
sess = table.session.get_connection()
73+
74+
dscode = table.to_frame().loc[0, 'DataStepSrc']
75+
76+
file_metadata = [{'role': 'score', 'name': 'dmcas_scorecode.sas'}]
77+
78+
zip_file = _build_zip_from_files({
79+
'fileMetadata.json': file_metadata,
80+
'dmcas_scorecode.sas': dscode
81+
})
82+
83+
return zip_file
84+
85+
2186
def create_package_from_astore(table):
2287
"""Create an importable model package from an ASTORE.
2388
@@ -81,45 +146,62 @@ def create_package_from_astore(table):
81146
'uri': '/dataTables/dataSources/cas~fs~cas-shared-default~fs~ModelStore/tables/{}'.format(astore_filename),
82147
'key': astore_key}]
83148

149+
zip_file = _build_zip_from_files({
150+
'dmcas_packagescorecode.sas': '\n'.join(package_ds2),
151+
'dmcas_epscorecode.sas': ep_ds2,
152+
astore_filename: astore,
153+
'ModelProperties.json': model_properties,
154+
'fileMetadata.json': file_metadata,
155+
'AstoreMetadata.json': astore_metadata,
156+
'inputVar.json': input_vars,
157+
'outputVar.json': output_vars
158+
})
159+
160+
return zip_file
161+
162+
163+
def _build_zip_from_files(files):
164+
"""Create a ZIP file containing the provided files.
165+
166+
Parameters
167+
----------
168+
files : dict
169+
Dictionary of filename: content to be added to the .zip file.
170+
171+
Returns
172+
-------
173+
BytesIO
174+
Byte stream representation of the .zip file.
175+
176+
"""
84177
try:
85178
# Create a temp folder
86179
folder = tempfile.mkdtemp()
87180

88-
# Closure for easily adding JSON files
89-
def json_file(data, filename):
90-
filename = os.path.join(folder, filename)
91-
with open(filename, 'w') as f:
92-
json.dump(data, f, indent=1)
181+
for k, v in six.iteritems(files):
182+
filename = os.path.join(folder, k)
93183

94-
filename = os.path.join(folder, 'dmcas_packagescorecode.sas')
95-
with open(filename, 'w') as f:
96-
f.write('\n'.join(package_ds2))
184+
# Write JSON file
185+
if os.path.splitext(k)[-1].lower() == '.json':
186+
with open(filename, 'w') as f:
187+
json.dump(v, f, indent=1)
188+
else:
189+
mode = 'wb' if isinstance(v, bytes) else 'w'
97190

98-
filename = os.path.join(folder, 'dmcas_epscorecode.sas')
99-
with open(filename, 'w') as f:
100-
f.write(ep_ds2)
101-
102-
filename = os.path.join(folder, astore_filename)
103-
with open(filename, 'wb') as f:
104-
f.write(astore)
105-
106-
json_file(model_properties, 'ModelProperties.json')
107-
json_file(file_metadata, 'fileMetadata.json')
108-
json_file(astore_metadata, 'AstoreMetadata.json')
109-
json_file(input_vars, 'inputVar.json')
110-
json_file(output_vars, 'outputVar.json')
191+
with open(filename, mode) as f:
192+
f.write(v)
111193

112194
files = os.listdir(folder)
113195

114196
with zipfile.ZipFile(os.path.join(folder, 'model.zip'), 'w') as z:
115197
for file in files:
116198
z.write(os.path.join(folder, file), file)
117199

118-
# Need to return the ZIP file data but also need to ensure the directory is cleaned up.
200+
# Need to return the ZIP file data but also need to ensure the
201+
# directory is cleaned up.
119202
# Read the bytes from disk and return an in memory "file".
120203
with open(os.path.join(folder, 'model.zip'), 'rb') as z:
121204
return io.BytesIO(z.read())
122-
123205
finally:
124206
shutil.rmtree(folder)
125207

tests/cassettes/tests.integration.test_model_repository.TestAStoreModel.test_model_import_swat.json

Lines changed: 265 additions & 182 deletions
Large diffs are not rendered by default.

tests/cassettes/tests.integration.test_tasks.TestModels.test_register_astore_swat.json

Lines changed: 368 additions & 357 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)