1212
1313"""
1414
15- import os
16- import sys
1715import argparse
18- import traceback
1916import json
20- import re
2117import logging
22- log = logging .getLogger (__name__ )
18+ import os
19+ import re
20+ import sys
21+ import traceback
2322
24- import yaml
2523import nbformat
26- from nbconvert import HTMLExporter
24+ import requests
25+ import yaml
2726from arcgis .gis import GIS
27+ from nbconvert import HTMLExporter
28+
29+ log = logging .getLogger (__name__ )
2830
2931ITEMS_METADATA_YAML_PATH = os .path .join ("." , "items_metadata.yaml" )
3032THUMBNAILS_DIR = os .path .join ("." , "static" , "thumbnails" )
3537NB_PORTAL_TYPE_KEYWORDS = "Notebook, Python"
3638NB_ITEM_PROPERTIES_RUNTIME_STAMP_ADVANCED = \
3739 {'notebookRuntimeName' : 'ArcGIS Notebook Python 3 Advanced' ,
38- 'notebookRuntimeVersion' : '5.0 ' }
40+ 'notebookRuntimeVersion' : '' }
3941NB_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' : '' }
4244NB_ITEM_PROPERTIES_RUNTIME_STAMP_STANDARD = \
4345 {'notebookRuntimeName' : 'ArcGIS Notebook Python 3 Standard' ,
44- 'notebookRuntimeVersion' : '5.0 ' }
46+ 'notebookRuntimeVersion' : '' }
4547NB_ITEM_FOLDER = "Notebook Samples"
4648
49+
4750def _main ():
4851 """Parses arguments, connects to GIS, reads YAML, uploads NBs"""
4952 args = _parse_cmd_line_args ()
5053 _setup_logging (args )
5154 gis = GIS (args .portal_url , args .username ,
5255 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 )
5361 items_metadata_yaml = _read_items_metadata_yaml ()
5462 if args .replace_profiles :
5563 _replace_profiles ()
@@ -58,100 +66,120 @@ def _main():
5866 if s .failed_uploads :
5967 raise Exception (f"Some uploads failed: { s .failed_uploads } " )
6068
69+
6170def _parse_cmd_line_args ():
6271 """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 )
6877 parser .add_argument ("--username" , "-u" , type = str ,
69- help = "Required username for the portal/org" )
78+ help = "Required username for the portal/org" )
7079 parser .add_argument ("--password" , "-p" , type = str ,
71- help = "Required password for the portal/org" )
80+ help = "Required password for the portal/org" )
7281 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 = "" )
7588 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" )
7790 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
8194 return args
8295
96+
8397def _setup_logging (args ):
8498 """Sets up the logging based on args"""
8599 if args .verbose :
86100 log .setLevel (logging .DEBUG )
87101 else :
88- log .setLevel (logging .INFO )
102+ log .setLevel (logging .INFO )
89103 stdout_handler = logging .StreamHandler (stream = sys .stdout )
90104 stdout_handler .setLevel (logging .DEBUG )
91105 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 '
96110 '"%(message)s"' ))
97111 log .addHandler (stdout_handler )
98112 log .info ("Logging at level {}." .format (logging .getLevelName (log .level )))
99113 log .debug ("args passed in => {}" .format (args ))
100114
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+
101126def _read_items_metadata_yaml ():
102127 """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 :
104129 return yaml .safe_load (f )
105130
131+
106132def _replace_profiles ():
107133 """Runs misc/tools/replace_profiles.py to go through each notebook in the
108134 repo and replace profiles with usernames/passwords
109135 """
110136 cmd = f"{ sys .executable } { REPLACE_PROFILES_SCRIPT } "
111137 os .system (cmd )
112138
139+
113140class ItemsUploader :
114141 def __init__ (self , gis , items_metadata_yaml ):
115142 self ._gis = gis
116143 self ._items_metadata_yaml = items_metadata_yaml
117144 self .failed_uploads = []
118145
119- def upload_items (self , share_after_upload = True ):
146+ def upload_items (self , share_after_upload = True ):
120147 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" ]:
123150 self ._stage_and_upload_item (entry , share_after_upload )
124151
125- def _stage_and_upload_item (self , entry , share_after_upload = True ):
152+ def _stage_and_upload_item (self , entry , share_after_upload = True ):
126153 log .info (f"Uploading { entry ['title' ]} " )
127154 log .debug (f" sample: { entry } " )
128155 try :
129156 nb_path = entry ["path" ]
130157 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" ))
132160 categories = entry .get ("categories" , None )
133161 self ._stamp_file_with_runtime (nb_path , runtime_stamp )
134162 item_id = self ._infer_item_id (entry ["url" ])
135163 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' ])
148176 if share_after_upload :
149- item .share ( everyone = True )
177+ item .sharing . sharing_level = "EVERYONE"
150178 item .protect ()
151179 if categories :
152180 self ._assign_categories_to_item (item , categories )
153181 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 } " )
155183 except Exception as e :
156184 self .failed_uploads .append (entry ['title' ])
157185 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,
170198 snippet , description , license_info , tags , nb_path ,
171199 runtime_stamp , thumbnail ):
172200 """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 ,
178206 "properties" : runtime_stamp }
179207 if categories :
180208 item_properties ["categories" ] = categories
@@ -188,11 +216,12 @@ def update_item(self, item_id, item_type, item_type_keywords, title, categories,
188216 log .debug (f'item { existing_item .homepage } exists, updating...' )
189217 item_properties ["url" ] = existing_item .homepage
190218 existing_item .update (item_properties ,
191- data = nb_path ,
192- thumbnail = thumbnail )
219+ data = nb_path ,
220+ thumbnail = thumbnail )
193221 resp = existing_item
194222 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!" )
196225 return resp
197226
198227 def _assign_categories_to_item (self , item , categories ):
@@ -207,8 +236,8 @@ def _apply_html_preview_to_item(self, item, nb_path):
207236
208237 json_file_name = "notebook_preview.json"
209238 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 )
212241
213242 if item .resources .list ():
214243 item .resources .remove ()
@@ -239,11 +268,12 @@ def _stamp_file_with_runtime(self, notebook_file_path, runtime_stamp):
239268 nb ['metadata' ]['esriNotebookRuntime' ] = runtime_stamp
240269 nbformat .write (nb , notebook_file_path , nbformat .NO_CONVERT )
241270
271+
242272if __name__ == "__main__" :
243273 try :
244274 _main ()
245275 sys .exit (0 )
246276 except Exception as e :
247277 log .exception (e )
248- log .info ("Program did not succesfully complete (unhandled exception)" )
278+ log .info ("Program did not successfully complete (unhandled exception)" )
249279 sys .exit (1 )
0 commit comments