Skip to content

Commit 10c66ff

Browse files
committed
Add in an example notebook for migrating models from SAS Viya 3.5 to SAS Viya 4.
1 parent 7cc0855 commit 10c66ff

File tree

2 files changed

+277
-0
lines changed

2 files changed

+277
-0
lines changed
Binary file not shown.
Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "8ac109d6-4746-4ac6-befc-8fcd249f8739",
6+
"metadata": {
7+
"Collapsed": "false"
8+
},
9+
"source": [
10+
"Copyright © 2023, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.\n",
11+
"SPDX-License-Identifier: Apache-2.0"
12+
]
13+
},
14+
{
15+
"cell_type": "markdown",
16+
"id": "417ee379-53a8-490a-9cb1-d06e6387dd3c",
17+
"metadata": {
18+
"Collapsed": "false"
19+
},
20+
"source": [
21+
"# Model Migration: Moving Models from SAS Viya 3.5 to SAS Viya 4 in SAS Model Manager\n",
22+
"\n",
23+
"This notebook provides an example scenario for downloading a model from SAS Viya 3.5, converting it in to a format acceptable for SAS Viya 4, then uploads the model to a SAS Viya 4 server. In order to complete the conversion, the following files are modified:\n",
24+
"- dmcas_epscorecode.sas (deleted)\n",
25+
"- dmcas_packagescore.sas (deleted)\n",
26+
"- score.sas (deleted)\n",
27+
"- ModelProperties.json\n",
28+
"- fileMetadata.json\n",
29+
"- python score code file\n",
30+
"\n",
31+
"Over the course of this notebook, a model from SAS Viya 3.5 is downloaded to a specified directory. The model contents are modified to the format expected in SAS Viya 4, then rezipped into a single model zip file. This new model is then uploaded to a SAS Viya 4 server. Optionally, the model zip file can be deleted, leaving the directory in an empty state for the next model to be migrated."
32+
]
33+
},
34+
{
35+
"cell_type": "code",
36+
"execution_count": null,
37+
"id": "29860b48-27af-428a-9012-676711a6220a",
38+
"metadata": {},
39+
"outputs": [],
40+
"source": [
41+
"import io\n",
42+
"import zipfile\n",
43+
"from pathlib import Path\n",
44+
"\n",
45+
"from sasctl._services.model_repository import ModelRepository as mr\n",
46+
"from sasctl.core import is_uuid\n",
47+
"from sasctl.utils import convert_model_zip\n",
48+
"from sasctl import Session"
49+
]
50+
},
51+
{
52+
"cell_type": "code",
53+
"execution_count": null,
54+
"id": "d20270bb-6b3a-4e07-ae7c-6e3181ec2f11",
55+
"metadata": {},
56+
"outputs": [],
57+
"source": [
58+
"def set_model_directory(path=Path.cwd()):\n",
59+
" \"\"\"\n",
60+
" Set or create an empty directory path for model migration\n",
61+
"\n",
62+
" Parameters\n",
63+
" ----------\n",
64+
" path : str or Path, optional\n",
65+
" A string or Path object pointing at an empty or nonexistent directory; default is the current working directory.\n",
66+
"\n",
67+
" Returns\n",
68+
" -------\n",
69+
" dir_path : Path\n",
70+
" A Path object indicating the empty directory for models.\n",
71+
" Raises\n",
72+
" ------\n",
73+
" FileExistsError:\n",
74+
" The provided directory is not empty.\n",
75+
" \"\"\"\n",
76+
" # Check if provided path is a valid directory and is empty\n",
77+
" if Path(path).is_dir():\n",
78+
" # Check if directory is not empty\n",
79+
" if any(Path(path).iterdir()):\n",
80+
" raise FileExistsError(f\"The directory {str(Path(path))} is not empty. Please either provide\" +\n",
81+
" \" an empty directory path or path of a directory to be created.\")\n",
82+
" return path\n",
83+
" elif Path(path).is_file():\n",
84+
" print(\"The provided path points at a file. Attempting to create a new directory with the name of the file.\")\n",
85+
" new_path = set_model_directory(Path(path).parent / Path(path).stem)\n",
86+
" return new_path\n",
87+
" else:\n",
88+
" Path(path).mkdir(parents=True)\n",
89+
" print(f\"The {str(Path(path))} directory was created.\")\n",
90+
" return path"
91+
]
92+
},
93+
{
94+
"cell_type": "code",
95+
"execution_count": null,
96+
"id": "a5dc813b-3ddc-4e45-946a-2920b16b3e58",
97+
"metadata": {},
98+
"outputs": [],
99+
"source": [
100+
"def download_viya35_model(path, model35, project35=None):\n",
101+
" \"\"\" Download the model zip for a SAS Viya 3.5 model from SAS Model Manager. Then unzip the model and delete the\n",
102+
" zip archive downloaded.\n",
103+
"\n",
104+
" Parameters\n",
105+
" ----------\n",
106+
" path : Path object\n",
107+
" Parent directory for models to be migrated.\n",
108+
" model : str or dict\n",
109+
" The name or id of the model, or a dictionary representation of the model.\n",
110+
" project : str or dict, optional\n",
111+
" The name or id of the project, or a dictionary representation of the project. Default value is None.\n",
112+
"\n",
113+
" Returns\n",
114+
" -------\n",
115+
" model : dict\n",
116+
" A dictionary representation of the model.\n",
117+
" project : dict or None\n",
118+
" A dictionary representation of the project.\n",
119+
" \"\"\"\n",
120+
" if project35:\n",
121+
" project35 = mr.get_project(project35)\n",
122+
" if is_uuid(model35):\n",
123+
" model35 = mr.get_model(model35)\n",
124+
" elif isinstance(model35, dict) and \"id\" in model35:\n",
125+
" model35 = model35\n",
126+
" else:\n",
127+
" model35 = mr.list_models(filter=f\"and(eq(projectName,'{project35.name}'),eq(name,'{model35}'))\")[0]\n",
128+
" else:\n",
129+
" # If only a name and no project is provided, the correct model may not be found from the repository\n",
130+
" if (not is_uuid(model35)) and (not isinstance(model35, dict)):\n",
131+
" print(\"No project was provided for model {}.\".format(model) + \" If another model with the same name\" +\n",
132+
" \" exists in the repository, it is not guaranteed to be the model you are attempting to reference.\")\n",
133+
" model35 = mr.get_model(model35)\n",
134+
"\n",
135+
" model_zip = mr.get(f\"models/{model35.id}\", params={\"format\": \"zip\"}, format_=\"content\")\n",
136+
" zip_path = Path(path) / (model35.name + \".zip\")\n",
137+
" with open(zip_path, \"wb\") as z_file:\n",
138+
" z_file.write(model_zip)\n",
139+
" print(f\"Model {model35.name} downloaded to {str(zip_path)}.\")\n",
140+
" with zipfile.ZipFile(str(zip_path), \"r\") as zip_contents:\n",
141+
" zip_contents.extractall(path)\n",
142+
" print(\"Model contents have been extracted from {}.\".format(str(zip_path)))\n",
143+
" zip_path.unlink()\n",
144+
" try:\n",
145+
" return model35, mr.get_project(model35.projectId)\n",
146+
" except AttributeError:\n",
147+
" return model35, None"
148+
]
149+
},
150+
{
151+
"cell_type": "code",
152+
"execution_count": null,
153+
"id": "ce90b596-649c-48f0-b21a-6e20142c21f0",
154+
"metadata": {},
155+
"outputs": [],
156+
"source": [
157+
"def convert_and_upload_model(path, model4, project4=None):\n",
158+
" \"\"\" Convert the model Viya 3.5 model contents to the Viya 4 model content format, then upload the model to a\n",
159+
" specified Viya 4 server.\n",
160+
"\n",
161+
" Parameters\n",
162+
" ----------\n",
163+
" path : str or Path object\n",
164+
" A string or Path object where the Viya 3.5 model contents are located.\n",
165+
" model : str or dict\n",
166+
" The name or id of the model, or a dictionary representation of the model.\n",
167+
" project : str or dict, optional\n",
168+
" The name or id of the project, or a dictionary representation of the project. Default value is None.\n",
169+
" \"\"\"\n",
170+
" convert_model_zip(path)\n",
171+
" \n",
172+
" project4 = mr.get_project(project4)\n",
173+
"\n",
174+
" # List all files in directory\n",
175+
" files = [x for x in path.glob(\"**/*\") if x.is_file()]\n",
176+
" # Zip all files in directory into a zip archive\n",
177+
" with zipfile.ZipFile(str(path / (model4 + \".zip\")), \"w\") as zip_file:\n",
178+
" for file in files:\n",
179+
" zip_file.write(str(file), arcname=file.name)\n",
180+
" file.unlink()\n",
181+
"\n",
182+
" # Import the model into SAS Model Manager\n",
183+
" with open(path / (model4 + \".zip\"), \"rb\") as zip_file:\n",
184+
" if project4:\n",
185+
" response = mr.import_model_from_zip(model4, project4, io.BytesIO(zip_file.read()))\n",
186+
" else:\n",
187+
" params = {\"name\": model4.name, \"type\": \"ZIP\", \"versionOption\": \"latest\"}\n",
188+
" params = \"&\".join(\"{}={}\".format(k, v) for k, v in params.items())\n",
189+
" response = mr.post(\n",
190+
" \"/models#octetStream\",\n",
191+
" data=io.BytesIO(zip_file.read()).read(),\n",
192+
" params=params,\n",
193+
" headers={\"Content-Type\": \"application/octet-stream\"},\n",
194+
" )\n",
195+
" # Uncomment if you want to delete the converted model zip\n",
196+
" # Path(path / model4.name + \".zip\").unlink()\n",
197+
"\n",
198+
" try:\n",
199+
" print(\n",
200+
" \"Model was successfully imported into SAS Model Manager as {} with UUID: {}.\".format(\n",
201+
" response.name, response.id\n",
202+
" )\n",
203+
" )\n",
204+
" except AttributeError:\n",
205+
" print(\"Model, {}, failed to import into SAS Model Manager.\".format(model.name))"
206+
]
207+
},
208+
{
209+
"cell_type": "code",
210+
"execution_count": null,
211+
"id": "36ba4f92-c446-4481-9213-37a178d33d47",
212+
"metadata": {},
213+
"outputs": [],
214+
"source": [
215+
"model_name = \"ExampleModel\"\n",
216+
"project_name = \"ExampleProject\"\n",
217+
"viya35_host = \"demo3.5.sas.com\"\n",
218+
"viya4_host = \"demo4.sas.com\"\n",
219+
"username = \"sasuser\"\n",
220+
"password = \"saspass\"\n",
221+
"path = Path.cwd() / \"data/ModelMigration\""
222+
]
223+
},
224+
{
225+
"cell_type": "code",
226+
"execution_count": null,
227+
"id": "9b0c93d8-93b7-4b98-a5e9-e7a21890e67d",
228+
"metadata": {},
229+
"outputs": [],
230+
"source": [
231+
"set_model_directory(path)"
232+
]
233+
},
234+
{
235+
"cell_type": "code",
236+
"execution_count": null,
237+
"id": "ba6a807a-85e2-4be8-b31c-77bf19bf5abb",
238+
"metadata": {},
239+
"outputs": [],
240+
"source": [
241+
"with Session(viya35_host, username, password, protocol=\"http\"):\n",
242+
" model, project = download_viya35_model(path, model_name, project_name)\n",
243+
"with Session(viya4_host, username, password, protocol=\"http\"):\n",
244+
" convert_and_upload_model(path, model_name, project_name)"
245+
]
246+
},
247+
{
248+
"cell_type": "code",
249+
"execution_count": null,
250+
"id": "40d9c737-f38b-4262-aa4d-e328a4d73c2b",
251+
"metadata": {},
252+
"outputs": [],
253+
"source": []
254+
}
255+
],
256+
"metadata": {
257+
"kernelspec": {
258+
"display_name": "dev-py38",
259+
"language": "python",
260+
"name": "dev-py38"
261+
},
262+
"language_info": {
263+
"codemirror_mode": {
264+
"name": "ipython",
265+
"version": 3
266+
},
267+
"file_extension": ".py",
268+
"mimetype": "text/x-python",
269+
"name": "python",
270+
"nbconvert_exporter": "python",
271+
"pygments_lexer": "ipython3",
272+
"version": "3.8.16"
273+
}
274+
},
275+
"nbformat": 4,
276+
"nbformat_minor": 5
277+
}

0 commit comments

Comments
 (0)