12
12
13
13
"""
14
14
15
- import os
16
- import sys
17
15
import argparse
18
- import traceback
19
16
import json
20
- import re
21
17
import logging
22
- log = logging .getLogger (__name__ )
18
+ import os
19
+ import re
20
+ import sys
21
+ import traceback
23
22
24
- import yaml
25
23
import nbformat
26
- from nbconvert import HTMLExporter
24
+ import requests
25
+ import yaml
27
26
from arcgis .gis import GIS
27
+ from nbconvert import HTMLExporter
28
+
29
+ log = logging .getLogger (__name__ )
28
30
29
31
ITEMS_METADATA_YAML_PATH = os .path .join ("." , "items_metadata.yaml" )
30
32
THUMBNAILS_DIR = os .path .join ("." , "static" , "thumbnails" )
35
37
NB_PORTAL_TYPE_KEYWORDS = "Notebook, Python"
36
38
NB_ITEM_PROPERTIES_RUNTIME_STAMP_ADVANCED = \
37
39
{'notebookRuntimeName' : 'ArcGIS Notebook Python 3 Advanced' ,
38
- 'notebookRuntimeVersion' : '5.0 ' }
40
+ 'notebookRuntimeVersion' : '' }
39
41
NB_ITEM_PROPERTIES_RUNTIME_STAMP_ADVANCED_GPU = \
40
- {'notebookRuntimeName' : 'ArcGIS Notebook Python 3 Advanced with GPU support' ,
41
- 'notebookRuntimeVersion' : '5.0 ' }
42
+ {'notebookRuntimeName' : 'ArcGIS Notebook Python 3 Advanced with GPU support' ,
43
+ 'notebookRuntimeVersion' : '' }
42
44
NB_ITEM_PROPERTIES_RUNTIME_STAMP_STANDARD = \
43
45
{'notebookRuntimeName' : 'ArcGIS Notebook Python 3 Standard' ,
44
- 'notebookRuntimeVersion' : '5.0 ' }
46
+ 'notebookRuntimeVersion' : '' }
45
47
NB_ITEM_FOLDER = "Notebook Samples"
46
48
49
+
47
50
def _main ():
48
51
"""Parses arguments, connects to GIS, reads YAML, uploads NBs"""
49
52
args = _parse_cmd_line_args ()
50
53
_setup_logging (args )
51
54
gis = GIS (args .portal_url , args .username ,
52
55
args .password , verify_cert = False )
56
+ if args .version :
57
+ NB_ITEM_PROPERTIES_RUNTIME_STAMP_ADVANCED ["notebookRuntimeVersion" ] = args .version
58
+ NB_ITEM_PROPERTIES_RUNTIME_STAMP_STANDARD ["notebookRuntimeVersion" ] = args .version
59
+ else :
60
+ _get_current_runtime (gis )
53
61
items_metadata_yaml = _read_items_metadata_yaml ()
54
62
if args .replace_profiles :
55
63
_replace_profiles ()
@@ -58,100 +66,120 @@ def _main():
58
66
if s .failed_uploads :
59
67
raise Exception (f"Some uploads failed: { s .failed_uploads } " )
60
68
69
+
61
70
def _parse_cmd_line_args ():
62
71
"""Parse CMD args, returns an object instance of all user passed in args"""
63
- parser = argparse .ArgumentParser (description = "Takes all notebooks " \
64
- "this in `gallery` directory, and will upload it to the specified " \
65
- "portal/org in the right group with the right categories. " \
66
- "(default is geosaurus.maps.arcgis.com, 'Esri Sample Notebooks' group)" ,
67
- formatter_class = argparse .RawTextHelpFormatter )
72
+ parser = argparse .ArgumentParser (description = "Takes all notebooks "
73
+ "this in `gallery` directory, and will upload it to the specified "
74
+ "portal/org in the right group with the right categories. "
75
+ "(default is geosaurus.maps.arcgis.com, 'Esri Sample Notebooks' group)" ,
76
+ formatter_class = argparse .RawTextHelpFormatter )
68
77
parser .add_argument ("--username" , "-u" , type = str ,
69
- help = "Required username for the portal/org" )
78
+ help = "Required username for the portal/org" )
70
79
parser .add_argument ("--password" , "-p" , type = str ,
71
- help = "Required password for the portal/org" )
80
+ help = "Required password for the portal/org" )
72
81
parser .add_argument ("--portal-url" , "-r" , type = str ,
73
- help = "The portal to connect to (Default:geosaurus.maps.arcgis.com)" ,
74
- default = "https://geosaurus.maps.arcgis.com/" )
82
+ help = "The portal to connect to (default: geosaurus.maps.arcgis.com)" ,
83
+ default = "https://geosaurus.maps.arcgis.com/" )
84
+ parser .add_argument ("--version" , "-ver" , type = str ,
85
+ help = "The version of notebook runtime to set on sample notebooks "
86
+ "(default: the latest version)" ,
87
+ default = "" )
75
88
parser .add_argument ("--verbose" , "-v" , action = "store_true" ,
76
- help = "Print all DEBUG log messages instead of just INFO" )
89
+ help = "Print all DEBUG log messages instead of just INFO" )
77
90
parser .add_argument ("--replace-profiles" , "-c" , action = "store_true" ,
78
- help = "Replace all profiles in notebooks with their appropriate username " \
79
- "and passwords. Does this by running misc/tools/replace_profiles.py" )
80
- args = parser .parse_args (sys .argv [1 :]) # don't use filename as 1st arg
91
+ help = "Replace all profiles in notebooks with their appropriate username "
92
+ "and passwords. Does this by running misc/tools/replace_profiles.py" )
93
+ args = parser .parse_args (sys .argv [1 :]) # don't use filename as 1st arg
81
94
return args
82
95
96
+
83
97
def _setup_logging (args ):
84
98
"""Sets up the logging based on args"""
85
99
if args .verbose :
86
100
log .setLevel (logging .DEBUG )
87
101
else :
88
- log .setLevel (logging .INFO )
102
+ log .setLevel (logging .INFO )
89
103
stdout_handler = logging .StreamHandler (stream = sys .stdout )
90
104
stdout_handler .setLevel (logging .DEBUG )
91
105
stdout_handler .setFormatter (logging .Formatter (
92
- '----- %(levelname)s | ' \
93
- '%(asctime)s | ' \
94
- '%(filename)s line %(lineno)d' \
95
- ' -----\n ' \
106
+ '----- %(levelname)s | '
107
+ '%(asctime)s | '
108
+ '%(filename)s line %(lineno)d'
109
+ ' -----\n '
96
110
'"%(message)s"' ))
97
111
log .addHandler (stdout_handler )
98
112
log .info ("Logging at level {}." .format (logging .getLevelName (log .level )))
99
113
log .debug ("args passed in => {}" .format (args ))
100
114
115
+
116
+ def _get_current_runtime (gis ):
117
+ ntbk_svr = gis .notebook_server [0 ]
118
+ rest = ntbk_svr ._url .replace ("/admin" , "/rest" )
119
+ rest_info = requests .get (f"{ rest } /info?f=json" , timeout = 5 , verify = False )
120
+ version = rest_info .json ()["currentRuntimeVersion" ]
121
+ NB_ITEM_PROPERTIES_RUNTIME_STAMP_ADVANCED ["notebookRuntimeVersion" ] = version
122
+ NB_ITEM_PROPERTIES_RUNTIME_STAMP_STANDARD ["notebookRuntimeVersion" ] = version
123
+ return version
124
+
125
+
101
126
def _read_items_metadata_yaml ():
102
127
"""Returns the items_metadata.yaml file as a dict"""
103
- with open (ITEMS_METADATA_YAML_PATH ) as f :
128
+ with open (ITEMS_METADATA_YAML_PATH , encoding = "utf-8" ) as f :
104
129
return yaml .safe_load (f )
105
130
131
+
106
132
def _replace_profiles ():
107
133
"""Runs misc/tools/replace_profiles.py to go through each notebook in the
108
134
repo and replace profiles with usernames/passwords
109
135
"""
110
136
cmd = f"{ sys .executable } { REPLACE_PROFILES_SCRIPT } "
111
137
os .system (cmd )
112
138
139
+
113
140
class ItemsUploader :
114
141
def __init__ (self , gis , items_metadata_yaml ):
115
142
self ._gis = gis
116
143
self ._items_metadata_yaml = items_metadata_yaml
117
144
self .failed_uploads = []
118
145
119
- def upload_items (self , share_after_upload = True ):
146
+ def upload_items (self , share_after_upload = True ):
120
147
for entry in self ._items_metadata_yaml ["samples" ] + \
121
- self ._items_metadata_yaml ["guides" ] + \
122
- self ._items_metadata_yaml ["labs" ]:
148
+ self ._items_metadata_yaml ["guides" ] + \
149
+ self ._items_metadata_yaml ["labs" ]:
123
150
self ._stage_and_upload_item (entry , share_after_upload )
124
151
125
- def _stage_and_upload_item (self , entry , share_after_upload = True ):
152
+ def _stage_and_upload_item (self , entry , share_after_upload = True ):
126
153
log .info (f"Uploading { entry ['title' ]} " )
127
154
log .debug (f" sample: { entry } " )
128
155
try :
129
156
nb_path = entry ["path" ]
130
157
self ._preupload_check (entry ['title' ], nb_path )
131
- runtime_stamp = self ._infer_runtime_stamp (entry .get ("runtime" , "standard" ))
158
+ runtime_stamp = self ._infer_runtime_stamp (
159
+ entry .get ("runtime" , "standard" ))
132
160
categories = entry .get ("categories" , None )
133
161
self ._stamp_file_with_runtime (nb_path , runtime_stamp )
134
162
item_id = self ._infer_item_id (entry ["url" ])
135
163
item = self .update_item (
136
- item_id = item_id ,
137
- item_type = NB_PORTAL_TYPE ,
138
- item_type_keywords = NB_PORTAL_TYPE_KEYWORDS ,
139
- title = entry ['title' ],
140
- categories = categories ,
141
- snippet = entry ['snippet' ],
142
- description = entry ['description' ],
143
- license_info = entry ['licenseInfo' ],
144
- tags = entry ['tags' ],
145
- nb_path = nb_path ,
146
- runtime_stamp = runtime_stamp ,
147
- thumbnail = entry ['thumbnail' ])
164
+ item_id = item_id ,
165
+ item_type = NB_PORTAL_TYPE ,
166
+ item_type_keywords = NB_PORTAL_TYPE_KEYWORDS ,
167
+ title = entry ['title' ],
168
+ categories = categories ,
169
+ snippet = entry ['snippet' ],
170
+ description = entry ['description' ],
171
+ license_info = entry ['licenseInfo' ],
172
+ tags = entry ['tags' ],
173
+ nb_path = nb_path ,
174
+ runtime_stamp = runtime_stamp ,
175
+ thumbnail = entry ['thumbnail' ])
148
176
if share_after_upload :
149
- item .share ( everyone = True )
177
+ item .sharing . sharing_level = "EVERYONE"
150
178
item .protect ()
151
179
if categories :
152
180
self ._assign_categories_to_item (item , categories )
153
181
self ._apply_html_preview_to_item (item , nb_path )
154
- log .info (f" Uploaded succeded -> { item .homepage } " )
182
+ log .info (f" Uploaded succeeded -> { item .homepage } " )
155
183
except Exception as e :
156
184
self .failed_uploads .append (entry ['title' ])
157
185
log .warn (f" Couldn't upload { entry ['title' ]} : { e } " )
@@ -170,11 +198,11 @@ def update_item(self, item_id, item_type, item_type_keywords, title, categories,
170
198
snippet , description , license_info , tags , nb_path ,
171
199
runtime_stamp , thumbnail ):
172
200
"""Actually uploads the notebook item to the portal"""
173
- item_properties = {"title" : title ,
174
- "snippet" : snippet ,
175
- "description" : description ,
176
- "licenseInfo" : license_info ,
177
- "tags" : tags ,
201
+ item_properties = {"title" : title ,
202
+ "snippet" : snippet ,
203
+ "description" : description ,
204
+ "licenseInfo" : license_info ,
205
+ "tags" : tags ,
178
206
"properties" : runtime_stamp }
179
207
if categories :
180
208
item_properties ["categories" ] = categories
@@ -188,11 +216,12 @@ def update_item(self, item_id, item_type, item_type_keywords, title, categories,
188
216
log .debug (f'item { existing_item .homepage } exists, updating...' )
189
217
item_properties ["url" ] = existing_item .homepage
190
218
existing_item .update (item_properties ,
191
- data = nb_path ,
192
- thumbnail = thumbnail )
219
+ data = nb_path ,
220
+ thumbnail = thumbnail )
193
221
resp = existing_item
194
222
else :
195
- raise Exception (f"Could not find item { item_id } to update. Failing!" )
223
+ raise Exception (
224
+ f"Could not find item { item_id } to update. Failing!" )
196
225
return resp
197
226
198
227
def _assign_categories_to_item (self , item , categories ):
@@ -207,8 +236,8 @@ def _apply_html_preview_to_item(self, item, nb_path):
207
236
208
237
json_file_name = "notebook_preview.json"
209
238
json_file_path = os .path .join ("." , json_file_name )
210
- with open (json_file_path , 'w' ) as f :
211
- json .dump ({"html" : html_str }, f )
239
+ with open (json_file_path , 'w' , encoding = "utf-8" ) as f :
240
+ json .dump ({"html" : html_str }, f )
212
241
213
242
if item .resources .list ():
214
243
item .resources .remove ()
@@ -239,11 +268,12 @@ def _stamp_file_with_runtime(self, notebook_file_path, runtime_stamp):
239
268
nb ['metadata' ]['esriNotebookRuntime' ] = runtime_stamp
240
269
nbformat .write (nb , notebook_file_path , nbformat .NO_CONVERT )
241
270
271
+
242
272
if __name__ == "__main__" :
243
273
try :
244
274
_main ()
245
275
sys .exit (0 )
246
276
except Exception as e :
247
277
log .exception (e )
248
- log .info ("Program did not succesfully complete (unhandled exception)" )
278
+ log .info ("Program did not successfully complete (unhandled exception)" )
249
279
sys .exit (1 )
0 commit comments