AnsweredAssumed Answered

Moving and updating a Portal item from a "dev" Portal to a "prod" Portal using the Python API

Question asked by jay.gregory on Jan 6, 2017
Latest reply on Apr 3, 2017 by ptak_devnetinc

I'm trying to create a juptyer notebook that facilities copying and updating a "dev" Portal webmap to my "prod" Portal (I'm working with two internal Portals, same layers published to each).  

 

It's easy to copy a web map from one portal to the other, using the copy_item function from clone portal users groups and content | ArcGIS for Developers .  However, I'm having problems updating the web map once I've copied it from dev to production.  

 

As far as I can tell, all I'd need to do is change the urls (from my development ArcGIS server to my production ArcGIS server) in the json representation of the Portal item.  It also seems like I should change the itemId  for the layers as well,  (I might just have to hardcode the production itemIds unless I want to search for the layers by name and retrieve the ids that way - I have yet to solve the best way to approach that problem).  

 

Lastly, once I've modified the data dictionary / json representation from the web map, I just run the update method.  

 

See the code below:

#####from some of the API documentation examples
TEXT_BASED_ITEM_TYPES = frozenset(['Web Map', 'Feature Service', 'Map Service','Web Scene',
                                   'Image Service', 'Feature Collection',
                                   'Feature Collection Template',
                                   'Web Mapping Application', 'Mobile Application',
                                   'Symbol Set', 'Color Set',
                                   'Windows Viewer Configuration'])
ITEM_COPY_PROPERTIES = ['title', 'type', 'typeKeywords', 'description', 'tags',
                        'snippet', 'extent', 'spatialReference', 'name',
                        'accessInformation', 'licenseInfo', 'culture', 'url', ]

def copy_item(target, owner, folder, item):
    with tempfile.TemporaryDirectory() as temp_dir:
        copy_item = {}
        for property_name in ITEM_COPY_PROPERTIES:
            copy_item[property_name] = item[property_name]

        data_file = None
        if item.type in TEXT_BASED_ITEM_TYPES:
            # If its a text-based item, then read the text and add it to the request.
            if item.size > 0:
                text = item.get_data(False)
                #textstr = text.decode('utf-8')
                copy_item['text'] = text
        elif item.size > 0: # download data for all other types, not just item.type in FILE_BASED_ITEM_TYPES:
            # download data and add to the request as a file
            data_file = item.download(temp_dir)

        thumbnail_file = item.download_thumbnail(temp_dir)

        metadata_file = item.download_metadata(temp_dir)

        # Add the item to the target portal
        copied_item = target.content.add(copy_item, data_file, thumbnail_file,
                                         metadata_file, owner, folder)

        return copied_item
#####################################

##My Notebook
from arcgis.gis import GIS
from arcgis.gis import Item
from arcgis.gis import User
import tempfile
from IPython.display import display

source = GIS("dev portal", "username","password", verify_cert=False)
target = GIS("prod portal","username","password", verify_cert=False)

myDevMap = Item(source,"d8c2a39f366843e0a5e24c5299aeb57c")

copy_item(target,"user","folder",myDevMap)
######Once I've copied once, I just hardcode the item ID
myProdMap = Item(target,"cc6a02d3e9644cd08cc105ae30a06d87")

#here I'm retrieveing the json representation of the map
data = myProdMap.get_data(try_json=True)
replaceData = data

##this is where I replace the URLS "gis-dev" is the development url, and "gis" is the production
layers = data['operationalLayers']
for index in range(len(layers)):
    if 'url' in layers[index].keys():
        url = layers[index]['url']
        if "gis-dev" in url:
            newurl = url.replace("gis-dev","gis")
            replaceData['operationalLayers'][index]['url']=newurl

myProdMap.update(data = replaceData)

 

When I try to run the update method, I get a whole host of errors, and I can't make heads or tails of them.  So, am I doing something wrong /  is there something I'm missing?   I see some http vs https errors in here, along with some key errors as well.  Don't really know what's going on but would love some assistance on this one.  

 

AttributeError                            Traceback (most recent call last)
<ipython-input-27-5fe3a5afb862> in <module>()
----> 1 EONHome_fromDev.update(data = replaceData)

C:\Program Files\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\lib\site-packages\arcgis\gis.py in update(self, item_properties, data, thumbnail, metadata)
   2846                     item_properties['tags'] = ",".join(item_properties['tags'])
   2847
-> 2848         ret = self._portal.update_item(self.itemid, item_properties, data, thumbnail, metadata, self.owner, folder)
   2849         if ret:
   2850             self._hydrate()

C:\Program Files\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\lib\site-packages\arcgis\_impl\portalpy.py in update_item(self, itemid, item_properties, data, thumbnail, metadata, owner, folder)
   2014         files = []
   2015         if data:
-> 2016             if _is_http_url(data):
   2017                 data = request.urlretrieve(data)[0]
   2018             files.append(('file', data, os.path.basename(data)))

C:\Program Files\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\lib\site-packages\arcgis\_impl\connection.py in _is_http_url(url)
   1084 def _is_http_url(url):
   1085     if url:
-> 1086         return urlparse(url).scheme in ['http', 'https']
   1087
   1088 def _unpack(obj_or_seq, key=None, flatten=False):

C:\Program Files\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\lib\urllib\parse.py in urlparse(url, scheme, allow_fragments)
    290     Note that we don't break the components up in smaller bits
    291     (e.g. netloc is a single string) and we don't expand % escapes."""
--> 292     url, scheme, _coerce_result = _coerce_args(url, scheme)
    293     splitresult = urlsplit(url, scheme, allow_fragments)
    294     scheme, netloc, url, query, fragment = splitresult

C:\Program Files\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\lib\urllib\parse.py in _coerce_args(*args)
    110     if str_input:
    111         return args + (_noop,)
--> 112     return _decode_args(args) + (_encode_result,)
    113
    114 # Result objects are more helpful than simple tuples

C:\Program Files\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\lib\urllib\parse.py in _decode_args(args, encoding, errors)
     94 def _decode_args(args, encoding=_implicit_encoding,
     95                        errors=_implicit_errors):
---> 96     return tuple(x.decode(encoding, errors) if x else '' for x in args)
     97
     98 def _coerce_args(*args):

C:\Program Files\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\lib\urllib\parse.py in <genexpr>(.0)
     94 def _decode_args(args, encoding=_implicit_encoding,
     95                        errors=_implicit_errors):
---> 96     return tuple(x.decode(encoding, errors) if x else '' for x in args)
     97
     98 def _coerce_args(*args):

AttributeError: 'dict' object has no attribute 'decode'

Outcomes