I am trying to create a phyton script to create a geoprocessing tool to Publish and overwrite web layers within ModelBuilder. I am referring to the instructions within this blog post:
The issue that I’m having is the script provided in the link above contains the input “map” and what I would like to select is an input “layer” in the content pane.
Jonah Lay has provided sample code within the comments of the blog post here: https://pro.arcgis.com/en/pro-app/latest/arcpy/sharing/featuresharingdraft-class.htm#GUID-8E27A3ED-A...
The issue is when I try to use that code I continue to receive errors. Is there someone that could help debug this issue? I have zero coding experience.
It would be helpful if you posted what code you're using, (see formatting guide here) as well as your error messages.
Hi AlfredBaldenweck,
This is the code I’m using:
import arcpy
import os
import xml.dom.minidom as DOM
def ScriptTool(map, service, summary, tags, description, overwriteService, portalFolder, timezone, federatedServerUrl, share_public, share_organization, share_groups, outdir):
"""ScriptTool function docstring"""
# Set output file names
sddraft_filename = service + ".sddraft"
sddraft_output_filename = os.path.join(outdir, sddraft_filename)
# Reference layers to publish
aprx = arcpy.mp.ArcGISProject("CURRENT")
m = aprx.listlayers('CURRENT')[0]
selected_layer = m.listLayers()[0]
# Create MapImageSharingDraft and set service properties
server_type = "FEDERATED_SERVER"
federated_server_url = federatedServerUrl
sharing_draft = m.getWebLayerSharingDraft(server_type, "MAP_IMAGE", service)
sharing_draft.federatedServerUrl = federated_server_url
sharing_draft.summary = summary
sharing_draft.tags = tags
sharing_draft.description = description
sharing_draft.credits = "My Credits"
sharing_draft.useLimitations = "My Use Limitations"
sharing_draft.overwriteExistingService = overwriteService
sharing_draft.portalFolder = portalFolder
# Create Service Definition Draft file
sharing_draft.exportToSDDraft(sddraft_output_filename)
outsddraft = sddraft_output_filename
arcpy.AddMessage("Service definition draft created")
# Set time zone
if (timezone != ""):
property_set = [{
"key": "dateFieldsRespectsDayLightSavingTime",
"value": "true"
},
{
"key": "dateFieldsTimezoneID",
"value": timezone
}]
SetTimezone(sddraft_output_filename, property_set=property_set)
# Create Service Definition file
sd_filename = service + ".sd"
sd_output_filename = os.path.join(outdir, sd_filename)
arcpy.StageService_server(sddraft_output_filename, sd_output_filename)
arcpy.AddMessage("Service definition created")
# Share to portal
output = arcpy.UploadServiceDefinition_server(sd_output_filename, federated_server_url,
in_override="OVERRIDE_DEFINITION", in_public=share_public,
in_organization=share_organization, in_groups=share_groups)
arcpy.AddMessage("Service published")
return output[5]
def SetTimezone(sddraftPath, property_set):
# Read the sddraft xml.
doc = DOM.parse(sddraftPath)
# Find all elements named TypeName. This is where the server object extension
# (SOE) names are defined.
typeNames = doc.getElementsByTagName('TypeName')
for typeName in typeNames:
# Get the TypeName to enable
if typeName.firstChild.data == "MapServer":
extension = typeName.parentNode
# prp = extension.childNodes.getElementsByTagNameNS('PropertyArray')
for extElement in extension.childNodes:
if extElement.tagName == 'Definition':
for definition in extElement.childNodes:
if definition.tagName == 'ConfigurationProperties':
for config_prop in definition.childNodes:
if config_prop.tagName == 'PropertyArray':
for prop in property_set:
prop_set = doc.createElement("PropertySetProperty")
attr = doc.createAttribute("xsi:type")
attr.value = "typens:PropertySetProperty"
prop_set.setAttributeNode(attr)
prop_key = doc.createElement("Key")
txt = doc.createTextNode(prop["key"])
prop_key.appendChild(txt)
prop_set.appendChild(prop_key)
prop_value = doc.createElement("Value")
attr = doc.createAttribute("xsi:type")
attr.value = "xs:string"
prop_value.setAttributeNode(attr)
txt = doc.createTextNode(prop["value"])
prop_value.appendChild(txt)
prop_set.appendChild(prop_value)
config_prop.appendChild(prop_set)
# Write to the .sddraft file
f = open(sddraftPath, 'w')
doc.writexml(f)
f.close()
arcpy.AddMessage("Time zone set")
return
if __name__ == '__main__':
# ScriptTool parameters
map = arcpy.GetParameter(0)
service = arcpy.GetParameterAsText(1)
summary = arcpy.GetParameterAsText(2)
tags = arcpy.GetParameterAsText(3)
description = arcpy.GetParameterAsText(4)
overwriteService = arcpy.GetParameter(5)
portalFolder = arcpy.GetParameterAsText(6)
timezone = arcpy.GetParameterAsText(7)
federatedServerUrl = arcpy.GetParameterAsText(8)
share_public = arcpy.GetParameterAsText(9)
share_organization = arcpy.GetParameterAsText(10)
share_groups = arcpy.GetParameterAsText(11)
outdir = arcpy.GetParameterAsText(12)
rest_endpoint = ScriptTool(map, service, summary, tags, description, overwriteService, portalFolder, timezone, federatedServerUrl, share_public, share_organization, share_groups, outdir)
arcpy.SetParameterAsText(1, rest_endpoint)
arcpy.AddMessage(rest_endpoint)
and this is the error:
That's an easy fix so far.
Basically that line (#13) is out of line with what's around it. Make it line up with lines 12 and 14 and that particular error will go away.
Python recognizes things like loops or parts of functions by the indentation.
def funct():
# Everything spaced here belongs to funct
#This is a different thing
def funct3():
# This belongs to funct3
exList = [1,2,3]
for e in exList:
# Everything in here belongs to this loop
# Then when that's done, I can continue on with what I was doing
Depending on what you're writing in (example Notepad++), there should be a setting to show you indentation lines/ white spaces.
Oops, I published that before I should have.
Make sure when creating that draft that you specify the layer(s) you want.
sharing_draft = m.getWebLayerSharingDraft(server_type, "MAP_IMAGE", service, [selected_layer])
You should also make sure that that's actually the layer you want to use; right now you're just grabbing the top layer. If you specify by name, you should be able to grab exactly the right one.
selected_layer = m.listLayers("Grazing Allotments 2023")[0]
Also, you're specifying a Map Image. Is that the type of service you want?
Thanks @AlfredBaldenweck , you've identified I wasn't using the correct sample code. (I'm trying to overwrite a weblayer) I've attached the new code and fixed the code that wasn't in line. I've also attached my new error but I'm feeling the solution is around specifying the layers I want. I'm just not sure what line I need to add that code into?
import arcpy
import os
import xml.dom.minidom as DOM
def ScriptTool(map, service, summary, tags, description, overwriteService, enableEditing, enableSync, enableWFS, timezone, share_public, share_organization, share_groups, outdir):
"""ScriptTool function docstring"""
# Set output file names
sddraft_filename = service + ".sddraft"
sddraft_output_filename = os.path.join(outdir, sddraft_filename)
# Reference layers to publish
aprx = arcpy.mp.ArcGISProject("CURRENT")
m = aprx.listlayers('CURRENT')[0]
selected_layer = m.listLayers()[0]
# Create FeatureSharingDraft and set service properties
sharing_draft = m.getWebLayerSharingDraft("HOSTING_SERVER", "FEATURE", service)
sharing_draft.summary = summary
sharing_draft.tags = tags
sharing_draft.description = description
sharing_draft.credits = "My Credits"
sharing_draft.useLimitations = "My Use Limitations"
sharing_draft.overwriteExistingService = overwriteService
# Create Service Definition Draft file
sharing_draft.exportToSDDraft(sddraft_output_filename)
outsddraft = sddraft_output_filename
arcpy.AddMessage("Service definition draft created")
# Modify capabilities
if enableEditing or enableSync:
ModifyCapabilities(sddraft_output_filename, enableEditing, enableSync)
if enableWFS:
EnableWFS(sddraft_output_filename)
# Set time zone
if(timezone != ""):
property_set = [{
"key": "dateFieldsRespectsDayLightSavingTime",
"value": "true"
},
{
"key": "dateFieldsTimezoneID",
"value": timezone
}]
SetTimezone(sddraft_output_filename, property_set=property_set)
# Create Service Definition file
sd_filename = service + ".sd"
sd_output_filename = os.path.join(outdir, sd_filename)
arcpy.StageService_server(sddraft_output_filename, sd_output_filename)
arcpy.AddMessage("Service definition created")
# Upload to portal
output = arcpy.UploadServiceDefinition_server(sd_output_filename, "My Hosted Services", in_override="OVERRIDE_DEFINITION", in_public=share_public, in_organization=share_organization, in_groups=share_groups)
arcpy.AddMessage("Service published")
return output[5]
def ModifyCapabilities(sddraft_output_filename, enableEditing, enableSync):
capabilities = "Query"
if enableEditing:
capabilities += ",Create,Delete,Update,Editing"
if enableSync:
capabilities += ",Sync"
# Modify feature layer capabilities to enable Create and Sync
doc = DOM.parse(sddraft_output_filename)
typeNames = doc.getElementsByTagName('TypeName')
for typeName in typeNames:
if typeName.firstChild.data == "FeatureServer":
extension = typeName.parentNode
for extElement in extension.childNodes:
if extElement.tagName == 'Definition':
for propArray in extElement.childNodes:
if propArray.tagName == 'Info':
for propSet in propArray.childNodes:
for prop in propSet.childNodes:
for prop1 in prop.childNodes:
if prop1.tagName == "Key":
if prop1.firstChild.data == 'webCapabilities':
if prop1.nextSibling.hasChildNodes():
prop1.nextSibling.firstChild.data = capabilities
else:
txt = doc.createTextNode(capabilities)
prop1.nextSibling.appendChild(txt)
# Write to the .sddraft file
f = open(sddraft_output_filename, 'w')
doc.writexml(f)
f.close()
arcpy.AddMessage("Capabilities updated")
return
def EnableWFS(sddraft_output_filename):
doc = DOM.parse(sddraft_output_filename)
typeNames = doc.getElementsByTagName('TypeName')
for typeName in typeNames:
# Get the TypeName to enable
if typeName.firstChild.data == "EnableWFSServer":
extension = typeName.parentNode
for extElement in extension.childNodes:
# Enable feature access
if extElement.tagName == 'Enabled':
extElement.firstChild.data = 'true'
# Write to the .sddraft file
f = open(sddraft_output_filename, 'w')
doc.writexml(f)
f.close()
arcpy.AddMessage("WFS set")
return
def SetTimezone(sddraftPath, property_set):
# Read the sddraft xml
doc = DOM.parse(sddraftPath)
# Find all elements named TypeName. This is where the server object extension
# (SOE) names are defined.
typeNames = doc.getElementsByTagName('TypeName')
for typeName in typeNames:
# Get the TypeName to enable
if typeName.firstChild.data == "MapServer":
extension = typeName.parentNode
# prp = extension.childNodes.getElementsByTagNameNS('PropertyArray')
for extElement in extension.childNodes:
if extElement.tagName == 'Definition':
for definition in extElement.childNodes:
if definition.tagName == 'ConfigurationProperties':
for config_prop in definition.childNodes:
if config_prop.tagName == 'PropertyArray':
for prop in property_set:
prop_set = doc.createElement("PropertySetProperty")
attr = doc.createAttribute("xsi:type")
attr.value = "typens:PropertySetProperty"
prop_set.setAttributeNode(attr)
prop_key = doc.createElement("Key")
txt = doc.createTextNode(prop["key"])
prop_key.appendChild(txt)
prop_set.appendChild(prop_key)
prop_value = doc.createElement("Value")
attr = doc.createAttribute("xsi:type")
attr.value = "xs:string"
prop_value.setAttributeNode(attr)
txt = doc.createTextNode(prop["value"])
prop_value.appendChild(txt)
prop_set.appendChild(prop_value)
config_prop.appendChild(prop_set)
# Write to the .sddraft file
f = open(sddraftPath, 'w')
doc.writexml(f)
f.close()
arcpy.AddMessage("Timezone set")
return
if __name__ == '__main__':
# ScriptTool parameters
map = arcpy.GetParameter(0)
service = arcpy.GetParameterAsText(1)
summary = arcpy.GetParameterAsText(2)
tags = arcpy.GetParameterAsText(3)
description = arcpy.GetParameterAsText(4)
overwriteService = arcpy.GetParameter(5)
enableEditing = arcpy.GetParameter(6)
enableSync = arcpy.GetParameter(7)
enableWFS = arcpy.GetParameter(8)
timezone = arcpy.GetParameterAsText(9)
share_public = arcpy.GetParameterAsText(10)
share_organization = arcpy.GetParameterAsText(11)
share_groups = arcpy.GetParameterAsText(12)
outdir = arcpy.GetParameterAsText(13)
rest_endpoint = ScriptTool(map, service, summary, tags, description, overwriteService, enableEditing, enableSync, enableWFS, timezone, share_public, share_organization, share_groups, outdir)
arcpy.SetParameterAsText(1, rest_endpoint)
arcpy.AddMessage(rest_endpoint)
Error Message:
Publish Web Feature Layer
=====================
Parameters
Layer MODIS 15km Buffer Current Day
Web Layer Name Draft
Summary
Tags
Description
Overwrite Existing Web Layer true
Enable Editing
Enable Sync
Enable WFS
Time zone
Share With Everyone PRIVATE
Share With Organization SHARE_ORGANIZATION
Share With Groups
Output Directory S:\GIS\Projects\GIS0000\GIS0014_PECL_Wildfire_Webmap\GIS0014_MODIS_HotSpot_Buffer\output
Rest Endpoint
=====================
Messages
Start Time: Monday, June 12, 2023 4:09:11 PM
Traceback (most recent call last):
File "S:\GIS\Projects\GIS0000\GIS0014_PECL_Wildfire_Webmap\Python\overwriteweblay.py", line 175, in <module>
rest_endpoint = ScriptTool(map, service, summary, tags, description, overwriteService, enableEditing, enableSync, enableWFS, timezone, share_public, share_organization, share_groups, outdir)
File "S:\GIS\Projects\GIS0000\GIS0014_PECL_Wildfire_Webmap\Python\overwriteweblay.py", line 14, in ScriptTool
m = aprx.listlayers('CURRENT')[0]
AttributeError: 'ArcGISProject' object has no attribute 'listlayers'
Failed script Publish Web Feature Layer...
Failed to execute (PublishWebFeatureLayer).
Failed at Monday, June 12, 2023 4:09:13 PM (Elapsed Time: 2.00 seconds)
Oops, I didn't catch that before.
Line 14 should be
m = aprx.listMaps("Map")[0]
# OR
m = aprx.activeMap
(change to suit your needs).
I'm slowly getting there @AlfredBaldenweck I don't want it to overwrite an entire map just a single feature layer. This is my updated code:
import arcpy
import os
import xml.dom.minidom as DOM
def ScriptTool( layer, service, summary, tags, description, overwriteService, enableEditing, enableSync, enableWFS, timezone, share_public, share_organization, share_groups, outdir):
"""ScriptTool function docstring"""
# Set output file names
sddraft_filename = service + ".sddraft"
sddraft_output_filename = os.path.join(outdir, sddraft_filename)
# Reference layers to publish
aprx = arcpy.mp.ArcGISProject("CURRENT")
m = aprx.listLayers("layer")[0]
selected_layer = m.listLayer()[0]
# Create FeatureSharingDraft and set service properties
sharing_draft = m.getWebLayerSharingDraft("HOSTING_SERVER", "FEATURE", service)
sharing_draft.summary = summary
sharing_draft.tags = tags
sharing_draft.description = description
sharing_draft.credits = "My Credits"
sharing_draft.useLimitations = "My Use Limitations"
sharing_draft.overwriteExistingService = overwriteService
# Create Service Definition Draft file
sharing_draft.exportToSDDraft(sddraft_output_filename)
outsddraft = sddraft_output_filename
arcpy.AddMessage("Service definition draft created")
# Modify capabilities
if enableEditing or enableSync:
ModifyCapabilities(sddraft_output_filename, enableEditing, enableSync)
if enableWFS:
EnableWFS(sddraft_output_filename)
# Set time zone
if(timezone != ""):
property_set = [{
"key": "dateFieldsRespectsDayLightSavingTime",
"value": "true"
},
{
"key": "dateFieldsTimezoneID",
"value": timezone
}]
SetTimezone(sddraft_output_filename, property_set=property_set)
# Create Service Definition file
sd_filename = service + ".sd"
sd_output_filename = os.path.join(outdir, sd_filename)
arcpy.StageService_server(sddraft_output_filename, sd_output_filename)
arcpy.AddMessage("Service definition created")
# Upload to portal
output = arcpy.UploadServiceDefinition_server(sd_output_filename, "My Hosted Services", in_override="OVERRIDE_DEFINITION", in_public=share_public, in_organization=share_organization, in_groups=share_groups)
arcpy.AddMessage("Service published")
return output[5]
def ModifyCapabilities(sddraft_output_filename, enableEditing, enableSync):
capabilities = "Query"
if enableEditing:
capabilities += ",Create,Delete,Update,Editing"
if enableSync:
capabilities += ",Sync"
# Modify feature layer capabilities to enable Create and Sync
doc = DOM.parse(sddraft_output_filename)
typeNames = doc.getElementsByTagName('TypeName')
for typeName in typeNames:
if typeName.firstChild.data == "FeatureServer":
extension = typeName.parentNode
for extElement in extension.childNodes:
if extElement.tagName == 'Definition':
for propArray in extElement.childNodes:
if propArray.tagName == 'Info':
for propSet in propArray.childNodes:
for prop in propSet.childNodes:
for prop1 in prop.childNodes:
if prop1.tagName == "Key":
if prop1.firstChild.data == 'webCapabilities':
if prop1.nextSibling.hasChildNodes():
prop1.nextSibling.firstChild.data = capabilities
else:
txt = doc.createTextNode(capabilities)
prop1.nextSibling.appendChild(txt)
# Write to the .sddraft file
f = open(sddraft_output_filename, 'w')
doc.writexml(f)
f.close()
arcpy.AddMessage("Capabilities updated")
return
def EnableWFS(sddraft_output_filename):
doc = DOM.parse(sddraft_output_filename)
typeNames = doc.getElementsByTagName('TypeName')
for typeName in typeNames:
# Get the TypeName to enable
if typeName.firstChild.data == "EnableWFSServer":
extension = typeName.parentNode
for extElement in extension.childNodes:
# Enable feature access
if extElement.tagName == 'Enabled':
extElement.firstChild.data = 'true'
# Write to the .sddraft file
f = open(sddraft_output_filename, 'w')
doc.writexml(f)
f.close()
arcpy.AddMessage("WFS set")
return
def SetTimezone(sddraftPath, property_set):
# Read the sddraft xml
doc = DOM.parse(sddraftPath)
# Find all elements named TypeName. This is where the server object extension
# (SOE) names are defined.
typeNames = doc.getElementsByTagName('TypeName')
for typeName in typeNames:
# Get the TypeName to enable
if typeName.firstChild.data == "MapServer":
extension = typeName.parentNode
# prp = extension.childNodes.getElementsByTagNameNS('PropertyArray')
for extElement in extension.childNodes:
if extElement.tagName == 'Definition':
for definition in extElement.childNodes:
if definition.tagName == 'ConfigurationProperties':
for config_prop in definition.childNodes:
if config_prop.tagName == 'PropertyArray':
for prop in property_set:
prop_set = doc.createElement("PropertySetProperty")
attr = doc.createAttribute("xsi:type")
attr.value = "typens:PropertySetProperty"
prop_set.setAttributeNode(attr)
prop_key = doc.createElement("Key")
txt = doc.createTextNode(prop["key"])
prop_key.appendChild(txt)
prop_set.appendChild(prop_key)
prop_value = doc.createElement("Value")
attr = doc.createAttribute("xsi:type")
attr.value = "xs:string"
prop_value.setAttributeNode(attr)
txt = doc.createTextNode(prop["value"])
prop_value.appendChild(txt)
prop_set.appendChild(prop_value)
config_prop.appendChild(prop_set)
# Write to the .sddraft file
f = open(sddraftPath, 'w')
doc.writexml(f)
f.close()
arcpy.AddMessage("Timezone set")
return
if __name__ == '__main__':
# ScriptTool parameters
map = arcpy.GetParameter(0)
service = arcpy.GetParameterAsText(1)
summary = arcpy.GetParameterAsText(2)
tags = arcpy.GetParameterAsText(3)
description = arcpy.GetParameterAsText(4)
overwriteService = arcpy.GetParameter(5)
enableEditing = arcpy.GetParameter(6)
enableSync = arcpy.GetParameter(7)
enableWFS = arcpy.GetParameter(8)
timezone = arcpy.GetParameterAsText(9)
share_public = arcpy.GetParameterAsText(10)
share_organization = arcpy.GetParameterAsText(11)
share_groups = arcpy.GetParameterAsText(12)
outdir = arcpy.GetParameterAsText(13)
rest_endpoint = ScriptTool(layer, service, summary, tags, description, overwriteService, enableEditing, enableSync, enableWFS, timezone, share_public, share_organization, share_groups, outdir)
arcpy.SetParameterAsText(1, rest_endpoint)
arcpy.AddMessage(rest_endpoint)
and this is my new error:
Publish Web Feature Layer
=====================
Parameters
Layer MODIS 15km Buffer Current Day
Web Layer Name HotSpot Test
Summary
Tags
Description
Overwrite Existing Web Layer true
Enable Editing
Enable Sync
Enable WFS
Time zone
Share With Everyone PRIVATE
Share With Organization SHARE_ORGANIZATION
Share With Groups
Output Directory S:\GIS\Projects\GIS0000\GIS0014_PECL_Wildfire_Webmap\GIS0014_MODIS_HotSpot_Buffer\output
Rest Endpoint
=====================
Messages
Start Time: Monday, June 12, 2023 4:45:51 PM
Traceback (most recent call last):
File "S:\GIS\Projects\GIS0000\GIS0014_PECL_Wildfire_Webmap\Python\overwriteweblay.py", line 175, in <module>
rest_endpoint = ScriptTool( layer, service, summary, tags, description, overwriteService, enableEditing, enableSync, enableWFS, timezone, share_public, share_organization, share_groups, outdir)
NameError: name 'layer' is not defined
Failed script Publish Web Feature Layer...
Failed to execute (PublishWebFeatureLayer).
Failed at Monday, June 12, 2023 4:45:53 PM (Elapsed Time: 1.75 seconds)
It's confused because you changed "map" in line 175 to "layer", but you haven't told it what "layer" is. Change it back to "map"
To make sure you're only uploading the layer, not the map, you list the layer(s) in line 18
sharing_draft = m.getWebLayerSharingDraft(server_type, "MAP_IMAGE", service, [selected_layer])
You also still haven't fixed the error that's going to run in line 14.