1
+ #!/usr/bin/env python
2
+ # encoding: utf-8
3
+ #
4
+ # Copyright © 2022, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ import re
8
+ import json
9
+ import pandas as pd
10
+
11
+ from pathlib import Path
12
+
13
+ def convertMetadata (zPath , pythonScoreCode = None ):
14
+ '''Modify the model metadata to match new requirements in SAS Viya 4. The
15
+ scoreCodeType in the ModelProperties.json file should be labelled as 'Python'.
16
+ The file associated with the 'score' role should be a Python file.
17
+
18
+ Parameters
19
+ ----------
20
+ zPath : string or Path object
21
+ Location of files in the SAS Viya 3.5 model zip.
22
+ pythonScoreCode : string, optional
23
+ File name of the Python score code. If None, then the name is
24
+ determined by the files in the model zip. Default value is None.
25
+
26
+ Returns
27
+ -------
28
+ scoreResource : string
29
+ File name of the score resource file.
30
+ pythonScoreCode : string
31
+ File name of the Python score code file.
32
+
33
+ Raises
34
+ ------
35
+ SyntaxError :
36
+ If no pythonScoreCode name is provided, but there are multiple Python
37
+ files in the zPath, then a SyntaxError is raised asking for further
38
+ clarification as to which file is the Python score code.
39
+ '''
40
+ # Replace the value of scoreCodeType to 'Python' in ModelProperties.json
41
+ with open (Path (zPath ) / "ModelProperties.json" , "r" ) as jFile :
42
+ modelProperties = json .loads (jFile .read ())
43
+
44
+ modelProperties .update ({"scoreCodeType" : "Python" })
45
+ propIndex = list (modelProperties .keys ())
46
+ propValues = list (modelProperties .values ())
47
+ outputJSON = pd .Series (propValues , index = propIndex )
48
+
49
+ with open (Path (zPath ) / "ModelProperties.json" , "w" ) as jFile :
50
+ dfDump = pd .Series .to_dict (outputJSON .transpose ())
51
+ json .dump (dfDump , jFile , indent = 4 , skipkeys = True )
52
+ print ("ModelProperties.json has been modified and rewritten for SAS Viya 4" )
53
+
54
+ # Replace the 'score' role file with Python score code file
55
+ with open (Path (zPath ) / "fileMetaData.json" , "r" ) as jFile :
56
+ metaData = json .loads (jFile .read ())
57
+ if pythonScoreCode is None :
58
+ numPyFiles = 0
59
+ for file in zPath .glob ('*.py' ):
60
+ pythonScoreCode = file .name
61
+ numPyFiles = numPyFiles + 1
62
+ if numPyFiles > 1 :
63
+ message = ("More than one Python file was found, therefore the score code" +
64
+ " the score code could not be determined. Please provide the" +
65
+ " name of the Python score code file as an argument." )
66
+ raise SyntaxError (message )
67
+ for i in range (len (metaData )):
68
+ if metaData [i ]["role" ] == "score" :
69
+ metaData [i ].update ({"name" : pythonScoreCode })
70
+ if metaData [i ]["role" ] == "scoreResource" :
71
+ scoreResource = metaData [i ]["name" ]
72
+ with open (Path (zPath ) / "fileMetaData.json" , "w" ) as jFile :
73
+ json .dump (metaData , jFile , indent = 4 , skipkeys = True )
74
+ print ("fileMetaData.json has been modified and rewritten for SAS Viya 4" )
75
+
76
+ return scoreResource , pythonScoreCode
77
+
78
+ def convertScoreCode (zPath , scoreResource , pythonScoreCode ):
79
+ '''Convert the Python score code used in SAS Viya 3.5 into score code usable in
80
+ SAS Viya 4. The two adjustments are including an 'import settings' statement and
81
+ replacing score resource access calls that reference an explicit path with
82
+ 'settings.pickle_path' + <scoreResource>.
83
+
84
+ Parameters
85
+ ----------
86
+ zPath : string or Path object
87
+ Location of files in the SAS Viya 3.5 model zip.
88
+ scoreResource : string
89
+ File name of the score resource file.
90
+ pythonScoreCode : string
91
+ File name of the Python score code file.
92
+ '''
93
+ # Read entire text of score code
94
+ with open (Path (zPath ) / pythonScoreCode , "r" ) as pyFile :
95
+ scoreCode = pyFile .read ()
96
+
97
+ # Add the import settings line to the score code
98
+ scoreCode = "import settings\n " + scoreCode
99
+
100
+ # Search for all directory paths in score code that contain the scoreResource
101
+ oldString = re .findall (r"'\/.*?\.[\w:]+'" , scoreCode )
102
+ oldString = [s for s in oldString if scoreResource in s ]
103
+ # Remove duplicates, as .replace() checks for all instances
104
+ oldString = list (set (oldString ))
105
+ # Replace Viya 3.5 style with Viya 4 style
106
+ newString = "settings.pickle_path + '{}'" .format (scoreResource )
107
+ for oldStr in oldString :
108
+ scoreCode = scoreCode .replace (oldStr , newString )
109
+
110
+ # Write new text of score code to file
111
+ with open (Path (zPath ) / pythonScoreCode , "w" ) as pyFile :
112
+ pyFile .write (scoreCode )
113
+ print ("{} has been modified and rewritten for SAS Viya 4" .format (pythonScoreCode ))
114
+
115
+ def deleteSASFiles (zPath ):
116
+ '''Remove .sas score files created for SAS Viya 3.5, which are no longer
117
+ needed in SAS Viya 4. These files are typically named score.sas,
118
+ dmcas_packagescorecode.sas, or dmcas_epscorescode.sas.
119
+
120
+ Parameters
121
+ ----------
122
+ zPath : string or Path object
123
+ Location of files in the SAS Viya 3.5 model zip.
124
+ '''
125
+ zPath = Path (zPath )
126
+ for file in zPath .glob ('*.sas' ):
127
+ file .unlink ()
128
+
129
+ def convertModelZip (zPath , pythonScoreCode = None ):
130
+ '''Pass the directory path of the model to be converted from SAS Viya 3.5 to
131
+ SAS Viya 4. Then the function removes any .sas files, adjusts the ModelProperties.json
132
+ and fileMetaData.json files, and modifies the score code.
133
+
134
+ Parameters
135
+ ----------
136
+ zPath : string or Path object
137
+ Location of files in the SAS Viya 3.5 model zip.
138
+ pythonScoreCode : string, optional
139
+ File name of the Python score code. If None, then the name is
140
+ determined by the files in the model zip. Default value is None.
141
+ '''
142
+ deleteSASFiles (zPath )
143
+ scoreResource , pythonScoreCode = convertMetadata (zPath , pythonScoreCode = None )
144
+ convertScoreCode (zPath , scoreResource , pythonScoreCode )
145
+
0 commit comments