├── README.md └── Samples ├── Backup Hosted Services.ipynb ├── Clone_ArcGIS_StoryMap.ipynb ├── Copy Organization Categories.ipynb ├── Copy Specific Item.ipynb ├── CopyHubContent.ipynb ├── Copy_Item_Resources_Between_Items.ipynb ├── Export_Users_Content_to_CSV.ipynb ├── Export_Users_to_CSV.ipynb ├── Find_Stale_Portal_Users.ipynb ├── List Group Items - Export to CSV.ipynb ├── List Items And Thumbnail Sizes.ipynb ├── List Items With Corresponding Service URL.ipynb ├── List_Items_With_Corresponding_Service_URL_and_Update.ipynb ├── List_Pro_Licenses_in_AGO_Organization.ipynb ├── Migrate_Enterprise_Users.ipynb ├── Update_IDP_Settings.ipynb ├── Update_Service_Instances.ipynb └── Update_User_Types.ipynb /README.md: -------------------------------------------------------------------------------- 1 | # ArcGIS-Python-API-Samples 2 | Welcome to the ArcGIS Python API Sample Page. These samples below were create based on real-world use-cases. Feel free to add any issues/improvements and contribute your own samples! 3 | 4 | These samples were written using [Jupyter](https://jupyter.org/) notebooks so that they can be easily shared and used interactively. 5 | 6 | # Samples 7 | Go view the samples [here](https://github.com/jmirmelstein/ArcGIS-Python-API-Samples/tree/master/Samples) 8 | 9 | # Help Documentation 10 | https://developers.arcgis.com/python/ 11 | -------------------------------------------------------------------------------- /Samples/Backup Hosted Services.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "** Set a datestamp and log into the GIS **" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 7, 13 | "metadata": {}, 14 | "outputs": [ 15 | { 16 | "name": "stdout", 17 | "output_type": "stream", 18 | "text": [ 19 | "Enter password: ········\n" 20 | ] 21 | } 22 | ], 23 | "source": [ 24 | "import arcgis, datetime, os\n", 25 | "d = datetime.datetime.strftime(datetime.datetime.now(), \"%Y%m%d\")\n", 26 | "\n", 27 | "username = \"\"\n", 28 | "GIS = arcgis.gis.GIS(\"https://arcgis.com\", username=username)" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "**Identify the parameters **\n", 36 | "- [ ] list of items\n", 37 | "- [ ] folder to move the exported datasets to in AGO (if desired, otherwise set to None\n", 38 | "- [ ] export folder for outputs\n", 39 | "- [ ] export format requested\n", 40 | "- [ ] whether or not to delete the exported items from online (delete the zipfile) once downloaded" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 15, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "item_ids = [\n", 50 | " \"3b1b0fb59b864c2b9884608bb8fdcec9\"\n", 51 | " ]\n", 52 | "\n", 53 | "export_AGO_folder = \"export_folder\"\n", 54 | "export_local_folder = r\"c:\\temp\\exports\"\n", 55 | "export_format = \"File Geodatabase\" #options Shapefile, CSV, File Geodatabase, \n", 56 | " #Feature Collection, GEoJSON, Scene Package, KML\n", 57 | "delete_tmp_files_after_export = True" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "metadata": {}, 63 | "source": [ 64 | "**Process each item individually and report on progress**" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 19, 70 | "metadata": {}, 71 | "outputs": [ 72 | { 73 | "name": "stdout", 74 | "output_type": "stream", 75 | "text": [ 76 | "------------------\n", 77 | "Processing item: 3b1b0fb59b864c2b9884608bb8fdcec9\n", 78 | "Exported: \n", 79 | "saved to: c:\\temp\\exports\\20182002_export_ACLEDacled.zip\n", 80 | "Deleted result\n" 81 | ] 82 | } 83 | ], 84 | "source": [ 85 | "for item in item_ids:\n", 86 | " print(\"------------------\\nProcessing item: {}\".format(item))\n", 87 | " item_object = arcgis.gis.Item(GIS, item)\n", 88 | " if item_object.type != \"Feature Service\":\n", 89 | " print(\"Item {} is not a feature service, skipping\".format(item))\n", 90 | " continue\n", 91 | " export_name = d + \"_export_\" + item_object.title\n", 92 | " result_item = item_object.export(export_name, export_format, wait=True)\n", 93 | " print(\"Exported: {}\".format(result_item))\n", 94 | " if export_AGO_folder:\n", 95 | " result_item.move(export_AGO_folder)\n", 96 | " download_result = result_item.download(export_folder)\n", 97 | " print(\"saved to: {}\".format(download_result))\n", 98 | " if delete_tmp_files_after_export:\n", 99 | " result_item.delete()\n", 100 | " print(\"Deleted result\")" 101 | ] 102 | } 103 | ], 104 | "metadata": { 105 | "kernelspec": { 106 | "display_name": "Python 3", 107 | "language": "python", 108 | "name": "python3" 109 | }, 110 | "language_info": { 111 | "codemirror_mode": { 112 | "name": "ipython", 113 | "version": 3 114 | }, 115 | "file_extension": ".py", 116 | "mimetype": "text/x-python", 117 | "name": "python", 118 | "nbconvert_exporter": "python", 119 | "pygments_lexer": "ipython3", 120 | "version": "3.6.2" 121 | } 122 | }, 123 | "nbformat": 4, 124 | "nbformat_minor": 2 125 | } 126 | -------------------------------------------------------------------------------- /Samples/Clone_ArcGIS_StoryMap.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Note: All Credit to University of Michigan for this notebook! - https://umich.maps.arcgis.com/home/item.html?id=9523eaa22a8043b087f798e51a9bee1b" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "# Clone an ArcGIS StoryMap\n", 15 | "\n", 16 | "A Notebook to guide you through you through the steps of cloning an ArcGIS StoryMap. (It is not for cloning Classic StoryMap apps.)\n", 17 | "\n", 18 | "The goal of this Notebook is to support situations where a \"full\" clone of StoryMap is required. Where the person creating the StoryMap wants to keep a copy for themselves, under their full control, however, they also need to provide a copy of the StoryMap to someone else, who will then have full control over that copy.\n", 19 | "\n", 20 | "This Notebook defines \"full\" clone as cloning not only the StoryMap, but also any referenced content in the StoryMap that is owned by the same user as the StoryMap. Cloning referenced content is supported for the major content types (images, express maps, web maps, web scenes, feature layers, etc.). For unsupported content types, the reference to the orignal item wil be carried through to the cloned StoryMap. Similarly, for referenced content that is not owned by the same user, the reference to the original item will be maintained in the clone." 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "## Some Additional Notes\n", 28 | "* The code in this Notebook is designed for readability and understanding of the various steps. It is not optimized.\n", 29 | "* It is generally recommended that the user running this Notebook have the built-in Administrator role. The user running the Notebook needs to have full access to the StoryMap (and any referenced content) to create a full copy. If the user only has view access to the StoryMap, then the Notebook can only clone the published version of the StoryMap. Any unpublished edits in the draft version will not be accessible, and, therefore, not included in the cloned StoryMap. (The latter situation can occur even when running as an Administrator, if you are not an Administrator on the system from which the StoryMap is beling cloned; for example, when you are cloning a StoryMap from a public ArcGIS Online account.)\n", 30 | "* If a StoryMap is shared with a Shared Update group, then the user running this Notebook need only be a member of that group with access to edit the StoryMap for the Notebook to be able to clone any draft version of the StoryMap, in addition to the published version.\n", 31 | "* Cloning a StoryMap produces a new item, which means a new URL. For certain use cases, you may want to change the ownershp of the original StoryMap (and appropriate referenced content) to the person for whom the clone is being made, so that references to the original URL are maintained, and then clone the StoryMap back to the original owner. For example, if a student makes StoryMap on behalf of faculty member, and the faculty member has already been distributing or linking to that StoryMap's URL, then it would be best to make the faculty member the owner of the original StoryMap, and produce a clone of it for the student.\n", 32 | "* The sharing settings for the original StoryMap are not carried over to the clone. The cloned StoryMap is initially only visible to its owner." 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "# Initialization" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "import arcgis\n", 49 | "from arcgis import GIS\n", 50 | "from arcgis import mapping\n", 51 | "from arcgis import features\n", 52 | "from datetime import datetime, timezone\n", 53 | "import json\n", 54 | "from pkg_resources import parse_version\n", 55 | "\n", 56 | "# Minimum and maxium known versions of ArcGIS StoryMaps for which this script is known to work.\n", 57 | "# (Note that older versions of StoryMaps can be updated to the latest verison by re-publishing them.)\n", 58 | "minimum_storymap_version = parse_version('20.0.0')\n", 59 | "maximum_storymap_version = parse_version('20.3.0') # lastest known version, June 2020 ArcGIS StoryMap release" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": null, 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "# Check that the version of the ArcGIS API for Python in the current environment is at least 1.7.0\n", 69 | "# This Notebook may work with older versions of the API, however, it has only been confirmed to work with 1.7.0 and up. \n", 70 | "if( parse_version(arcgis.__version__) < parse_version('1.7.0') ):\n", 71 | " print(\"This Notebook is known to work in version 1.7.0 (or later) of the ArcGIS API for Python.\")\n", 72 | " print(\"You are using an earlier version of the API {0}. Proceed at your own risk...\".format(arcgis.__version__))\n", 73 | " print(\"Location:\", arcgis.__file__)\n", 74 | "else:\n", 75 | " print(\"Environment is using ArcGIS API for Python version:\", arcgis.__version__)" 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": {}, 81 | "source": [ 82 | "# Provide Information for Cloning" 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": {}, 88 | "source": [ 89 | "## StoryMap to be Cloned" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [ 98 | "# Enter the Item ID for the StoryMap to be cloned.\n", 99 | "source_storymap_itemId = 'item_id'" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "## Source GIS Connection Info\n", 107 | "\n", 108 | "Typically this is the ArcGIS Online organization that hosts the StoryMap which is being cloned, and for which the user running this Notebook is an Administrator.\n", 109 | "\n", 110 | "If the StoryMap to be cloned is hosted on an organization of which the user running the Notebook is not a member, then as long as the StoryMap and its content are shared publicly, then this Notebook will clone the published version of the StoryMap, but not any unpublished edits in the draft version of the StoryMap." 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": null, 116 | "metadata": {}, 117 | "outputs": [], 118 | "source": [ 119 | "# Define the source GIS which will be used to access the StoryMap to be cloned.\n", 120 | "# Fill in the login information for the source GIS account here, or set use_builtin to True to authenticate via ArcGIS Pro or ArcGIS Online:\n", 121 | "source_portal_url = ''\n", 122 | "source_username = ''\n", 123 | "source_password = ''\n", 124 | "source_use_builtin = False" 125 | ] 126 | }, 127 | { 128 | "cell_type": "markdown", 129 | "metadata": {}, 130 | "source": [ 131 | "## Target GIS Connection Info\n", 132 | "\n", 133 | "This is the ArcGIS Online organization to which the StoryMap is being cloned." 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": null, 139 | "metadata": {}, 140 | "outputs": [], 141 | "source": [ 142 | "# Define the target GIS (where the story map should be copied to)\n", 143 | "# Fill in the login information for the source GIS account here, or set use_builtin to True to authenticate via ArcGIS Pro or ArcGIS Online:\n", 144 | "target_portal_url = '' # URL for your ArcGIS for Student Use ArcGIS Online instance.\n", 145 | "tagert_username = ''\n", 146 | "target_password = ''\n", 147 | "target_use_builtin = False" 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "# Clone an ArcGIS StoryMap\n", 155 | "\n", 156 | "Begin the process of cloning the specified ArcGIS StoryMap." 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "metadata": {}, 162 | "source": [ 163 | "## Setup GIS Connections" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": null, 169 | "metadata": {}, 170 | "outputs": [], 171 | "source": [ 172 | "# A function for connecting to an ArcGIS Online instance.\n", 173 | "def gis_login(portal_url='', username='', password='', use_builtin=False):\n", 174 | " try:\n", 175 | " if use_builtin:\n", 176 | " # Checking home path is a good indicator for ArcGIS Online versus local Notebook server environment.\n", 177 | " homepath = %env HOMEPATH\n", 178 | " if( homepath == r'/home/arcgis' ):\n", 179 | " print( \"ArcGIS Online Notebook Server\" )\n", 180 | " gis = GIS('home')\n", 181 | " else:\n", 182 | " print( \"Local Notebook Server\" )\n", 183 | " gis = GIS('pro')\n", 184 | " else:\n", 185 | " gis = GIS(url = portal_url,username=username, password=password) \n", 186 | " print( 'Login successful.' )\n", 187 | " print( ' url: ' + gis.url)\n", 188 | " # Name is only available for organizational subscriptions, not public ArcGIS Oline accounts.\n", 189 | " if( 'name' in gis.properties.user):\n", 190 | " print( ' server: ' + gis.properties.name )\n", 191 | " print( ' user: ' + gis.properties.user.username )\n", 192 | " # Role only available for organizational subscriptions, not public ArcGIS Oline accounts.\n", 193 | " if( 'role' in gis.properties.user):\n", 194 | " print( ' role: ' + gis.properties.user.role )\n", 195 | " print( ' provider: ' + gis.properties.user.provider )\n", 196 | " except:\n", 197 | " print('Login error.')\n", 198 | " #gis = None\n", 199 | " return(gis)" 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "execution_count": null, 205 | "metadata": {}, 206 | "outputs": [], 207 | "source": [ 208 | "# Connect to source GIS instance.\n", 209 | "source_gis = gis_login(portal_url=source_portal_url, username=source_username, password=source_password, use_builtin=source_use_builtin)" 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": null, 215 | "metadata": {}, 216 | "outputs": [], 217 | "source": [ 218 | "# Connect to target GIS instance.\n", 219 | "target_gis = gis_login(portal_url=target_portal_url, username=tagert_username, password=target_password, use_builtin=target_use_builtin)" 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "## Create Tag for Cloned Items\n", 227 | "\n", 228 | "The tag can help with searching to find all the content created as a result of the cloning. Note that clone_items() will also add tags containing item IDs for some items it produces, in order to help associate them with their originals." 229 | ] 230 | }, 231 | { 232 | "cell_type": "code", 233 | "execution_count": null, 234 | "metadata": {}, 235 | "outputs": [], 236 | "source": [ 237 | "# Create a Tag to add to cloned items to help keep track of things, in case you need to undo something.\n", 238 | "# The tag incorporates the current time at which this Notebook is being run.\n", 239 | "# (While the system permits you to put a \":\" in tag, do not do so! It is the delimiter for keywords used in querying in ArcGIS onilne (e.g., query=\"tags:Demo\"), so using a \":\" inside a tag will make it impossible to search for.)\n", 240 | "process_timestamp_tag = 'StoryMap_Clone_Created_' + datetime.now(timezone.utc).strftime(\"%Y-%m-%d_%H.%M.%S.%f_UTC\")\n", 241 | "print('process_timestamp_tag:', process_timestamp_tag)" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": null, 247 | "metadata": {}, 248 | "outputs": [], 249 | "source": [ 250 | "# Check to make sure there are no items already on the target GIS using this tag.\n", 251 | "items = target_gis.content.search(\"tags:\"+process_timestamp_tag)\n", 252 | "if( len(items) > 0 ):\n", 253 | " # Content with this tag really shouldn't exist already, so if it does, then you probably should start over and run the cell above again to get a new tag.\n", 254 | " print(\"ERROR: Content with tag already exists on target GIS!\")\n", 255 | " print(items)\n", 256 | "else:\n", 257 | " print(\"No content with tag exists on target GIS.\")" 258 | ] 259 | }, 260 | { 261 | "cell_type": "markdown", 262 | "metadata": {}, 263 | "source": [ 264 | "## Retrive the StoryMap to Clone" 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": null, 270 | "metadata": { 271 | "scrolled": true 272 | }, 273 | "outputs": [], 274 | "source": [ 275 | "# Get StoryMap to clone. \n", 276 | "storymap_item = source_gis.content.get(source_storymap_itemId)\n", 277 | "storymap_item" 278 | ] 279 | }, 280 | { 281 | "cell_type": "code", 282 | "execution_count": null, 283 | "metadata": {}, 284 | "outputs": [], 285 | "source": [ 286 | "# Set source_user to owner of StoryMap; only referenced items also owned by the same user should also be cloned.\n", 287 | "source_user = storymap_item['owner']\n", 288 | "print('StoryMap owned by:', source_user)" 289 | ] 290 | }, 291 | { 292 | "cell_type": "code", 293 | "execution_count": null, 294 | "metadata": {}, 295 | "outputs": [], 296 | "source": [ 297 | "# Determine if user running script has access to unpublished edits for StoryMap, or only the published version, \n", 298 | "# in which case those edits will not be included in the clone.\n", 299 | "#\n", 300 | "# Unpublished edits are inaccessible to the user running the script when:\n", 301 | "# * user is not a member of the ArcGIS Online organization hosting the StoryMap, or\n", 302 | "# * user is from the same org, but is not an Admin, nor the owner of the StoryMap\n", 303 | "\n", 304 | "# Get user item for StoryMap owner.\n", 305 | "storymap_user = source_gis.users.get(source_user)\n", 306 | "\n", 307 | "# Display warning to user, if this script will not be able to clone unpublished edits.\n", 308 | "# Also set flag for access to those draft changes, to that thigns can be handled appropriately later on.\n", 309 | "if( \n", 310 | " ('orgId' not in storymap_user) or\n", 311 | " (source_gis.properties.user.role != 'org_admin' and source_gis.properties.user.username != source_user )\n", 312 | "):\n", 313 | " print(\"WARNING: Your account does not have access to unpublished edits in the StoryMap. If any are present, then they will not be included in the clone.\")\n", 314 | " print(\" Your account:\", source_gis.properties.user.username)\n", 315 | " draft_accessible = False\n", 316 | "else:\n", 317 | " print(\"Your account has access to unpublished edits in the StoryMap. If any are present, then will be included in the clone.\")\n", 318 | " draft_accessible = True" 319 | ] 320 | }, 321 | { 322 | "cell_type": "code", 323 | "execution_count": null, 324 | "metadata": {}, 325 | "outputs": [], 326 | "source": [ 327 | "# For reference, dispaly the StoryMap's typeKeywords\n", 328 | "print(\"StoryMap's typeKeywords\", storymap_item['typeKeywords'])" 329 | ] 330 | }, 331 | { 332 | "cell_type": "code", 333 | "execution_count": null, 334 | "metadata": {}, 335 | "outputs": [], 336 | "source": [ 337 | "# Check StoryMap's typeKeywords to ensure it fits the model this Notebook was built upon.\n", 338 | "\n", 339 | "# Check that it is an arcgis-storymaps item.\n", 340 | "if( 'arcgis-storymaps' in storymap_item['typeKeywords'] ):\n", 341 | " print(\"arcgis-storymaps typeKeyword is present.\")\n", 342 | "else:\n", 343 | " print(\"WARNING: 'arcgis-storymaps' not present in typeKeywords!\")\n", 344 | "\n", 345 | "# Check that status is one of the known typeKeywords.\n", 346 | "status = [ x for x in storymap_item['typeKeywords'] if x.startswith(('smstatusdraft','smstatuspublished','smstatusunpublishedchanges'), 0)]\n", 347 | "if( status ):\n", 348 | " print(status[0], 'is a known StoryMap status.')\n", 349 | "else:\n", 350 | " print(\"WARNING: no known StoryMap status typeKeyword is present!\")\n", 351 | " \n", 352 | "# Check that StoryMap's versions are present, and are one of the known versions.\n", 353 | "smdraftversion = [ x for x in storymap_item['typeKeywords'] if x.startswith('smversiondraft', 0)]\n", 354 | "if( smdraftversion ):\n", 355 | " version = parse_version(smdraftversion[0][15:])\n", 356 | " if( version >= minimum_storymap_version and version <= maximum_storymap_version ):\n", 357 | " print('smdraftversion:', version, 'is a tested StoryMap version.')\n", 358 | " elif( version < minimum_storymap_version ):\n", 359 | " print(\"WARNING: StoryMap has an untested smdraftversion:\", version)\n", 360 | " print(\"WARNING: It is recommended that you re-publish the StoryMap so that it is updated to the current version of ArcGIS StoryMaps.\")\n", 361 | " else:\n", 362 | " print(\"WARNING: StoryMap has an untested smdraftversion:\", version)\n", 363 | " print(\"WARNING: This version is newer then the most recent release with which this script was tested, and may work perfectly fine.\")\n", 364 | "else:\n", 365 | " print(\"WARNING: no 'smversiondraft' typeKeyword found.\")\n", 366 | "# A published version will only be presnet if StoryMap has been published.\n", 367 | "if( any(status in ['smstatuspublished','smstatusunpublishedchanges'] for status in storymap_item['typeKeywords']) ):\n", 368 | " smversionpublished = [ x for x in storymap_item['typeKeywords'] if x.startswith('smversionpublished', 0)]\n", 369 | " if( smversionpublished ):\n", 370 | " version = parse_version(smversionpublished[0][19:])\n", 371 | " if( version >= minimum_storymap_version and version <= maximum_storymap_version ):\n", 372 | " print('smversionpublished:', version, 'is a tested StoryMap version.')\n", 373 | " elif( version < minimum_storymap_version ):\n", 374 | " print(\"WARNING: StoryMap has an untested smversionpublished:\", version)\n", 375 | " print(\"WARNING: It is recommended that you re-publish the StoryMap so that it is updated to the current version of ArcGIS StoryMaps.\")\n", 376 | " else: \n", 377 | " print(\"WARNING: StoryMap has an untested smversionpublished:\", version)\n", 378 | " print(\"WARNING: This version is newer then the most recent release with which this script was tested, and may work perfectly fine.\")\n", 379 | " else:\n", 380 | " print(\"WARNING: no 'smversionpublished' typeKeyword found.\")" 381 | ] 382 | }, 383 | { 384 | "cell_type": "markdown", 385 | "metadata": {}, 386 | "source": [ 387 | "## Identify Referenced Content in StoryMap\n", 388 | "\n", 389 | "Generate a list of item IDs for the referenced content in both, if applicable, the published and draft versions of the Storymap." 390 | ] 391 | }, 392 | { 393 | "cell_type": "code", 394 | "execution_count": null, 395 | "metadata": {}, 396 | "outputs": [], 397 | "source": [ 398 | "# Retrieve the StoryMap's draft json Resource ID (or file name) from the smdraftresourceid keyword in the item's typeKeywords.\n", 399 | "smdraftresourceid = [x[18:] for x in storymap_item['typeKeywords'] if x.startswith('smdraftresourceid:',0)][0]\n", 400 | "smdraftresourceid" 401 | ] 402 | }, 403 | { 404 | "cell_type": "code", 405 | "execution_count": null, 406 | "metadata": {}, 407 | "outputs": [], 408 | "source": [ 409 | "# Function for generating a list of itemIds for referenced resources for supported types (i.e., web maps, web scenes) in a StoryMap\n", 410 | "def get_resource_itemIds(resources):\n", 411 | " wm_itemIds = []\n", 412 | " for key, val in resources.items():\n", 413 | " print()\n", 414 | " if( val['type'] == 'webmap' ):\n", 415 | " \n", 416 | " # Obtain referenced item.\n", 417 | " itemId = val['data']['itemId']\n", 418 | " items = source_gis.content.search('id:' + itemId, outside_org = True)\n", 419 | " \n", 420 | " # If item is accessible to user running Notebook, then process the item referenced.\n", 421 | " if( len(items) == 1 ):\n", 422 | " item = source_gis.content.get(itemId)\n", 423 | " # Only track itemIds for swizzling for webmap resources owned by the same user (i.e., source_user.)\n", 424 | " if(item['owner'] == source_user):\n", 425 | " print( item['id'], val['type'] )\n", 426 | " print( item['type'], item['typeKeywords'] )\n", 427 | " # Is webmap item a Web Map?\n", 428 | " if( 'Web Map' in item['typeKeywords'] ):\n", 429 | " print( \">>>> Adding Web Map to wm_itemIDs map\" )\n", 430 | " wm_itemIds.append(item['id'])\n", 431 | " # Is webmap item a Web Scene?\n", 432 | " elif( 'Web Scene' in item['typeKeywords'] ):\n", 433 | " print( \">>>> Adding Web Scene to wm_itemIDs map\" )\n", 434 | " wm_itemIds.append(item['id'])\n", 435 | " # Else is an unsupported webmap type... what is it?\n", 436 | " else:\n", 437 | " print( \">>>> WARNING: Unsupported webmap type\" )\n", 438 | " else:\n", 439 | " print( item['id'], item['owner'])\n", 440 | " print(\">>>> Not owned by source_user\")\n", 441 | " \n", 442 | " elif( len(items) == 0 ):\n", 443 | " # User running script is not able to accessed the referenced item, so return an error, as clone will not be complete.\n", 444 | " print(\"ERROR: Referenced item is not accessible, so cannot include in clone. ID =\", itemId)\n", 445 | " \n", 446 | " else:\n", 447 | " print(\"ERROR: unexpected number of results returned from search\", len(items))\n", 448 | " \n", 449 | " elif( val['type'] == 'expressmap' or val['type'] == 'image' or val['type'] == 'story-theme'):\n", 450 | " # These \"built-in\" types are cloned along with the StoryMap, so no itemId to track for them.\n", 451 | " print(val['type'])\n", 452 | " print(\">>>> Inclusive resource type, no itemId to track\")\n", 453 | " else:\n", 454 | " # Any type we haven't yet dealt with...\n", 455 | " print(val['type'])\n", 456 | " print(\">>>> WARNING: 'type' is not supported\")\n", 457 | " return(wm_itemIds)" 458 | ] 459 | }, 460 | { 461 | "cell_type": "code", 462 | "execution_count": null, 463 | "metadata": {}, 464 | "outputs": [], 465 | "source": [ 466 | "# Get list of resource itemIds in draft JSON, if draft is accessible by user running Notebook.\n", 467 | "print(\"Draft JSON itemIds:\")\n", 468 | "if( draft_accessible ):\n", 469 | " wm_itemIds = get_resource_itemIds(storymap_item.resources.get(smdraftresourceid)['resources'])\n", 470 | "else:\n", 471 | " print(\"You do not have access to StoryMap's unpublished edits. They will not be cloned.\")\n", 472 | " wm_itemIds = [] " 473 | ] 474 | }, 475 | { 476 | "cell_type": "code", 477 | "execution_count": null, 478 | "metadata": {}, 479 | "outputs": [], 480 | "source": [ 481 | "# If StoryMap is published, then add to list the list of resource itemIds in published JSON.\n", 482 | "if( any(status in ['smstatuspublished','smstatusunpublishedchanges'] for status in storymap_item['typeKeywords']) ):\n", 483 | " print(\"\\nPublished JSON itemIds:\")\n", 484 | " wm_itemIds.extend(get_resource_itemIds(storymap_item.get_data()['resources']))\n", 485 | "else:\n", 486 | " print(\"StoryMap is not published.\")" 487 | ] 488 | }, 489 | { 490 | "cell_type": "code", 491 | "execution_count": null, 492 | "metadata": {}, 493 | "outputs": [], 494 | "source": [ 495 | "# Show complete list of webmap itemIds. \n", 496 | "print(\"\\nComplete list of webmap itemIds:\")\n", 497 | "print(wm_itemIds)\n", 498 | "\n", 499 | "# Reduce list to unique set of webmap itemIds.\n", 500 | "wm_itemIds = list(set(wm_itemIds))\n", 501 | "\n", 502 | "# Show unique list of itemIds.\n", 503 | "print(\"\\nUnique list of webmap itemIds:\")\n", 504 | "print(wm_itemIds)" 505 | ] 506 | }, 507 | { 508 | "cell_type": "markdown", 509 | "metadata": {}, 510 | "source": [ 511 | "## Retrieve Referenced Feature Layers\n", 512 | "\n", 513 | "Get a list of feature layers, which are referenced by Web Maps and Web Scenes referenced in the Story Map." 514 | ] 515 | }, 516 | { 517 | "cell_type": "code", 518 | "execution_count": null, 519 | "metadata": {}, 520 | "outputs": [], 521 | "source": [ 522 | "# Function to extract feature layer itemIds owned by source user from layers in Web Maps and Web Scenes.\n", 523 | "def extract_fl_itemIds( layers ):\n", 524 | " for layer in layers:\n", 525 | " print()\n", 526 | " print(\"layer:\", layer['id'], layer['layerType'])\n", 527 | " # Is layer an \"ArcGISFeatureLayer\" and has an itemId?\n", 528 | " if( layer['layerType'] == 'ArcGISFeatureLayer' and 'itemId' in layer.keys() ):\n", 529 | " fl_item = source_gis.content.get(layer['itemId'])\n", 530 | " # Is feature layer owned by source_user\n", 531 | " if( fl_item['owner'] == source_user ):\n", 532 | " print(\"layer itemId:\", layer['itemId'])\n", 533 | " fl_itemIds_to_clone.append(layer['itemId'])\n", 534 | " print(\">>>> CLONE\")\n", 535 | " else:\n", 536 | " print(\"layer itemId:\", layer['itemId'])\n", 537 | " fl_itemIds_do_not_clone.append(layer['itemId'])\n", 538 | " print(\">>>> PASS AS-IS: Not owned by source_user.\")\n", 539 | " # Is layer a \"GroupLayer\"?\n", 540 | " elif( layer['layerType'] == 'GroupLayer' ):\n", 541 | " print(\"Group Layer:\", layer['title'] )\n", 542 | " extract_fl_itemIds(layer['layers'])\n", 543 | " else:\n", 544 | " print(\">>>> IGNORE: Not an ArcGISFeatureLayer with an itemId.\")" 545 | ] 546 | }, 547 | { 548 | "cell_type": "code", 549 | "execution_count": null, 550 | "metadata": {}, 551 | "outputs": [], 552 | "source": [ 553 | "# Get list of Feature Layer itemIds in Web Maps for Feature Layers that are ArcGISFeatureLayer, have an itemId,\n", 554 | "# and are owned by source_user. These are the Feature Layers which need to be cloned.\n", 555 | "#\n", 556 | "# Also get a list of Feature Layer itemIds in Web Maps for ones that do not need to be cloned, so that these can be \n", 557 | "# passed through as-is; the cloned StoryMap will point the same item in this case as the original StoryMap.\n", 558 | "#\n", 559 | "# Goal is to avoid cloning unnecessary duplicates of feature layers referenced by more than one map or xcene in Story, \n", 560 | "# and to avoid clonging feature layers not owned by the source_user (not their's to clone).\n", 561 | "\n", 562 | "fl_itemIds_to_clone = []\n", 563 | "fl_itemIds_do_not_clone = []\n", 564 | "\n", 565 | "for itemId in wm_itemIds:\n", 566 | " item = source_gis.content.get(itemId)\n", 567 | " print(\"\\nWorking on item:\")\n", 568 | " print(item['id'], '\"'+item['title']+'\"', item['type'], item['owner'])\n", 569 | " print(\"type:\", item['type'])\n", 570 | " print(\"typeKeywords:\", item['typeKeywords'])\n", 571 | " \n", 572 | " # Is item a Web Map?\n", 573 | " if( item['type'] == 'Web Map' ):\n", 574 | " wm = mapping.WebMap(item)\n", 575 | " extract_fl_itemIds(wm.layers)\n", 576 | " # Is item a Web Scene?\n", 577 | " elif( item['type'] == 'Web Scene' ):\n", 578 | " ws = mapping.WebScene(item)\n", 579 | " extract_fl_itemIds(ws['operationalLayers'])\n", 580 | " # Something other than a Web Map or Web Scene, so ignore it.\n", 581 | " else:\n", 582 | " print(\" >>>> IGNORE: Not a Web Map.\")" 583 | ] 584 | }, 585 | { 586 | "cell_type": "code", 587 | "execution_count": null, 588 | "metadata": {}, 589 | "outputs": [], 590 | "source": [ 591 | "# Reduce lists to unique set of itemIds.\n", 592 | "fl_itemIds_to_clone = list(set(fl_itemIds_to_clone))\n", 593 | "fl_itemIds_do_not_clone = list(set(fl_itemIds_do_not_clone))\n", 594 | "\n", 595 | "print(\"Feature Layers that will be cloned:\")\n", 596 | "for itemId in fl_itemIds_to_clone:\n", 597 | " print(itemId)\n", 598 | " \n", 599 | "print(\"\\nFeature Layers that will NOT be cloned:\")\n", 600 | "for itemId in fl_itemIds_do_not_clone:\n", 601 | " print(itemId)" 602 | ] 603 | }, 604 | { 605 | "cell_type": "markdown", 606 | "metadata": {}, 607 | "source": [ 608 | "## Clone Feature Layers\n", 609 | "\n", 610 | "Start the cloning process by first cloning the appropriate feature layers referenced by Web Maps and Web Scenes referenced in the StoryMap." 611 | ] 612 | }, 613 | { 614 | "cell_type": "code", 615 | "execution_count": null, 616 | "metadata": {}, 617 | "outputs": [], 618 | "source": [ 619 | "# Clone Feature Layers and produce map of original to cloned itemIds.\n", 620 | "\n", 621 | "fl_itemId_map = {}\n", 622 | "\n", 623 | "print(\"\\nOriginal itemId Cloned itemId\")\n", 624 | "\n", 625 | "for fl_itemId in fl_itemIds_to_clone:\n", 626 | " cloned_items = target_gis.content.clone_items(\n", 627 | " items = [source_gis.content.get(fl_itemId)],\n", 628 | " search_existing_items = False\n", 629 | " )\n", 630 | " \n", 631 | " # Expecting only one cloned item when cloning the feature layer\n", 632 | " if( len(cloned_items) != 1 ):\n", 633 | " print(\">>>> WARNING: Unexpected number of clones produced!\", len(cloned_items))\n", 634 | " for item in cloned_items:\n", 635 | " print(item['id'], '\"'+item['title']+'\"', item['type'], item['owner'])\n", 636 | " \n", 637 | " # Assume there is one cloned item and it is the feature layer\n", 638 | " cloned_item = cloned_items[0]\n", 639 | " print(fl_itemId, cloned_item['id'], '\"'+cloned_item['title']+'\"', cloned_item['type'], cloned_item['owner'])\n", 640 | " \n", 641 | " # Add the Tag from above to the cloned items to help keep track of things.\n", 642 | " cloned_item['tags'].append(process_timestamp_tag)\n", 643 | " result = cloned_item.update(\n", 644 | " item_properties = {\n", 645 | " 'tags': cloned_item['tags']\n", 646 | " }\n", 647 | " )\n", 648 | " \n", 649 | " # Add source/original and target/cloned feature layer's itemId to itemId map.\n", 650 | " fl_itemId_map.update({fl_itemId:cloned_item['id']})" 651 | ] 652 | }, 653 | { 654 | "cell_type": "code", 655 | "execution_count": null, 656 | "metadata": {}, 657 | "outputs": [], 658 | "source": [ 659 | "# Add mapping of non-cloned feature layers to point to themselves, so the references to the originals \n", 660 | "# will be preserved in the cloned StoryMap.\n", 661 | "for fl_itemId in fl_itemIds_do_not_clone:\n", 662 | " fl_itemId_map.update({fl_itemId:fl_itemId})\n", 663 | "\n", 664 | "# Show the whole feature layer itemId_map dictionary.\n", 665 | "print(\"\\nOriginal itemId Cloned itemId\")\n", 666 | "for k,v in fl_itemId_map.items():\n", 667 | " item = source_gis.content.get(k)\n", 668 | " print(k, v, '\"'+item['title']+'\"', item['type'], item['owner'])" 669 | ] 670 | }, 671 | { 672 | "cell_type": "markdown", 673 | "metadata": {}, 674 | "source": [ 675 | "## Clone Resources (e.g., Web Maps, Web Scenes)\n", 676 | "\n", 677 | "The next step in the cloning process is to clone the appropriate resources referenced in the StoryMap." 678 | ] 679 | }, 680 | { 681 | "cell_type": "code", 682 | "execution_count": null, 683 | "metadata": {}, 684 | "outputs": [], 685 | "source": [ 686 | "# Clone resources (i.e., Web Maps, Web Scenes) and generate a map of source itemIds to cloned itemIds.\n", 687 | "# When cloning web maps, use the feature layer itemId map created above with the item_mapping parameter.\n", 688 | "# At this point the list of resources to clone consists of only supported resource types (e.g, webmaps), as it was filtered above.\n", 689 | "#\n", 690 | "# There should only be one item returned by clone_items for each resource cloned..\n", 691 | "\n", 692 | "wm_itemId_map = {}\n", 693 | "\n", 694 | "print(\"\\nOriginal itemId Cloned itemId\")\n", 695 | "\n", 696 | "for wm_itemId in wm_itemIds:\n", 697 | " cloned_items = target_gis.content.clone_items(\n", 698 | " items = [source_gis.content.get(wm_itemId)],\n", 699 | " item_mapping = fl_itemId_map,\n", 700 | " search_existing_items = False\n", 701 | " )\n", 702 | " \n", 703 | " # Expecting only one cloned item when cloning a Web Map or Web Scene.\n", 704 | " if( len(cloned_items) != 1 ):\n", 705 | " if( len(cloned_items) == 0 ):\n", 706 | " print(\">>>> WARNING: No clones produced!\")\n", 707 | " else:\n", 708 | " print(\">>>> WARNING: Unexpected number (>1) of clones produced!\", len(cloned_items))\n", 709 | " for item in cloned_items:\n", 710 | " print(item['id'], '\"'+item['title']+'\"', item['type'], item['owner'])\n", 711 | " \n", 712 | " # Assume there is one cloned item and it is the cloned web map.\n", 713 | " cloned_item = cloned_items[0]\n", 714 | " print()\n", 715 | " print(wm_itemId, cloned_item['id'], '\"'+cloned_item['title']+'\"', cloned_item['type'], cloned_item['owner'])\n", 716 | " \n", 717 | " # Add the Tag from above to the cloned items to help keep track of things.\n", 718 | " cloned_item['tags'].append(process_timestamp_tag)\n", 719 | " result = cloned_item.update(\n", 720 | " item_properties = {\n", 721 | " 'tags': cloned_item['tags']\n", 722 | " }\n", 723 | " )\n", 724 | " \n", 725 | " # Add source/original and target/cloned webmaps' itemIds to itemId_map dictionary.\n", 726 | " wm_itemId_map.update({wm_itemId:cloned_item['id']})\n", 727 | " \n", 728 | " # If webmap is a Web Scene, then addtional steps are required to complete its clone.\n", 729 | " if( cloned_item['type'] == 'Web Scene' ):\n", 730 | " print(\"\\nSwizzling layers of Web Scene:\", wm_itemId, cloned_item['id'])\n", 731 | " \n", 732 | " # Get json data of original Web Scene\n", 733 | " data = source_gis.content.get(wm_itemId).get_data()\n", 734 | " \n", 735 | " # Swizzle feature Layer itemIDs as needed.\n", 736 | " for layer in data['operationalLayers']:\n", 737 | " if( layer['layerType'] == 'ArcGISFeatureLayer' and 'itemId' in layer.keys() ):\n", 738 | " print(\"\\nArcGISFeatureLayer w/ itemId:\", layer['title'])\n", 739 | " # Update both the layer's URL and itemId\n", 740 | " original_itemId = layer['itemId']\n", 741 | " original_url = layer['url']\n", 742 | "\n", 743 | " layer['url'] = target_gis.content.get(fl_itemId_map[layer['itemId']])['url'] + '/' + layer['url'].rpartition('/')[2]\n", 744 | "\n", 745 | " layer['itemId'] = fl_itemId_map[layer['itemId']]\n", 746 | "\n", 747 | " print(\"Mapped:\", original_itemId, \"to\", layer['itemId'])\n", 748 | " print(\"Mapped:\", original_url, \"to\", layer['url'])\n", 749 | "\n", 750 | " elif( layer['layerType'] == 'GroupLayer:', layer['title'] ):\n", 751 | " print(\"\\nGroupLayer\")\n", 752 | " for l in layer['layers']:\n", 753 | " if( l['layerType'] == 'ArcGISFeatureLayer' and 'itemId' in l.keys() ):\n", 754 | " print(\"\\nGroup - ArcGISFeatureLayer w/ itemId:\", l['title'])\n", 755 | " # Update both the layer's URL and itemId\n", 756 | " original_itemId = l['itemId']\n", 757 | " original_url = l['url']\n", 758 | "\n", 759 | " l['url'] = target_gis.content.get(fl_itemId_map[l['itemId']])['url'] + '/' + l['url'].rpartition('/')[2]\n", 760 | "\n", 761 | " l['itemId'] = fl_itemId_map[l['itemId']]\n", 762 | "\n", 763 | " print(\"Mapped:\", original_itemId, \"to\", l['itemId'])\n", 764 | " print(\"Mapped:\", original_url, \"to\", l['url'])\n", 765 | " \n", 766 | " # Update json data of cloned Web Scene.\n", 767 | " result = cloned_item.update(data = data)\n", 768 | " print(\"Updated Web Scene data:\", result)" 769 | ] 770 | }, 771 | { 772 | "cell_type": "markdown", 773 | "metadata": {}, 774 | "source": [ 775 | "## Clone StoryMap\n", 776 | "\n", 777 | "The last thing that needs to be cloned is the StoryMap itself." 778 | ] 779 | }, 780 | { 781 | "cell_type": "code", 782 | "execution_count": null, 783 | "metadata": { 784 | "scrolled": true 785 | }, 786 | "outputs": [], 787 | "source": [ 788 | "# Clone the ArcGIS StoryMap from the source to the target\n", 789 | "clones = target_gis.content.clone_items(\n", 790 | " items = [storymap_item],\n", 791 | " search_existing_items = False\n", 792 | ")\n", 793 | "# Cloned StoryMap should be the first and only item returned.\n", 794 | "if( len(cloned_items) != 1 ):\n", 795 | " if( len(cloned_items) == 0 ):\n", 796 | " print(\">>>> ERROR: No StoryMap clone produced!\")\n", 797 | " else:\n", 798 | " print(\">>>> ERROR: Unexpected number (>1) of clone items produced while cloning StoryMap!\", len(cloned_items))\n", 799 | " for item in cloned_items:\n", 800 | " print(item['id'], '\"'+item['title']+'\"', item['type'], item['owner'])\n", 801 | " \n", 802 | "storymap_clone = clones[0]\n", 803 | "storymap_clone" 804 | ] 805 | }, 806 | { 807 | "cell_type": "code", 808 | "execution_count": null, 809 | "metadata": {}, 810 | "outputs": [], 811 | "source": [ 812 | "# Add the Tag from above to the cloned StoryMap to help keep track of things.\n", 813 | "storymap_clone['tags'].append(process_timestamp_tag)\n", 814 | "update_result = storymap_clone.update(\n", 815 | " item_properties = {\n", 816 | " #'title': '[Clone] ' + storymap_clone['title'],\n", 817 | " 'tags': storymap_clone['tags']\n", 818 | " }\n", 819 | ")\n", 820 | "print(\"Update results:\", update_result)" 821 | ] 822 | }, 823 | { 824 | "cell_type": "code", 825 | "execution_count": null, 826 | "metadata": {}, 827 | "outputs": [], 828 | "source": [ 829 | "# Update clone's url property to point at itself (clone_items initially leaves it pointing to the original StoryMap.)\n", 830 | "print(\"Original url:\", storymap_clone.url)\n", 831 | "result = storymap_clone.update(\n", 832 | " item_properties = {'url': 'https://storymaps.arcgis.com/stories/' + storymap_clone.id}\n", 833 | ")\n", 834 | "print(\"Update result:\", result)\n", 835 | "print(\"Updated url: \", storymap_clone.url)" 836 | ] 837 | }, 838 | { 839 | "cell_type": "markdown", 840 | "metadata": {}, 841 | "source": [ 842 | "## Update Cloned StoryMap's JSON with mapped itemIds\n", 843 | "\n", 844 | "Swizzle the references in the Cloned StoryMap to point at the cloned Feature Layers, Web Maps, Web Scences, etc., rather than the originals." 845 | ] 846 | }, 847 | { 848 | "cell_type": "code", 849 | "execution_count": null, 850 | "metadata": {}, 851 | "outputs": [], 852 | "source": [ 853 | "# Get draft JSON resource ID or file name.\n", 854 | "cloned_smdraftresourceid = [x[18:] for x in storymap_clone['typeKeywords'] if x.startswith('smdraftresourceid:',0)][0]\n", 855 | "cloned_smdraftresourceid" 856 | ] 857 | }, 858 | { 859 | "cell_type": "code", 860 | "execution_count": null, 861 | "metadata": {}, 862 | "outputs": [], 863 | "source": [ 864 | "# Function to replace itemIds in resource section of json using itemIds_map.\n", 865 | "def replace_cloned_itemIds(data):\n", 866 | " for key, val in data['resources'].items():\n", 867 | " if( val['type'] == 'webmap' ):\n", 868 | " # Check to see if webmap's itemId is one that was cloned\n", 869 | " if( [k for k,v in wm_itemId_map.items() if k == val['data']['itemId']] ):\n", 870 | " # Replace itemId with cloned itemId.\n", 871 | " print(\"match\", val['data']['itemId'], wm_itemId_map[val['data']['itemId']])\n", 872 | " val['data']['itemId'] = wm_itemId_map[val['data']['itemId']]\n", 873 | " else:\n", 874 | " print(\"no match\", val['data']['itemId'])" 875 | ] 876 | }, 877 | { 878 | "cell_type": "code", 879 | "execution_count": null, 880 | "metadata": {}, 881 | "outputs": [], 882 | "source": [ 883 | "# Function to replace resource references (e.g., r-) in json using itemIds_map.\n", 884 | "def recursive_replace_reference(data, old, new):\n", 885 | " for k,v in data.items():\n", 886 | " if k == old:\n", 887 | " data[new] = data.pop(old)\n", 888 | " if v == old:\n", 889 | " data[k] = new\n", 890 | " elif isinstance(v,dict):\n", 891 | " recursive_replace_reference(v, old, new)" 892 | ] 893 | }, 894 | { 895 | "cell_type": "code", 896 | "execution_count": null, 897 | "metadata": {}, 898 | "outputs": [], 899 | "source": [ 900 | "# If StoryMap is published, then update published JSON.\n", 901 | "if( any(status in ['smstatuspublished','smstatusunpublishedchanges'] for status in storymap_clone['typeKeywords']) ):\n", 902 | " \n", 903 | " # Get the current published json.\n", 904 | " clone_data_published = storymap_item.get_data()\n", 905 | " \n", 906 | " # Replace the source itemIds with the mapped target itemIds in the resource section of published json.\n", 907 | " print(\"Updating itemIds\")\n", 908 | " replace_cloned_itemIds(clone_data_published)\n", 909 | " \n", 910 | " # Replace the itemIDs in the resource references.\n", 911 | " print(\"\\nUpdating resource references\")\n", 912 | " for k,v in wm_itemId_map.items():\n", 913 | " old = 'r-' + k\n", 914 | " new = 'r-' + v\n", 915 | " print(\"Replacing {0} with {1}\".format(old, new))\n", 916 | " recursive_replace_reference(clone_data_published, old, new)\n", 917 | " \n", 918 | " # Update the cloned StoryMap's published json with the updated version.\n", 919 | " result = storymap_clone.update(\n", 920 | " data = clone_data_published\n", 921 | " )\n", 922 | " print(\"\\nUpdating published data:\", result)" 923 | ] 924 | }, 925 | { 926 | "cell_type": "code", 927 | "execution_count": null, 928 | "metadata": {}, 929 | "outputs": [], 930 | "source": [ 931 | "# If the draft version of the StoryMap was accessible, then also update the clone's draft json.\n", 932 | "if( draft_accessible ):\n", 933 | " \n", 934 | " # Get the current draft json resource.\n", 935 | " wm_itemIds = get_resource_itemIds(storymap_item.resources.get(smdraftresourceid)['resources'])\n", 936 | " clone_data_draft = storymap_clone.resources.get(cloned_smdraftresourceid)\n", 937 | "\n", 938 | " # Replace the source itemIds with the mapped target itemIds in the resource section of draft json.\n", 939 | " print(\"Updating itemIds\")\n", 940 | " replace_cloned_itemIds(clone_data_draft)\n", 941 | "\n", 942 | " # Replace the itemIDs in the resource references.\n", 943 | " print(\"\\nUpdating resource references\")\n", 944 | " for k,v in wm_itemId_map.items():\n", 945 | " old = 'r-' + k\n", 946 | " new = 'r-' + v\n", 947 | " print(\"Replacing {0} with {1}\".format(old, new))\n", 948 | " recursive_replace_reference(clone_data_draft, old, new)\n", 949 | "\n", 950 | " # Remove current draft json resource.\n", 951 | " result = storymap_clone.resources.remove(cloned_smdraftresourceid)\n", 952 | " print(\"\\nRemoving draft data:\", result)\n", 953 | "else:\n", 954 | " print(\"You do not have access to StoryMap's unpublished edits. Published JSON will be substituted for Draft JSON in cloned StoryMap.\") \n", 955 | " clone_data_draft = clone_data_published\n", 956 | "\n", 957 | "# Add updated draft json resource back using same name.\n", 958 | "result = storymap_clone.resources.add(\n", 959 | " file_name = cloned_smdraftresourceid,\n", 960 | " text = json.dumps(clone_data_draft)\n", 961 | ")\n", 962 | "print(\"\\nAdding draft data:\", result)" 963 | ] 964 | }, 965 | { 966 | "cell_type": "markdown", 967 | "metadata": {}, 968 | "source": [ 969 | "# Examine Cloned StoryMap\n", 970 | "\n", 971 | "Done! View the final product." 972 | ] 973 | }, 974 | { 975 | "cell_type": "code", 976 | "execution_count": null, 977 | "metadata": {}, 978 | "outputs": [], 979 | "source": [ 980 | "# Check out the Cloned Story to see if everything is okay.\n", 981 | "storymap_clone" 982 | ] 983 | }, 984 | { 985 | "cell_type": "markdown", 986 | "metadata": {}, 987 | "source": [ 988 | "# Stop Here!\n", 989 | "The content below is not part of the StoryMap cloning workflow, but can be helpful if troubleshooting is required." 990 | ] 991 | }, 992 | { 993 | "cell_type": "code", 994 | "execution_count": null, 995 | "metadata": {}, 996 | "outputs": [], 997 | "source": [ 998 | "# Throw an exeception to prevent Run All Cells from going past this point. \n", 999 | "# (You don't want accidentally run the cells that would delete all the cloned content you just created!)\n", 1000 | "raise SystemExit(\"Stop!\")" 1001 | ] 1002 | }, 1003 | { 1004 | "cell_type": "markdown", 1005 | "metadata": {}, 1006 | "source": [ 1007 | "## Display StoryMap JSON\n", 1008 | "Print out the JSON data for the StoryMap, so that you can cut and paste it into a JSON viewer to make it easier to understand and navigate the hierarchy." 1009 | ] 1010 | }, 1011 | { 1012 | "cell_type": "code", 1013 | "execution_count": null, 1014 | "metadata": { 1015 | "scrolled": true 1016 | }, 1017 | "outputs": [], 1018 | "source": [ 1019 | "print(json.dumps(storymap_item.resources.get(smdraftresourceid), indent=4))" 1020 | ] 1021 | }, 1022 | { 1023 | "cell_type": "code", 1024 | "execution_count": null, 1025 | "metadata": { 1026 | "scrolled": true 1027 | }, 1028 | "outputs": [], 1029 | "source": [ 1030 | "print(json.dumps(storymap_item.get_data(), indent=4))" 1031 | ] 1032 | }, 1033 | { 1034 | "cell_type": "code", 1035 | "execution_count": null, 1036 | "metadata": { 1037 | "scrolled": true 1038 | }, 1039 | "outputs": [], 1040 | "source": [ 1041 | "print(json.dumps(storymap_clone.resources.get(cloned_smdraftresourceid), indent=4))" 1042 | ] 1043 | }, 1044 | { 1045 | "cell_type": "code", 1046 | "execution_count": null, 1047 | "metadata": { 1048 | "scrolled": true 1049 | }, 1050 | "outputs": [], 1051 | "source": [ 1052 | "print(json.dumps(storymap_clone.get_data(), indent=4))" 1053 | ] 1054 | }, 1055 | { 1056 | "cell_type": "markdown", 1057 | "metadata": {}, 1058 | "source": [ 1059 | "## Clean-up: Delete all the Cloned Content" 1060 | ] 1061 | }, 1062 | { 1063 | "cell_type": "code", 1064 | "execution_count": null, 1065 | "metadata": {}, 1066 | "outputs": [], 1067 | "source": [ 1068 | "# Delete all the cloned content; identify it using the Tag assigned above.\n", 1069 | "\n", 1070 | "# Get list of items with Tag.\n", 1071 | "print(\"Items to delete:\")\n", 1072 | "items = target_gis.content.search('tags:' + process_timestamp_tag)\n", 1073 | "for item in items:\n", 1074 | " print(item)\n", 1075 | " \n", 1076 | "# Delete the list of items.\n", 1077 | "print(\"\\nDeleting\")\n", 1078 | "result = target_gis.content.delete_items(items)\n", 1079 | "print(result)" 1080 | ] 1081 | }, 1082 | { 1083 | "cell_type": "markdown", 1084 | "metadata": {}, 1085 | "source": [ 1086 | "## Inspect an ArcGIS StoryMap's Properties" 1087 | ] 1088 | }, 1089 | { 1090 | "cell_type": "code", 1091 | "execution_count": null, 1092 | "metadata": {}, 1093 | "outputs": [], 1094 | "source": [ 1095 | "storymap = source_gis.content.get('8607ff45996d4832894203d377ff3953')\n", 1096 | "storymap" 1097 | ] 1098 | }, 1099 | { 1100 | "cell_type": "code", 1101 | "execution_count": null, 1102 | "metadata": {}, 1103 | "outputs": [], 1104 | "source": [ 1105 | "for k,v in storymap.items():\n", 1106 | " print(k,v)" 1107 | ] 1108 | } 1109 | ], 1110 | "metadata": { 1111 | "kernelspec": { 1112 | "display_name": "Python 3", 1113 | "language": "python", 1114 | "name": "python3" 1115 | }, 1116 | "language_info": { 1117 | "codemirror_mode": { 1118 | "name": "ipython", 1119 | "version": 3 1120 | }, 1121 | "file_extension": ".py", 1122 | "mimetype": "text/x-python", 1123 | "name": "python", 1124 | "nbconvert_exporter": "python", 1125 | "pygments_lexer": "ipython3", 1126 | "version": "3.6.9" 1127 | } 1128 | }, 1129 | "nbformat": 4, 1130 | "nbformat_minor": 4 1131 | } 1132 | -------------------------------------------------------------------------------- /Samples/Copy Organization Categories.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### Establish your GIS Connections" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import arcgis\n", 17 | "src_gis = arcgis.gis.GIS(\"https://org1.maps.arcgis.com\",\"user1\")\n", 18 | "print(src_gis._username, \"@\", src_gis)\n", 19 | "target_gis = arcgis.gis.GIS(\"https://org2.maps.arcgis.com\", \"user2\")\n", 20 | "print(target_gis._username, \"@\", target_gis)" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "#### Display the current categories for your organizations" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 4, 33 | "metadata": {}, 34 | "outputs": [ 35 | { 36 | "name": "stdout", 37 | "output_type": "stream", 38 | "text": [ 39 | "Source Categories:\n", 40 | "[{'categories': [{'categories': [{'categories': [],\n", 41 | " 'title': 'New and noteworthy'},\n", 42 | " {'categories': [], 'title': 'Current events'}],\n", 43 | " 'title': 'Trending-2'},\n", 44 | " {'categories': [{'categories': [], 'title': 'Reference maps'},\n", 45 | " {'categories': [], 'title': 'Creative maps'},\n", 46 | " {'categories': [], 'title': 'Vector tiles'},\n", 47 | " {'categories': [],\n", 48 | " 'title': 'Component layers'},\n", 49 | " {'categories': [],\n", 50 | " 'title': 'Historical maps'}],\n", 51 | " 'title': 'Basemaps'},\n", 52 | " {'categories': [{'categories': [], 'title': 'Basemap imagery'},\n", 53 | " {'categories': [],\n", 54 | " 'title': 'Multispectral imagery'},\n", 55 | " {'categories': [],\n", 56 | " 'title': 'Temporal imagery'},\n", 57 | " {'categories': [], 'title': 'Event imagery'}],\n", 58 | " 'title': 'Imagery'},\n", 59 | " {'categories': [{'categories': [], 'title': 'Administrative'},\n", 60 | " {'categories': [], 'title': 'Environmental'},\n", 61 | " {'categories': [], 'title': 'Geometric'}],\n", 62 | " 'title': 'Boundaries'},\n", 63 | " {'categories': [{'categories': [], 'title': 'Population'},\n", 64 | " {'categories': [], 'title': 'Housing'},\n", 65 | " {'categories': [], 'title': 'Neighborhoods'},\n", 66 | " {'categories': [], 'title': 'Jobs'},\n", 67 | " {'categories': [], 'title': 'Income'},\n", 68 | " {'categories': [], 'title': 'Spending'},\n", 69 | " {'categories': [], 'title': 'Health'},\n", 70 | " {'categories': [], 'title': 'Education'},\n", 71 | " {'categories': [], 'title': 'At risk'},\n", 72 | " {'categories': [], 'title': 'Public safety'}],\n", 73 | " 'title': 'People'},\n", 74 | " {'categories': [{'categories': [], 'title': 'Transportation'},\n", 75 | " {'categories': [], 'title': 'Traffic'},\n", 76 | " {'categories': [], 'title': 'Structures'},\n", 77 | " {'categories': [], 'title': 'Utilities'},\n", 78 | " {'categories': [], 'title': 'Businesses'},\n", 79 | " {'categories': [], 'title': 'Agriculture'}],\n", 80 | " 'title': 'Infrastructure'},\n", 81 | " {'categories': [{'categories': [],\n", 82 | " 'title': 'Earth observations'},\n", 83 | " {'categories': [], 'title': 'Oceans'},\n", 84 | " {'categories': [],\n", 85 | " 'title': 'Elevation and bathymetry'},\n", 86 | " {'categories': [],\n", 87 | " 'title': 'Weather and climate'},\n", 88 | " {'categories': [], 'title': 'Land cover'},\n", 89 | " {'categories': [],\n", 90 | " 'title': 'Energy resources'},\n", 91 | " {'categories': [],\n", 92 | " 'title': 'Soils and geology'},\n", 93 | " {'categories': [], 'title': 'Fresh water'},\n", 94 | " {'categories': [], 'title': 'Habitat'},\n", 95 | " {'categories': [], 'title': 'Species'}],\n", 96 | " 'title': 'Environment'}],\n", 97 | " 'title': 'Categories'}]\n" 98 | ] 99 | } 100 | ], 101 | "source": [ 102 | "src_categories = src_gis.content.categories.schema\n", 103 | "print (\"Source Categories:\")\n", 104 | "import pprint\n", 105 | "pprint.pprint(src_categories)" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "metadata": {}, 111 | "source": [ 112 | "#### Now, apply your Source Organization's Categories to your Target Organization. We will preserve the Original Target Categories if needed in this notebook." 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": 8, 118 | "metadata": {}, 119 | "outputs": [ 120 | { 121 | "name": "stdout", 122 | "output_type": "stream", 123 | "text": [ 124 | "New Categories:\n", 125 | "~~~~~~~~~~~~~\n", 126 | "[{'categories': [{'categories': [{'categories': [],\n", 127 | " 'title': 'New and noteworthy'},\n", 128 | " {'categories': [], 'title': 'Current events'}],\n", 129 | " 'title': 'Trending-2'},\n", 130 | " {'categories': [{'categories': [], 'title': 'Reference maps'},\n", 131 | " {'categories': [], 'title': 'Creative maps'},\n", 132 | " {'categories': [], 'title': 'Vector tiles'},\n", 133 | " {'categories': [],\n", 134 | " 'title': 'Component layers'},\n", 135 | " {'categories': [],\n", 136 | " 'title': 'Historical maps'}],\n", 137 | " 'title': 'Basemaps'},\n", 138 | " {'categories': [{'categories': [], 'title': 'Basemap imagery'},\n", 139 | " {'categories': [],\n", 140 | " 'title': 'Multispectral imagery'},\n", 141 | " {'categories': [],\n", 142 | " 'title': 'Temporal imagery'},\n", 143 | " {'categories': [], 'title': 'Event imagery'}],\n", 144 | " 'title': 'Imagery'},\n", 145 | " {'categories': [{'categories': [], 'title': 'Administrative'},\n", 146 | " {'categories': [], 'title': 'Environmental'},\n", 147 | " {'categories': [], 'title': 'Geometric'}],\n", 148 | " 'title': 'Boundaries'},\n", 149 | " {'categories': [{'categories': [], 'title': 'Population'},\n", 150 | " {'categories': [], 'title': 'Housing'},\n", 151 | " {'categories': [], 'title': 'Neighborhoods'},\n", 152 | " {'categories': [], 'title': 'Jobs'},\n", 153 | " {'categories': [], 'title': 'Income'},\n", 154 | " {'categories': [], 'title': 'Spending'},\n", 155 | " {'categories': [], 'title': 'Health'},\n", 156 | " {'categories': [], 'title': 'Education'},\n", 157 | " {'categories': [], 'title': 'At risk'},\n", 158 | " {'categories': [], 'title': 'Public safety'}],\n", 159 | " 'title': 'People'},\n", 160 | " {'categories': [{'categories': [], 'title': 'Transportation'},\n", 161 | " {'categories': [], 'title': 'Traffic'},\n", 162 | " {'categories': [], 'title': 'Structures'},\n", 163 | " {'categories': [], 'title': 'Utilities'},\n", 164 | " {'categories': [], 'title': 'Businesses'},\n", 165 | " {'categories': [], 'title': 'Agriculture'}],\n", 166 | " 'title': 'Infrastructure'},\n", 167 | " {'categories': [{'categories': [],\n", 168 | " 'title': 'Earth observations'},\n", 169 | " {'categories': [], 'title': 'Oceans'},\n", 170 | " {'categories': [],\n", 171 | " 'title': 'Elevation and bathymetry'},\n", 172 | " {'categories': [],\n", 173 | " 'title': 'Weather and climate'},\n", 174 | " {'categories': [], 'title': 'Land cover'},\n", 175 | " {'categories': [],\n", 176 | " 'title': 'Energy resources'},\n", 177 | " {'categories': [],\n", 178 | " 'title': 'Soils and geology'},\n", 179 | " {'categories': [], 'title': 'Fresh water'},\n", 180 | " {'categories': [], 'title': 'Habitat'},\n", 181 | " {'categories': [], 'title': 'Species'}],\n", 182 | " 'title': 'Environment'}],\n", 183 | " 'title': 'Categories'}]\n", 184 | "\n", 185 | "Original Categories\n", 186 | "~~~~~~~~~~~~\n", 187 | "[{'categories': [{'categories': [{'categories': [],\n", 188 | " 'title': 'New and noteworthy'},\n", 189 | " {'categories': [], 'title': 'Current events'}],\n", 190 | " 'title': 'Trending-2'},\n", 191 | " {'categories': [{'categories': [], 'title': 'Reference maps'},\n", 192 | " {'categories': [], 'title': 'Creative maps'},\n", 193 | " {'categories': [], 'title': 'Vector tiles'},\n", 194 | " {'categories': [],\n", 195 | " 'title': 'Component layers'},\n", 196 | " {'categories': [],\n", 197 | " 'title': 'Historical maps'}],\n", 198 | " 'title': 'Basemaps'},\n", 199 | " {'categories': [{'categories': [], 'title': 'Basemap imagery'},\n", 200 | " {'categories': [],\n", 201 | " 'title': 'Multispectral imagery'},\n", 202 | " {'categories': [],\n", 203 | " 'title': 'Temporal imagery'},\n", 204 | " {'categories': [], 'title': 'Event imagery'}],\n", 205 | " 'title': 'Imagery'},\n", 206 | " {'categories': [{'categories': [], 'title': 'Administrative'},\n", 207 | " {'categories': [], 'title': 'Environmental'},\n", 208 | " {'categories': [], 'title': 'Geometric'}],\n", 209 | " 'title': 'Boundaries'},\n", 210 | " {'categories': [{'categories': [], 'title': 'Population'},\n", 211 | " {'categories': [], 'title': 'Housing'},\n", 212 | " {'categories': [], 'title': 'Neighborhoods'},\n", 213 | " {'categories': [], 'title': 'Jobs'},\n", 214 | " {'categories': [], 'title': 'Income'},\n", 215 | " {'categories': [], 'title': 'Spending'},\n", 216 | " {'categories': [], 'title': 'Health'},\n", 217 | " {'categories': [], 'title': 'Education'},\n", 218 | " {'categories': [], 'title': 'At risk'},\n", 219 | " {'categories': [], 'title': 'Public safety'}],\n", 220 | " 'title': 'People'},\n", 221 | " {'categories': [{'categories': [], 'title': 'Transportation'},\n", 222 | " {'categories': [], 'title': 'Traffic'},\n", 223 | " {'categories': [], 'title': 'Structures'},\n", 224 | " {'categories': [], 'title': 'Utilities'},\n", 225 | " {'categories': [], 'title': 'Businesses'},\n", 226 | " {'categories': [], 'title': 'Agriculture'}],\n", 227 | " 'title': 'Infrastructure'},\n", 228 | " {'categories': [{'categories': [],\n", 229 | " 'title': 'Earth observations'},\n", 230 | " {'categories': [], 'title': 'Oceans'},\n", 231 | " {'categories': [],\n", 232 | " 'title': 'Elevation and bathymetry'},\n", 233 | " {'categories': [],\n", 234 | " 'title': 'Weather and climate'},\n", 235 | " {'categories': [], 'title': 'Land cover'},\n", 236 | " {'categories': [],\n", 237 | " 'title': 'Energy resources'},\n", 238 | " {'categories': [],\n", 239 | " 'title': 'Soils and geology'},\n", 240 | " {'categories': [], 'title': 'Fresh water'},\n", 241 | " {'categories': [], 'title': 'Habitat'},\n", 242 | " {'categories': [], 'title': 'Species'}],\n", 243 | " 'title': 'Environment'}],\n", 244 | " 'title': 'Categories'}]\n" 245 | ] 246 | } 247 | ], 248 | "source": [ 249 | "target_categories_bak = target_gis.content.categories.schema\n", 250 | "target_gis.content.categories.schema = src_categories\n", 251 | "print(\"New Categories:\\n~~~~~~~~~~~~~\")\n", 252 | "pprint.pprint(target_gis.content.categories.schema)\n", 253 | "print(\"\\nOriginal Categories\\n~~~~~~~~~~~~\")\n", 254 | "pprint.pprint(target_categories_bak)" 255 | ] 256 | } 257 | ], 258 | "metadata": { 259 | "kernelspec": { 260 | "display_name": "Python 3", 261 | "language": "python", 262 | "name": "python3" 263 | }, 264 | "language_info": { 265 | "codemirror_mode": { 266 | "name": "ipython", 267 | "version": 3 268 | }, 269 | "file_extension": ".py", 270 | "mimetype": "text/x-python", 271 | "name": "python", 272 | "nbconvert_exporter": "python", 273 | "pygments_lexer": "ipython3", 274 | "version": "3.6.9" 275 | } 276 | }, 277 | "nbformat": 4, 278 | "nbformat_minor": 4 279 | } 280 | -------------------------------------------------------------------------------- /Samples/Copy Specific Item.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Copying a specific Dashboard from one Organization to Another\n", 8 | "This notebook will help with copying and ArcGIS Dashboard or other web application from one organization to another. It will copy any web maps and hosted feature services owned by the user to the destination organization as well.\n", 9 | "\n", 10 | "This notebook cannot be used to copy Groups in their entirety. " 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "#### Set up your initial Variables:\n", 18 | "Define these variables for the source and destination organization and item." 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 1, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "src_gis_url = 'https://myorg.maps.arcgis.com' #https://zyz.maps.arcgis.com - can also use 'home' if you are running the notebook from the org you want to use as the src. \n", 28 | "src_gis_username = '' # Your username for the source organization\n", 29 | "src_gis_password = None # provide a password for that user, if None is left, you will be prompted to provide the password.\n", 30 | "src_item_id = '' # Update this to the item ID of the item you want to copy ex: dbbd9dfedc2344f8bacf893daf20e478\n", 31 | "\n", 32 | "\n", 33 | "target_gis_url = 'https://myorg.maps.arcgis.com' #https://myneworg.maps.arcgis.coNm\n", 34 | "target_gis_username = '' # Username for Target Org\n", 35 | "target_gis_password = '' # Password for Target Org\n", 36 | "target_folder_name = '' #Name for the Folder in the target user's content that you want to copy content into." 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "import arcgis\n", 46 | "src_gis = arcgis.gis.GIS(src_gis_url, src_gis_username, src_gis_password)\n", 47 | "target_gis = arcgis.gis.GIS(target_gis_url, target_gis_username, target_gis_password)\n", 48 | "print(src_gis)\n", 49 | "print(target_gis)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "#### Copy an individual Item and related items\n", 57 | "Execute these cells to copy the item(s) to the target organization. Any items referenced by the item, like web maps used in a dashboard, will also be copied (or attempted to copy)" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 5, 63 | "metadata": {}, 64 | "outputs": [ 65 | { 66 | "data": { 67 | "text/plain": [ 68 | "[]" 69 | ] 70 | }, 71 | "execution_count": 5, 72 | "metadata": {}, 73 | "output_type": "execute_result" 74 | } 75 | ], 76 | "source": [ 77 | "src_item = arcgis.gis.Item(src_gis, src_item_id)\n", 78 | "src_item\n", 79 | "#Validate that the item that appears below is the correct Item you want to copy." 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": 6, 85 | "metadata": {}, 86 | "outputs": [ 87 | { 88 | "data": { 89 | "text/plain": [ 90 | "[]" 91 | ] 92 | }, 93 | "execution_count": 6, 94 | "metadata": {}, 95 | "output_type": "execute_result" 96 | } 97 | ], 98 | "source": [ 99 | "result = target_gis.content.clone_items([src_item],folder=target_folder_name)\n", 100 | "for i in result:\n", 101 | " print()\"Created Target Item: \", i.id, i.title, i.type)" 102 | ] 103 | } 104 | ], 105 | "metadata": { 106 | "esriNotebookRuntime": { 107 | "notebookRuntimeName": "ArcGIS Notebook Python 3 Advanced", 108 | "notebookRuntimeVersion": "3.0" 109 | }, 110 | "kernelspec": { 111 | "display_name": "Python 3", 112 | "language": "python", 113 | "name": "python3" 114 | }, 115 | "language_info": { 116 | "codemirror_mode": { 117 | "name": "ipython", 118 | "version": 3 119 | }, 120 | "file_extension": ".py", 121 | "mimetype": "text/x-python", 122 | "name": "python", 123 | "nbconvert_exporter": "python", 124 | "pygments_lexer": "ipython3", 125 | "version": "3.6.9" 126 | } 127 | }, 128 | "nbformat": 4, 129 | "nbformat_minor": 2 130 | } 131 | -------------------------------------------------------------------------------- /Samples/CopyHubContent.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "#### This notebook can be used to copy ArcGIS Hub Initiatives, Sites and pages from one organization to another." 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 35, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "src_gis_url = 'https://myorg.maps.arcgis.com'\n", 17 | "src_gis_username = ''\n", 18 | "src_gis_password = ''\n", 19 | "\n", 20 | "target_gis_url = 'https://myorg.maps.arcgis.com'\n", 21 | "target_gis_username = ''\n", 22 | "target_gis_password = ''" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "#### Set up Connections to the two different organizations" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "import arcgis\n", 39 | "src_gis = arcgis.gis.GIS(src_gis_url, src_gis_username, src_gis_password)\n", 40 | "target_gis = arcgis.gis.GIS(target_gis_url, target_gis_username, target_gis_password)\n", 41 | "src_hub = arcgis.apps.hub.Hub(src_gis)\n", 42 | "target_hub = arcgis.apps.hub.Hub(target_gis)\n", 43 | "src_initiatives = src_hub.initiatives.search()\n", 44 | "target_initiatives = target_hub.initiatives.search()\n", 45 | "print(\"{} Initiatives in Source Org\".format(len(src_initiatives)))\n", 46 | "print(\"{} Initiatives in Target Org\".format(len(target_initiatives)))" 47 | ] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "metadata": {}, 52 | "source": [ 53 | "#### Duplicate an Initiative" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "#Work in Progress\n", 63 | "#src_initiative_id = '3607f61c0f8e44a681fa75df5e39d0c2'\n", 64 | "#src_initiative = [i for i in src_initiatives if i.itemid == src_initiative_id][0]\n", 65 | "#target_hub.initiatives.add()" 66 | ] 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "metadata": {}, 71 | "source": [ 72 | "#### Duplicate a Site and Sync Site Properties\n", 73 | "Prior to running this step, create the target Site in the target GIS, and record that site's ItemID from your My Content Page, use that for the target_site_item_id below. This script will then sync over site properties etc. It will not copy pages, those need to be manually re-created, and then layout and configurations can be synced via the script below this section" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": 75, 79 | "metadata": {}, 80 | "outputs": [ 81 | { 82 | "data": { 83 | "text/plain": [ 84 | "True" 85 | ] 86 | }, 87 | "execution_count": 75, 88 | "metadata": {}, 89 | "output_type": "execute_result" 90 | } 91 | ], 92 | "source": [ 93 | "src_site_itemid = '5313234937d84e42b4d4625715ebc9f0'\n", 94 | "target_site_itemid = '312f3e8877694977bc44a347671a980d'\n", 95 | "src_site_item = arcgis.gis.Item(src_gis, src_site_itemid)\n", 96 | "src_site_item_props = {\n", 97 | " \"description\": src_site_item.description,\n", 98 | " \"title\": src_site_item.title,\n", 99 | " \"tags\": src_site_item.tags,\n", 100 | " \"snippet\": src_site_item.snippet,\n", 101 | " \"licenseInfo\": src_site_item.licenseInfo\n", 102 | "}\n", 103 | "site_data = src_site_item.get_data()\n", 104 | "site_data['values']['pages'] = []\n", 105 | "arcgis.gis.Item(target_gis, target_site_itemid).update(item_properties=src_site_item_props, data=site_data)\n", 106 | "\n", 107 | "#WIP:\n", 108 | "#pages = [page['id'] for page in site_data['values']['pages']]\n", 109 | "#page_dict = {}\n", 110 | "#for page in pages:\n", 111 | "# result_item = target_gis.content.clone_items([arcgis.gis.Item(src_gis, page)])[0]\n", 112 | "# page_dict[page] = result_item.id\n", 113 | "#old_pages = site_data['values']['pages']\n", 114 | "#new_pages = []\n", 115 | "#for i in old_pages:\n", 116 | "# d = {'id':page_dict[i['id']],\n", 117 | "# 'title':i['title'],\n", 118 | "# 'slug': i['slug']}\n", 119 | "# new_pages.append(d)\n", 120 | "#site_data['values']['pages'] = new_pages" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "#### Sync a single Hub Page's Contents (Layout, etc.) to a Target Page\n", 128 | "Prior to running this step, you need to create the required Pages in the target Site, using whatever name/slug you want for the pages. You will then run this section repeatedly, syncing one page's properties and layout at a time." 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 76, 134 | "metadata": {}, 135 | "outputs": [ 136 | { 137 | "data": { 138 | "text/plain": [ 139 | "True" 140 | ] 141 | }, 142 | "execution_count": 76, 143 | "metadata": {}, 144 | "output_type": "execute_result" 145 | } 146 | ], 147 | "source": [ 148 | "src_page_id = '4ed56b350c834d8a83bd0b3548cc0978'\n", 149 | "target_page_id = '15958231fb2149a7a8069c361fe9bac5'\n", 150 | "src_page_item = arcgis.gis.Item(src_gis, src_page_id)\n", 151 | "src_page_data = src_page_item.get_data()\n", 152 | "src_page_props = {\n", 153 | " \"description\": src_page_item.description,\n", 154 | " \"title\": src_page_item.title,\n", 155 | " \"tags\": src_page_item.tags,\n", 156 | " \"snippet\": src_page_item.snippet,\n", 157 | " \"licenseInfo\": src_page_item.licenseInfo\n", 158 | "}\n", 159 | "arcgis.gis.Item(target_gis, target_page_id).update(item_properties=src_page_props, data=src_page_data)" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": null, 165 | "metadata": {}, 166 | "outputs": [], 167 | "source": [] 168 | } 169 | ], 170 | "metadata": { 171 | "kernelspec": { 172 | "display_name": "Python 3", 173 | "language": "python", 174 | "name": "python3" 175 | }, 176 | "language_info": { 177 | "codemirror_mode": { 178 | "name": "ipython", 179 | "version": 3 180 | }, 181 | "file_extension": ".py", 182 | "mimetype": "text/x-python", 183 | "name": "python", 184 | "nbconvert_exporter": "python", 185 | "pygments_lexer": "ipython3", 186 | "version": "3.6.9" 187 | } 188 | }, 189 | "nbformat": 4, 190 | "nbformat_minor": 4 191 | } 192 | -------------------------------------------------------------------------------- /Samples/Copy_Item_Resources_Between_Items.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from arcgis import GIS\n", 12 | "import arcgis" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": { 19 | "collapsed": true 20 | }, 21 | "outputs": [], 22 | "source": [ 23 | "source_portal = ''\n", 24 | "source_user = ''\n", 25 | "source_pw = ''\n", 26 | "source_itemid = ''\n", 27 | "\n", 28 | "dest_portal = ''\n", 29 | "dest_user = ''\n", 30 | "dest_pw = ''\n", 31 | "dest_itemid = ''\n" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": { 38 | "collapsed": true 39 | }, 40 | "outputs": [], 41 | "source": [ 42 | "gis_src = GIS(source_portal, source_user, source_pw)\n", 43 | "gis_dest = GIS(dest_portal, dest_user, dest_pw)\n", 44 | "\n", 45 | "source_item = arcgis.gis.Item(gis_src, source_itemid)\n", 46 | "dest_item = arcgis.gis.Item(gis_dest, dest_itemid)\n", 47 | "source_item_resources = [r['resource'] for r in source_item.resources.list()]\n", 48 | "dest_item_resources = [r['resource'] for r in dest_item.resources.list()]" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "**Copy Item Resources from Source to Destination**" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "metadata": { 62 | "collapsed": true 63 | }, 64 | "outputs": [], 65 | "source": [ 66 | "for r in [s for s in source_item_resources if \"_thumb\" not in s]:\n", 67 | " if r in dest_item_resources:\n", 68 | " print(\"File {} exists in destination\".format(r))\n", 69 | " continue\n", 70 | " else:\n", 71 | " try:\n", 72 | " tempfile = source_item.resources.get(r)\n", 73 | " result = dest_item.resources.add(tempfile)\n", 74 | " if result['success'] == True:\n", 75 | " print(\"Successfully copied item {} to destination itemid {}\".format(r,dest_itemid))\n", 76 | " except:\n", 77 | " print(\"Error uploading item: {}\".format(r))" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "**Save Item Resources to Folder**" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": null, 90 | "metadata": { 91 | "collapsed": true 92 | }, 93 | "outputs": [], 94 | "source": [ 95 | "output_folder = r\"c:\\tmp\\images\n", 96 | "for r in [s for s in source_item_resources if \"_thumb\" not in s]:\n", 97 | " try:\n", 98 | " result = source_item.resources.get(r, out_folder=output_folder)\n", 99 | " print(result)\n", 100 | " except:\n", 101 | " print(\"########issue downloading: {}\".format(r))" 102 | ] 103 | } 104 | ], 105 | "metadata": { 106 | "kernelspec": { 107 | "display_name": "Python 3", 108 | "language": "python", 109 | "name": "python3" 110 | }, 111 | "language_info": { 112 | "codemirror_mode": { 113 | "name": "ipython", 114 | "version": 3 115 | }, 116 | "file_extension": ".py", 117 | "mimetype": "text/x-python", 118 | "name": "python", 119 | "nbconvert_exporter": "python", 120 | "pygments_lexer": "ipython3", 121 | "version": "3.5.3" 122 | } 123 | }, 124 | "nbformat": 4, 125 | "nbformat_minor": 2 126 | } 127 | -------------------------------------------------------------------------------- /Samples/Export_Users_Content_to_CSV.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Export ArcGIS Online or Portal for ArcGIS Users to CSV\n", 8 | "This script allows a user to login to a portal and export a CSV list of all users and their corresponding items within it\n", 9 | "\n", 10 | "#### First, let's import the ArcGIS Python API & Other Required Python Modules" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "metadata": { 17 | "collapsed": true 18 | }, 19 | "outputs": [], 20 | "source": [ 21 | "from arcgis.gis import GIS\n", 22 | "from arcgis.gis import RoleManager\n", 23 | "import getpass\n", 24 | "import csv\n", 25 | "import os" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "#### Now, let's define some input parameters\n", 33 | "A box will appear allowing you to enter your password. When running interactively, you will be prompted to enter a password. Hit the Enter key to continue." 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "metadata": { 40 | "collapsed": false 41 | }, 42 | "outputs": [], 43 | "source": [ 44 | "# Enter the URL to your ArcGIS Online Organization or Portal for ArcGIS\n", 45 | "portalURL = 'https://org.maps.arcgis.com'\n", 46 | "# Enter the username & password for an Administrator in your Organization\n", 47 | "username = 'jeremy'\n", 48 | "password = getpass.getpass()\n", 49 | "outputFile = r'C:\\temp\\output.csv'" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "#### Next, let's find out how many users are in our Organization\n", 57 | "*Note*: the default user search is for 100 users, we are overriding that to 1000 users below" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "metadata": { 64 | "collapsed": false 65 | }, 66 | "outputs": [], 67 | "source": [ 68 | "gis = GIS(portalURL, username, password)\n", 69 | "allUsers = gis.users.search(max_users=1000)\n", 70 | "print('Total Portal Users: ' + str(len(allUsers)))" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "metadata": {}, 76 | "source": [ 77 | "#### Time to export the CSV File" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "metadata": { 84 | "collapsed": false 85 | }, 86 | "outputs": [], 87 | "source": [ 88 | "with open(outputFile, 'w') as output:\n", 89 | " dataWriter = csv.writer(output, delimiter=',', quotechar='\"', quoting=csv.QUOTE_MINIMAL, lineterminator='\\n')\n", 90 | " # Write header row.\n", 91 | " dataWriter.writerow(['Full Name',\n", 92 | " 'Email',\n", 93 | " 'Username',\n", 94 | " 'Date Created',\n", 95 | " 'Role',\n", 96 | " 'Item ID'])\n", 97 | " \n", 98 | " for user in allUsers:\n", 99 | " # Get User's Role\n", 100 | " if user.role == 'org_admin':\n", 101 | " userRole = 'Administrator'\n", 102 | " elif user.role == 'org_publisher':\n", 103 | " userRole = 'Publisher'\n", 104 | " elif user.role == 'org_user':\n", 105 | " userRole = 'User'\n", 106 | " else:\n", 107 | " userRole = RoleManager(gis).get_role(user.role).name\n", 108 | " \n", 109 | " # Get User's Items\n", 110 | " items_by_id = []\n", 111 | " print(\"Collecting item ids for {}...\".format(user.username))\n", 112 | " user_content = user.items()\n", 113 | " # Copy item ids from root folder first\n", 114 | " for item in user_content:\n", 115 | " items_by_id.append(item.itemid)\n", 116 | " # Copy item ids from folders next\n", 117 | " folders = user.folders\n", 118 | " for folder in folders:\n", 119 | " folder_items = user.items(folder=folder['title'])\n", 120 | " for item in folder_items:\n", 121 | " items_by_id.append(item.itemid)\n", 122 | " \n", 123 | " if len(items_by_id) > 0:\n", 124 | " for i, item_by_id in enumerate(items_by_id): \n", 125 | " dataWriter.writerow([user['fullName'],\n", 126 | " user['email'],\n", 127 | " user['username'],\n", 128 | " time.strftime(\"%Y-%m-%d\",time.gmtime(user['created']/1000)),\n", 129 | " userRole,\n", 130 | " items_by_id[i]])" 131 | ] 132 | } 133 | ], 134 | "metadata": { 135 | "kernelspec": { 136 | "display_name": "Python 3", 137 | "language": "python", 138 | "name": "python3" 139 | }, 140 | "language_info": { 141 | "codemirror_mode": { 142 | "name": "ipython", 143 | "version": 3 144 | }, 145 | "file_extension": ".py", 146 | "mimetype": "text/x-python", 147 | "name": "python", 148 | "nbconvert_exporter": "python", 149 | "pygments_lexer": "ipython3", 150 | "version": "3.5.3" 151 | } 152 | }, 153 | "nbformat": 4, 154 | "nbformat_minor": 2 155 | } 156 | -------------------------------------------------------------------------------- /Samples/Export_Users_to_CSV.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Export ArcGIS Online or Portal for ArcGIS Users to CSV\n", 8 | "This script allows a user to login to a portal and export a CSV list of all users within it\n", 9 | "\n", 10 | "#### First, let's import the ArcGIS Python API & Other Required Python Modules" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 19, 16 | "metadata": { 17 | "collapsed": true 18 | }, 19 | "outputs": [], 20 | "source": [ 21 | "from arcgis.gis import GIS\n", 22 | "from arcgis.gis import RoleManager\n", 23 | "import getpass\n", 24 | "import csv" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "#### Now, let's define some input parameters\n", 32 | "A box will appear allowing you to enter your password. When running interactively, you will be prompted to enter a password. Hit the Enter key to continue." 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 2, 38 | "metadata": { 39 | "collapsed": false 40 | }, 41 | "outputs": [ 42 | { 43 | "name": "stdout", 44 | "output_type": "stream", 45 | "text": [ 46 | "········\n" 47 | ] 48 | } 49 | ], 50 | "source": [ 51 | "# Enter the URL to your ArcGIS Online Organization or Portal for ArcGIS\n", 52 | "portalURL = 'https://org.maps.arcgis.com'\n", 53 | "# Enter the username & password for an Administrator in your Organization\n", 54 | "username = 'jeremy'\n", 55 | "password = getpass.getpass()\n", 56 | "outputFile = r'C:\\temp\\output.csv'" 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "metadata": {}, 62 | "source": [ 63 | "#### Next, let's find out how many users are in our Organization\n", 64 | "*Note*: the default user search is for 100 users, we are overriding that to 1000 users below" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": { 71 | "collapsed": false 72 | }, 73 | "outputs": [], 74 | "source": [ 75 | "gis = GIS(portalURL, username, password)\n", 76 | "allUsers = gis.users.search(max_users=1000)\n", 77 | "print('Total Portal Users: ' + str(len(allUsers)))" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "#### Time to export the CSV File" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": null, 90 | "metadata": { 91 | "collapsed": false 92 | }, 93 | "outputs": [], 94 | "source": [ 95 | "with open(outputFile, 'w') as output:\n", 96 | " dataWriter = csv.writer(output, delimiter=',', quotechar='\"', quoting=csv.QUOTE_MINIMAL, lineterminator='\\n')\n", 97 | " # Write header row.\n", 98 | " dataWriter.writerow(['Full Name',\n", 99 | " 'Email',\n", 100 | " 'Username',\n", 101 | " 'Date Created',\n", 102 | " 'Role'])\n", 103 | " \n", 104 | " for user in allUsers:\n", 105 | " if user.role == 'org_admin':\n", 106 | " userRole = 'Administrator'\n", 107 | " elif user.role == 'org_publisher':\n", 108 | " userRole = 'Publisher'\n", 109 | " elif user.role == 'org_user':\n", 110 | " userRole = 'User'\n", 111 | " else:\n", 112 | " userRole = RoleManager(gis).get_role(user.role).name\n", 113 | " dataWriter.writerow([user['fullName'],\n", 114 | " user['email'],\n", 115 | " user['username'],\n", 116 | " time.strftime(\"%Y-%m-%d\",time.gmtime(user['created']/1000)),\n", 117 | " userRole])\n" 118 | ] 119 | } 120 | ], 121 | "metadata": { 122 | "kernelspec": { 123 | "display_name": "Python 3", 124 | "language": "python", 125 | "name": "python3" 126 | }, 127 | "language_info": { 128 | "codemirror_mode": { 129 | "name": "ipython", 130 | "version": 3 131 | }, 132 | "file_extension": ".py", 133 | "mimetype": "text/x-python", 134 | "name": "python", 135 | "nbconvert_exporter": "python", 136 | "pygments_lexer": "ipython3", 137 | "version": "3.5.3" 138 | } 139 | }, 140 | "nbformat": 4, 141 | "nbformat_minor": 2 142 | } 143 | -------------------------------------------------------------------------------- /Samples/Find_Stale_Portal_Users.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Find Stale Portal Users\n", 8 | "This script lists any user of ArcGIS Online/Portal that is considered _stale_\n", 9 | "\n", 10 | "#### First, let's import the ArcGIS Python API & Other Required Python Modules" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 1, 16 | "metadata": { 17 | "collapsed": true 18 | }, 19 | "outputs": [], 20 | "source": [ 21 | "import time, datetime\n", 22 | "from arcgis.gis import GIS" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "#### Now, let's define some input parameters\n", 30 | "A box will appear allowing you to enter your password. When running interactively, you will be prompted to enter a password. Hit the Enter key to continue." 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 2, 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "# Enter the URL to your ArcGIS Online Organization or Portal for ArcGIS\n", 40 | "portalURL = 'https://org.maps.arcgis.com' # Portal Example: https://gis.domain.com/portal\n", 41 | "# Enter the username & password for an Administrator in your Organization\n", 42 | "username = 'jeremy'\n", 43 | "# Tell us how old a user should be, in days, in order for them to be considered stale\n", 44 | "daysOlderThan = 365" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "#### Next, let's find out how many users are in our Organization\n", 52 | "*Note*: the default user search is for 100 users, we are overriding that to 1000 users below" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": 3, 58 | "metadata": {}, 59 | "outputs": [ 60 | { 61 | "name": "stdout", 62 | "output_type": "stream", 63 | "text": [ 64 | "Enter password: ········\n", 65 | "Total Portal Users: 65\n" 66 | ] 67 | } 68 | ], 69 | "source": [ 70 | "gis = GIS(portalURL, username)\n", 71 | "allUsers = gis.users.search(max_users=1000)\n", 72 | "print('Total Portal Users: ' + str(len(allUsers)))" 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "metadata": {}, 78 | "source": [ 79 | "#### Now, let's find the stale users..." 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": 8, 85 | "metadata": { 86 | "scrolled": true 87 | }, 88 | "outputs": [], 89 | "source": [ 90 | "staleUsers = []\n", 91 | "for user in allUsers:\n", 92 | " if user.lastLogin > 0: #User has logged in \n", 93 | " lastLoginDate = datetime.datetime.strptime(time.ctime(user.lastLogin/1000), \"%a %b %d %H:%M:%S %Y\")\n", 94 | " if lastLoginDate < datetime.datetime.now()-datetime.timedelta(days = daysOlderThan): # User is stale\n", 95 | " if len(user.groups) == 0 and len(user.items()) == 0: # User not a member of any groups and does not own content\n", 96 | " staleUsers.append(user)" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": { 103 | "scrolled": true 104 | }, 105 | "outputs": [], 106 | "source": [ 107 | "#### Let's see who's stale\n", 108 | "for user in staleUsers:\n", 109 | " print('Deleting: {}'.format(user.username))\n", 110 | " user.delete()" 111 | ] 112 | } 113 | ], 114 | "metadata": { 115 | "kernelspec": { 116 | "display_name": "Python 3", 117 | "language": "python", 118 | "name": "python3" 119 | }, 120 | "language_info": { 121 | "codemirror_mode": { 122 | "name": "ipython", 123 | "version": 3 124 | }, 125 | "file_extension": ".py", 126 | "mimetype": "text/x-python", 127 | "name": "python", 128 | "nbconvert_exporter": "python", 129 | "pygments_lexer": "ipython3", 130 | "version": "3.6.2" 131 | } 132 | }, 133 | "nbformat": 4, 134 | "nbformat_minor": 2 135 | } 136 | -------------------------------------------------------------------------------- /Samples/List Group Items - Export to CSV.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## List Group Items and Export to CSV" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "#### Start by importing the required Python modules and defining your GIS object" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "from arcgis.gis import GIS\n", 24 | "import csv\n", 25 | "from datetime import datetime" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "gis = GIS(\"home\")" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "#### Search for a particular Group in your Organization" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": null, 47 | "metadata": {}, 48 | "outputs": [], 49 | "source": [ 50 | "myGroup = gis.groups.search('Your Group Name')" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "metadata": {}, 56 | "source": [ 57 | "Since this is a search operation, the search will return a list of groups that meet the query. By asking for Group 0, we are listing the first group found below. If you are explicit with your search, there should only be one group returned!" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "myGroup[0]" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": {}, 72 | "source": [ 73 | "#### Now that we have our group, we need to get a list of all of the items inside of it" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "myGroupsStuff = myGroup[0].content()" 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": {}, 88 | "source": [ 89 | "#### Now that we have all of our items, it's time to export a CSV" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "metadata": {}, 95 | "source": [ 96 | "Get the current time stamp so the exported CSV has a unique title" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [ 105 | "now = datetime.now()\n", 106 | "dt_string = now.strftime(\"%Y%m%d_%H%M%S\")" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": null, 112 | "metadata": {}, 113 | "outputs": [], 114 | "source": [ 115 | "with open('/arcgis/home/Output_{}.csv'.format(dt_string), 'w') as output:\n", 116 | " dataWriter = csv.writer(output, delimiter=',', quotechar='\"', quoting=csv.QUOTE_MINIMAL, lineterminator='\\n')\n", 117 | " # Write header row.\n", 118 | " dataWriter.writerow(['Item ID',\n", 119 | " 'Owner',\n", 120 | " 'Title',\n", 121 | " 'Summary',\n", 122 | " 'Date Created',\n", 123 | " 'Date Modified'])\n", 124 | " for item in myGroupsStuff:\n", 125 | " dataWriter.writerow([\n", 126 | " item.id,\n", 127 | " item.owner,\n", 128 | " item.title,\n", 129 | " item.snippet,\n", 130 | " time.strftime(\"%Y-%m-%d\",time.gmtime(item.created/1000)),\n", 131 | " time.strftime(\"%Y-%m-%d\",time.gmtime(item.modified/1000)),\n", 132 | " ])" 133 | ] 134 | } 135 | ], 136 | "metadata": { 137 | "esriNotebookRuntime": { 138 | "notebookRuntimeName": "ArcGIS Notebook Python 3 Advanced", 139 | "notebookRuntimeVersion": "3.0" 140 | }, 141 | "kernelspec": { 142 | "display_name": "Python 3", 143 | "language": "python", 144 | "name": "python3" 145 | }, 146 | "language_info": { 147 | "codemirror_mode": { 148 | "name": "ipython", 149 | "version": 3 150 | }, 151 | "file_extension": ".py", 152 | "mimetype": "text/x-python", 153 | "name": "python", 154 | "nbconvert_exporter": "python", 155 | "pygments_lexer": "ipython3", 156 | "version": "3.6.9" 157 | } 158 | }, 159 | "nbformat": 4, 160 | "nbformat_minor": 2 161 | } 162 | -------------------------------------------------------------------------------- /Samples/List Items And Thumbnail Sizes.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## List Items and Thumbnail Sizes\n", 8 | "This script allows a user to login to a portal and find all items and their corresponding thumbnail size\n", 9 | "\n", 10 | "#### First, let's import the ArcGIS Python API & Other Required Python Modules" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "from arcgis.gis import GIS\n", 20 | "import getpass\n", 21 | "import matplotlib.pyplot" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "#### Now, let's define some input parameters\n", 29 | "A box will appear allowing you to enter your password. When running interactively, you will be prompted to enter a password. Hit the Enter key to continue." 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "# Enter the URL to your ArcGIS Online Organization or Portal for ArcGIS\n", 39 | "portalURL = 'https://myorg.maps.arcgis.com'\n", 40 | "# Enter the username & password for an Administrator in your Organization\n", 41 | "username = 'username'\n", 42 | "password = getpass.getpass()" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "#### Now, let's define out GIS object - a connection to the portal" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": null, 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [ 58 | "gis = GIS(portalURL, username, password)\n", 59 | "token = gis._con.token" 60 | ] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "metadata": {}, 65 | "source": [ 66 | "#### Next, let's find out how many Web Maps are in our Organization\n", 67 | "*Note*: if your Org has more than 1000 items, adjust the 'max_items' parameter below" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": null, 73 | "metadata": {}, 74 | "outputs": [], 75 | "source": [ 76 | "items = gis.content.search('owner:{}'.format(username),max_items=1000)\n", 77 | "print('Found {} Total Items'.format(len(items)))" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "for item in items:\n", 87 | " try:\n", 88 | " thumbURL = '{}?token={}'.format(item.get_thumbnail_link(),token)\n", 89 | " height, width, channels = matplotlib.pyplot.imread(thumbURL).shape\n", 90 | " print(item.id,height,width)\n", 91 | " #print(thumbURL)\n", 92 | " except:\n", 93 | " print(item.id, 'Unknown Dimensions')" 94 | ] 95 | } 96 | ], 97 | "metadata": { 98 | "kernelspec": { 99 | "display_name": "Python 3", 100 | "language": "python", 101 | "name": "python3" 102 | }, 103 | "language_info": { 104 | "codemirror_mode": { 105 | "name": "ipython", 106 | "version": 3 107 | }, 108 | "file_extension": ".py", 109 | "mimetype": "text/x-python", 110 | "name": "python", 111 | "nbconvert_exporter": "python", 112 | "pygments_lexer": "ipython3", 113 | "version": "3.6.5" 114 | } 115 | }, 116 | "nbformat": 4, 117 | "nbformat_minor": 2 118 | } 119 | -------------------------------------------------------------------------------- /Samples/List Items With Corresponding Service URL.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## List Items with Corresponding Service URL\n", 8 | "This script allows a user to login to a portal and find Web Maps that contain a specific service URL\n", 9 | "\n", 10 | "#### First, let's import the ArcGIS Python API & Other Required Python Modules" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "metadata": { 17 | "collapsed": true 18 | }, 19 | "outputs": [], 20 | "source": [ 21 | "from arcgis.gis import GIS\n", 22 | "import getpass" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "#### Now, let's define some input parameters\n", 30 | "A box will appear allowing you to enter your password. When running interactively, you will be prompted to enter a password. Hit the Enter key to continue." 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": null, 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "# Enter the URL to your ArcGIS Online Organization or Portal for ArcGIS\n", 40 | "portalURL = 'https://myorg.maps.arcgis.com'\n", 41 | "# Enter the username & password for an Administrator in your Organization\n", 42 | "username = 'username'\n", 43 | "password = getpass.getpass()\n", 44 | "serviceURL = 'SampleWorldCities'" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "#### Now, let's define out GIS object - a connection to the portal" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": { 58 | "collapsed": true 59 | }, 60 | "outputs": [], 61 | "source": [ 62 | "gis = GIS(portalURL, username, password)" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "#### Next, let's find out how many Web Maps are in our Organization\n", 70 | "*Note*: if your Org has more than 1000 items, adjust the 'max_items' parameter below" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "metadata": {}, 77 | "outputs": [], 78 | "source": [ 79 | "webMaps = gis.content.search('*',item_type='Web Map',max_items=1000)\n", 80 | "print('Found {} Total WebMap Items'.format(len(webMaps)))" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": null, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "for webMap in webMaps:\n", 90 | " if serviceURL in str(webMap.get_data()):\n", 91 | " print(webMap.id)" 92 | ] 93 | } 94 | ], 95 | "metadata": { 96 | "kernelspec": { 97 | "display_name": "Python 3", 98 | "language": "python", 99 | "name": "python3" 100 | }, 101 | "language_info": { 102 | "codemirror_mode": { 103 | "name": "ipython", 104 | "version": 3 105 | }, 106 | "file_extension": ".py", 107 | "mimetype": "text/x-python", 108 | "name": "python", 109 | "nbconvert_exporter": "python", 110 | "pygments_lexer": "ipython3", 111 | "version": "3.5.3" 112 | } 113 | }, 114 | "nbformat": 4, 115 | "nbformat_minor": 2 116 | } 117 | -------------------------------------------------------------------------------- /Samples/List_Items_With_Corresponding_Service_URL_and_Update.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## List Items with Corresponding Service URL and Update\n", 8 | "This script allows a user to login to a portal and find/replace URL patterns that exist in Web Maps. This is useful for GIS Services that have moved to a different DNS or Service Name. \n", 9 | "\n", 10 | "#### First, let's import the ArcGIS Python API & Other Required Python Modules" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "metadata": { 17 | "collapsed": true 18 | }, 19 | "outputs": [], 20 | "source": [ 21 | "from arcgis.gis import GIS\n", 22 | "import getpass" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "#### Now, let's define some input parameters\n", 30 | "A box will appear allowing you to enter your password. When running interactively, you will be prompted to enter a password. Hit the Enter key to continue." 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": null, 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "# Enter the URL to your ArcGIS Online Organization or Portal for ArcGIS\n", 40 | "portalURL = 'https://org.maps.arcgis.com'\n", 41 | "# Enter the username & password for an Administrator in your Organization\n", 42 | "username = 'username'\n", 43 | "password = getpass.getpass()" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "metadata": { 50 | "collapsed": true 51 | }, 52 | "outputs": [], 53 | "source": [ 54 | "serviceURL = 'oldURL.domain.com'\n", 55 | "newServiceURL = 'newURL.domain.com'" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "#### Now, let's define out GIS object - a connection to the portal" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": { 69 | "collapsed": true 70 | }, 71 | "outputs": [], 72 | "source": [ 73 | "gis = GIS(portalURL, username, password)" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": {}, 79 | "source": [ 80 | "#### Next, let's find out how many Web Maps are in our Organization\n", 81 | "*Note*: if your Org has more than 1000 items, adjust the 'max_items' parameter below" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": null, 87 | "metadata": {}, 88 | "outputs": [], 89 | "source": [ 90 | "webMaps = gis.content.search('*',item_type='Web Map',max_items=1000)\n", 91 | "print('Found {} Total WebMap Items'.format(len(webMaps)))" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "for i, webMap in enumerate(webMaps):\n", 101 | " #If you want to see progress, uncomment the following line\n", 102 | " #print('Getting Data for: {}'.format(i)) \n", 103 | " jsonData = str(webMap.get_data())\n", 104 | " if serviceURL in jsonData:\n", 105 | " print('Updating WebMap: {}'.format(webMap.id))\n", 106 | " webMap.update(data=jsonData.replace(serviceURL,newServiceURL))" 107 | ] 108 | } 109 | ], 110 | "metadata": { 111 | "kernelspec": { 112 | "display_name": "Python 3", 113 | "language": "python", 114 | "name": "python3" 115 | }, 116 | "language_info": { 117 | "codemirror_mode": { 118 | "name": "ipython", 119 | "version": 3 120 | }, 121 | "file_extension": ".py", 122 | "mimetype": "text/x-python", 123 | "name": "python", 124 | "nbconvert_exporter": "python", 125 | "pygments_lexer": "ipython3", 126 | "version": "3.5.3" 127 | } 128 | }, 129 | "nbformat": 4, 130 | "nbformat_minor": 2 131 | } 132 | -------------------------------------------------------------------------------- /Samples/List_Pro_Licenses_in_AGO_Organization.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "#Import Modules\n", 10 | "import arcgis\n", 11 | "\n", 12 | "#List All Users in the Organization\n", 13 | "your_username = input(\"Enter your ArcGIS Online Username (case-sensitive!): \")\n", 14 | "gis = arcgis.gis.GIS(username=your_username)\n", 15 | "allUsers = gis.users.search()\n", 16 | "\n", 17 | "#Create a License Manager Object to use to query the licenses\n", 18 | "licenseManager = arcgis.gis.admin.AGOLAdminManager(gis)\n", 19 | "users_with_pro = []\n", 20 | "for license in licenseManager.license.get(\"ArcGIS Pro\").all():\n", 21 | " if \"desktop\" in str(license['entitlements']):\n", 22 | " print(\"User \", license['username'], \" is currently licensed for ArcGIS Pro!\")\n", 23 | " users_with_pro.append(license['username'])" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": null, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "#Print a CSV you can paste into a text file and save as .csv\n", 33 | "print(\"username,email,hasProLicense\")\n", 34 | "for user in allUsers:\n", 35 | " if user.username in users_with_pro:\n", 36 | " print(\",\".join([user.username, user.email, \"Yes\"]))\n", 37 | " else:\n", 38 | " print(\",\".join([user.username, user.email, \"No\"]))" 39 | ] 40 | } 41 | ], 42 | "metadata": { 43 | "kernelspec": { 44 | "display_name": "Python 3", 45 | "language": "python", 46 | "name": "python3" 47 | }, 48 | "language_info": { 49 | "codemirror_mode": { 50 | "name": "ipython", 51 | "version": 3 52 | }, 53 | "file_extension": ".py", 54 | "mimetype": "text/x-python", 55 | "name": "python", 56 | "nbconvert_exporter": "python", 57 | "pygments_lexer": "ipython3", 58 | "version": "3.6.2" 59 | } 60 | }, 61 | "nbformat": 4, 62 | "nbformat_minor": 2 63 | } 64 | -------------------------------------------------------------------------------- /Samples/Migrate_Enterprise_Users.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Migrate Enterprise Users\n", 8 | "This script allows a user to login to change the UPN Name of an Enteprise Account\n", 9 | "\n", 10 | "#### First, let's import the ArcGIS Python API & Other Required Python Modules" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "metadata": { 17 | "collapsed": true 18 | }, 19 | "outputs": [], 20 | "source": [ 21 | "from arcgis.gis import *" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "#### Now, let's define some input parameters\n", 29 | "A box will appear allowing you to enter your password. When running interactively, you will be prompted to enter a password. Hit the Enter key to continue." 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": { 36 | "collapsed": false 37 | }, 38 | "outputs": [], 39 | "source": [ 40 | "# Enter the URL to your ArcGIS Online Organization or Portal for ArcGIS\n", 41 | "portalURL = 'https://myorg.maps.arcgis.com'\n", 42 | "OrgBaseURL = portalURL.split('/')[-1].split('.')[0]\n", 43 | "# Enter the username & password for an Administrator in your Organization\n", 44 | "username = ''\n", 45 | "password = ''" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "metadata": {}, 51 | "source": [ 52 | "#### Define the Old UPN and New UPN for the user in your Org\n", 53 | "\n", 54 | "*Note*: THIS MUST MATCH THE SAML NAMEID RESPONSE" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": null, 60 | "metadata": { 61 | "collapsed": false 62 | }, 63 | "outputs": [], 64 | "source": [ 65 | "orig_UPN = \"123456@company.com\"\n", 66 | "new_UPN = \"John.Doe@company.com\"\n", 67 | "# Below we will automatically get the proper User ID structure\n", 68 | "orig_userid = '{}_{}'.format(orig_UPN,OrgBaseURL)\n", 69 | "new_userid = '{}_{}'.format(new_UPN,OrgBaseURL)" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": {}, 75 | "source": [ 76 | "#### Next, let's find out how many users are in our Organization\n", 77 | "*Note*: the default user search is for 100 users, we are overriding that to 1000 users below" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "metadata": { 84 | "collapsed": false 85 | }, 86 | "outputs": [], 87 | "source": [ 88 | "gis = GIS(portalURL, username, password)\n", 89 | "allUsers = gis.users.search(max_users=1000)\n", 90 | "print('Total Portal Users: ' + str(len(allUsers)))" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": { 96 | "collapsed": true 97 | }, 98 | "source": [ 99 | "#### Get the details for the Original User" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": null, 105 | "metadata": { 106 | "collapsed": false 107 | }, 108 | "outputs": [], 109 | "source": [ 110 | "olduser = gis.users.get(orig_userid)\n", 111 | "olduser" 112 | ] 113 | }, 114 | { 115 | "cell_type": "markdown", 116 | "metadata": {}, 117 | "source": [ 118 | "#### Remove ArcGIS Pro Entitlements" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": null, 124 | "metadata": { 125 | "collapsed": false 126 | }, 127 | "outputs": [], 128 | "source": [ 129 | "licenses = gis.admin.license\n", 130 | "if licenses.get(name=\"arcgis pro\").user_entitlement(username=orig_userid):\n", 131 | " oldUserEntitlements = licenses.get(name=\"arcgis pro\").user_entitlement(username=orig_userid)['entitlements']\n", 132 | " licenses.get(name=\"arcgis pro\").revoke(username=orig_userid, entitlements=oldUserEntitlements, supress_email=True)\n", 133 | " print('Removed Entitlements')\n", 134 | "else:\n", 135 | " print('No entitlements to Remove')\n", 136 | "if bool(licenses.get(name=\"arcgis pro\").user_entitlement(username=orig_userid)) == False:\n", 137 | " print('User has no entitlements')" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "metadata": {}, 143 | "source": [ 144 | "#### Create a New User Based on the Old User's Info" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": null, 150 | "metadata": { 151 | "collapsed": false 152 | }, 153 | "outputs": [], 154 | "source": [ 155 | "if olduser.role == 'org_admin':\n", 156 | " # Based on the old user's info, create the New User\n", 157 | " newuser = gis.users.create(username = new_userid,\n", 158 | " password = None,\n", 159 | " firstname = olduser.firstName,\n", 160 | " lastname = olduser.lastName,\n", 161 | " email = olduser.email,\n", 162 | " description = olduser.description,\n", 163 | " role = 'org_user',\n", 164 | " provider = 'enterprise',\n", 165 | " idp_username=new_UPN)\n", 166 | "else:\n", 167 | " # Based on the old user's info, create the New User\n", 168 | " newuser = gis.users.create(username = new_userid,\n", 169 | " password = None,\n", 170 | " firstname = olduser.firstName,\n", 171 | " lastname = olduser.lastName,\n", 172 | " email = olduser.email,\n", 173 | " description = olduser.description,\n", 174 | " role = olduser.role,\n", 175 | " provider = 'enterprise',\n", 176 | " idp_username=new_UPN)\n", 177 | "print('Created User: {}'.format(new_userid))" 178 | ] 179 | }, 180 | { 181 | "cell_type": "markdown", 182 | "metadata": {}, 183 | "source": [ 184 | "#### Set the language of the new user to match the old user" 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": null, 190 | "metadata": { 191 | "collapsed": false 192 | }, 193 | "outputs": [], 194 | "source": [ 195 | "newuser.update(culture=olduser.culture)\n", 196 | "print('Updated language to: {}'.format(newuser.culture))" 197 | ] 198 | }, 199 | { 200 | "cell_type": "markdown", 201 | "metadata": {}, 202 | "source": [ 203 | "#### Get the details for the New User" 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": null, 209 | "metadata": { 210 | "collapsed": false 211 | }, 212 | "outputs": [], 213 | "source": [ 214 | "newuser = gis.users.get(new_userid)\n", 215 | "newuser" 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "metadata": {}, 221 | "source": [ 222 | "#### Reassign Groups" 223 | ] 224 | }, 225 | { 226 | "cell_type": "code", 227 | "execution_count": null, 228 | "metadata": { 229 | "collapsed": false 230 | }, 231 | "outputs": [], 232 | "source": [ 233 | "usergroups = olduser['groups']\n", 234 | "for group in usergroups:\n", 235 | " grp = gis.groups.get(group['id'])\n", 236 | " if (grp.owner == orig_userid):\n", 237 | " grp.reassign_to(new_userid)\n", 238 | " else:\n", 239 | " grp.add_users([new_userid])\n", 240 | " grp.remove_users([orig_userid])\n", 241 | " \n", 242 | "print('Reassigned Groups')" 243 | ] 244 | }, 245 | { 246 | "cell_type": "markdown", 247 | "metadata": {}, 248 | "source": [ 249 | "#### Reassign Content" 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": null, 255 | "metadata": { 256 | "collapsed": false 257 | }, 258 | "outputs": [], 259 | "source": [ 260 | "usercontent = olduser.items()\n", 261 | "folders = olduser.folders\n", 262 | "for item in usercontent:\n", 263 | " try:\n", 264 | " item.reassign_to(new_userid)\n", 265 | " except Exception as e:\n", 266 | " print(\"Item may have been already assigned to the user.\")\n", 267 | "\n", 268 | "for folder in folders:\n", 269 | " gis.content.create_folder(folder['title'], new_userid)\n", 270 | " folderitems = olduser.items(folder=folder['title'])\n", 271 | " for item in folderitems:\n", 272 | " item.reassign_to(new_userid, target_folder=folder['title'])\n", 273 | "\n", 274 | "print('Reassigned Content')" 275 | ] 276 | }, 277 | { 278 | "cell_type": "markdown", 279 | "metadata": { 280 | "collapsed": true 281 | }, 282 | "source": [ 283 | "#### Disable Old User Account" 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": null, 289 | "metadata": { 290 | "collapsed": false 291 | }, 292 | "outputs": [], 293 | "source": [ 294 | "olduser.disable()\n", 295 | "print('Disabled Old User')" 296 | ] 297 | }, 298 | { 299 | "cell_type": "markdown", 300 | "metadata": {}, 301 | "source": [ 302 | "#### Assign Entitlements" 303 | ] 304 | }, 305 | { 306 | "cell_type": "code", 307 | "execution_count": null, 308 | "metadata": { 309 | "collapsed": false 310 | }, 311 | "outputs": [], 312 | "source": [ 313 | "if oldUserEntitlements:\n", 314 | " licenses.get(name=\"arcgis pro\").assign(username=new_userid, entitlements=oldUserEntitlements, supress_email=True)\n", 315 | " print('Assigned entitlements')\n", 316 | "else:\n", 317 | " print('User does not have entitlements')\n", 318 | "if bool(licenses.get(name=\"arcgis pro\").user_entitlement(username=new_userid)) == True:\n", 319 | " print('User has entitlements')" 320 | ] 321 | } 322 | ], 323 | "metadata": { 324 | "kernelspec": { 325 | "display_name": "Python 3", 326 | "language": "python", 327 | "name": "python3" 328 | }, 329 | "language_info": { 330 | "codemirror_mode": { 331 | "name": "ipython", 332 | "version": 3 333 | }, 334 | "file_extension": ".py", 335 | "mimetype": "text/x-python", 336 | "name": "python", 337 | "nbconvert_exporter": "python", 338 | "pygments_lexer": "ipython3", 339 | "version": "3.5.3" 340 | } 341 | }, 342 | "nbformat": 4, 343 | "nbformat_minor": 2 344 | } 345 | -------------------------------------------------------------------------------- /Samples/Update_IDP_Settings.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "#set variables and connect to Portal\n", 12 | "portal_url = \"<>\"\n", 13 | "user = \"<>\"\n", 14 | "pw = \"<>\"\n", 15 | "\n", 16 | "from geosaurus.src import arcgis #currently only available in latest 'arcgis' code\n", 17 | "GIS = arcgis.GIS(portal_url, user, pw)" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 5, 23 | "metadata": {}, 24 | "outputs": [ 25 | { 26 | "data": { 27 | "text/plain": [ 28 | "{\n", 29 | " \"updateProfileAtSignin\": false,\n", 30 | " \"supportsLogoutRequest\": false,\n", 31 | " \"entityId\": null,\n", 32 | " \"userCreditAssignment\": -1,\n", 33 | " \"roleId\": null,\n", 34 | " \"level\": null,\n", 35 | " \"useSHA256\": false,\n", 36 | " \"bindingPostUrl\": \"https://adfshost.esri.com/adfs/ls/idpinitiatedsignon.aspx\",\n", 37 | " \"id\": \"C96fDHeteIIx9bSK\",\n", 38 | " \"supportSignedRequest\": false,\n", 39 | " \"signUpMode\": \"Invitation\",\n", 40 | " \"name\": \"set_by_code\",\n", 41 | " \"certificate\": \"MIIC6DC...\",\n", 42 | " \"encryptionCertificate\": \"MIIC7jCCAd....\",\n", 43 | " \"groups\": [],\n", 44 | " \"bindingUrl\": \"https://adfshost.esri.com/adfs/ls/idpinitiatedsignon.aspx\",\n", 45 | " \"encryptionSupported\": false,\n", 46 | " \"logoutUrl\": \"https://adfshost.esri.com/adfs/ls/\"\n", 47 | "}" 48 | ] 49 | }, 50 | "execution_count": 5, 51 | "metadata": {}, 52 | "output_type": "execute_result" 53 | } 54 | ], 55 | "source": [ 56 | "#Print current idp configuration\n", 57 | "GIS.admin.idp.configuration" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 13, 63 | "metadata": {}, 64 | "outputs": [ 65 | { 66 | "data": { 67 | "text/plain": [ 68 | "{'idpId': 'YVGHuJzThFoQLcDY', 'success': True}" 69 | ] 70 | }, 71 | "execution_count": 13, 72 | "metadata": {}, 73 | "output_type": "execute_result" 74 | } 75 | ], 76 | "source": [ 77 | "#unregister current IdPp\n", 78 | "GIS.admin.idp._unregister()\n", 79 | "\n", 80 | "#re-add IdP\n", 81 | "GIS.admin.idp._add(name=\"testname\",idpMetadataUrl=\"https://adfshost.esri.com/federationmetadata/2007-06/federationmetadata.xml\")" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": 25, 87 | "metadata": {}, 88 | "outputs": [ 89 | { 90 | "data": { 91 | "text/plain": [ 92 | "True" 93 | ] 94 | }, 95 | "execution_count": 25, 96 | "metadata": {}, 97 | "output_type": "execute_result" 98 | } 99 | ], 100 | "source": [ 101 | "#Update Name or Signup Mode configuration\n", 102 | "\n", 103 | "GIS.admin.idp.configuration\n", 104 | "GIS.admin.idp._update(name=\"test3\")\n", 105 | "\n", 106 | "GIS.admin.idp.configuration\n", 107 | "GIS.admin.idp._update(signUpMode=\"Automatic\")" 108 | ] 109 | } 110 | ], 111 | "metadata": { 112 | "kernelspec": { 113 | "display_name": "Python 3", 114 | "language": "python", 115 | "name": "python3" 116 | }, 117 | "language_info": { 118 | "codemirror_mode": { 119 | "name": "ipython", 120 | "version": 3 121 | }, 122 | "file_extension": ".py", 123 | "mimetype": "text/x-python", 124 | "name": "python", 125 | "nbconvert_exporter": "python", 126 | "pygments_lexer": "ipython3", 127 | "version": "3.5.3" 128 | } 129 | }, 130 | "nbformat": 4, 131 | "nbformat_minor": 2 132 | } 133 | -------------------------------------------------------------------------------- /Samples/Update_Service_Instances.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "**Set Variables**" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": { 14 | "collapsed": true 15 | }, 16 | "outputs": [], 17 | "source": [ 18 | "serverUrl = \"https:///\"\n", 19 | "serviceFolderName = \"folder\" #Server Folder (/rest/services/)\n", 20 | "instanceNumber = <> # Integer, number of min instances to set, must be <= max Instance Count\n", 21 | "user = \"\" #Must be your PSA account if using a federated Server\n", 22 | "pw = \"\"" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "Create Server Connection (direct in this case)" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "import arcgis.gis.server as Server # Currently requires the latest build of the arcgis module\n", 39 | "arcgis.__version__ #confirm version, must be 1.2.5 or higher\n", 40 | "s = Server.Server(serverUrl, username=user, password=pw)" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "Process Services in given folder and set minInstancesPerNode value" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": null, 53 | "metadata": {}, 54 | "outputs": [], 55 | "source": [ 56 | "for svc in s.services.list(folder=serviceFolderName):\n", 57 | " svc_dict = json.loads(svc._json)\n", 58 | " print(\"Updating Service: {}\".format(svc_dict[\"serviceName\"]))\n", 59 | " svc_dict['minInstancesPerNode'] = instanceNumber\n", 60 | " newJSON = json.dumps(svc_dict)\n", 61 | " print(svc.edit(newJSON))\n", 62 | " print(\"Successfully updated service: {}\".format(svc_dict['serviceName']))\n", 63 | " " 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "metadata": { 70 | "collapsed": true 71 | }, 72 | "outputs": [], 73 | "source": [] 74 | } 75 | ], 76 | "metadata": { 77 | "kernelspec": { 78 | "display_name": "Python 3", 79 | "language": "python", 80 | "name": "python3" 81 | }, 82 | "language_info": { 83 | "codemirror_mode": { 84 | "name": "ipython", 85 | "version": 3 86 | }, 87 | "file_extension": ".py", 88 | "mimetype": "text/x-python", 89 | "name": "python", 90 | "nbconvert_exporter": "python", 91 | "pygments_lexer": "ipython3", 92 | "version": "3.5.3" 93 | } 94 | }, 95 | "nbformat": 4, 96 | "nbformat_minor": 2 97 | } 98 | -------------------------------------------------------------------------------- /Samples/Update_User_Types.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "#Imports\n", 10 | "import arcgis\n", 11 | "import time\n", 12 | "start = time.time()" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "portal_username = ''\n", 22 | "pw = ''\n", 23 | "\n", 24 | "portal_url = 'https:///portal'\n", 25 | "\n", 26 | "#specify FROM and TO user types\n", 27 | "original_user_type = 'liteUT'\n", 28 | "new_user_type = 'viewerUT'\n", 29 | "\n", 30 | "gis = arcgis.gis.GIS(portal_url,portal_username,pw)\n", 31 | "liteUsers = gis.users.search(max_users=10000, user_type=original_user_type)\n", 32 | "print(\"Total Users needing update: {}\".format(len(liteUsers)))\n", 33 | "for user in liteUsers:\n", 34 | " print(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\")\n", 35 | " print(\"Updating: {}\".format(user))\n", 36 | " try:\n", 37 | " user.update_license_type(new_user_type)\n", 38 | " except:\n", 39 | " print(\"failed to update {}\".format(user))\n", 40 | " " 41 | ] 42 | } 43 | ], 44 | "metadata": { 45 | "kernelspec": { 46 | "display_name": "Python 3", 47 | "language": "python", 48 | "name": "python3" 49 | }, 50 | "language_info": { 51 | "codemirror_mode": { 52 | "name": "ipython", 53 | "version": 3 54 | }, 55 | "file_extension": ".py", 56 | "mimetype": "text/x-python", 57 | "name": "python", 58 | "nbconvert_exporter": "python", 59 | "pygments_lexer": "ipython3", 60 | "version": "3.6.8" 61 | } 62 | }, 63 | "nbformat": 4, 64 | "nbformat_minor": 2 65 | } 66 | --------------------------------------------------------------------------------