Hello everyone,
I have a .pyt toolbox which runs correctly in the ArcGIS Pro 3.5 environment but fails when the tool is hosted on ArcGIS Enterprise 11.3. The tool uses arcpy and the arcgis api for python to retrieve a hosted webmap, included layers and hosted layout, all of which are available to all users within the organization.
The tool uses gis = GIS("home") when run inside of ArcGIS Pro. However, when run in a hosted environment, I receive the the following errors:
{"error":{"code":499,"message":"Token Required","details":[]}}
RuntimeError: Failed to create layer from service at https://portal_url.com/gisweb/rest/services/Hosted/feature_layer/FeatureServer/1.\n"
I assume this is related to user authentication/credentials, but I cannot figure out how to properly pass credentials when hosting. Should I be using keyring to do so? If so, what is the proper way of setting it up? Does the .pyt toolbox need to be run from the server machine in ArcGIS Pro, or can it be run on my local computer?
Below is the execute() portion of the .pyt toolbox. Any insights you may have into how to properly pass API credentials when hosting a .pyt toolbox on enterprise would be greatly appreciated.
"""
def execute(self, parameters, messages):
parcel_apn = parameters[0].valueAsText
output_pdf = parameters[1].valueAsText
web_map_id = "a4373271ba30412939sh23201h8h282"
layout_id = "btheh82hek930jj3932429je9302030"
attributes = ["SHAPE@", "apn", "property_name"]
parcels = [parcel_apn]
gis = GIS("home")
webmap_item = gis.content.get(web_map_id)
arcpy.AddMessage(webmap_item)
web_map_json = webmap_item.get_data()
arcpy.AddMessage("")
arcpy.AddMessage("")
pretty_json = json.dumps(web_map_json, indent=4)
arcpy.AddMessage(pretty_json)
web_map_json.update(json.loads('''{"mapOptions" : { "extent" : { "xmin":6787761, "ymin":2073010, "xmax":6787762, "ymax":2073011, "spatialReference" : { "wkid" : 6418 } }}}'''))
pagx_path = os.path.join(arcpy.env.scratchFolder, "Report.pagx")
layout_item = gis.content.get(layout_id)
layout_item.download(pagx_path)
result = arcpy.mp.ConvertWebMapToArcGISProject(json.dumps(web_map_json), template_pagx=fr"{pagx_path}\Property_Report_1.pagx")
aprx = result.ArcGISProject
map = aprx.listMaps("Web Map")[0]
layer = map.listLayers("Properties")[0]
layout = aprx.listLayouts("Property Report")[0]
map_frame = layout.listElements("MAPFRAME_ELEMENT", "Property Map Frame")[0]
arcpy.AddMessage(map_frame.name)
property_name_text = layout.listElements("TEXT_ELEMENT", "Property Name")[0]
apn_text = layout.listElements("TEXT_ELEMENT", "APN")[0]
for parcel in parcels:
arcpy.management.SelectLayerByAttribute(layer, "NEW_SELECTION", where_clause=f"apn = '{parcel}'")
with arcpy.da.SearchCursor(layer, attributes, where_clause=f"apn = '{parcel}'") as cursor:
for row in cursor:
extent = row[0].extent
apn = row[1]
property_name = row[2]
# Get Parcel Layer Extent and Pad it by 20-Percent in the Active Map Frame
pad = 0.20
width_pad = extent.width * pad
height_pad = extent.height * pad
new_xmin = (extent.XMin - width_pad)
new_xmax = (extent.XMax + width_pad)
new_ymin = (extent.YMin - height_pad)
new_ymax = (extent.YMax + height_pad)
padded_extent = arcpy.Extent(new_xmin, new_ymin, new_xmax, new_ymax, spatial_reference=extent.spatialReference)
map_frame.camera.setExtent(padded_extent)
property_name_text.text = f"{property_name}"
apn_text.text = f"APN: {apn}"
layer.definitionQuery = f"APN = '{parcel}'"
layout.exportToPDF(output_pdf)
return
"""