Select to view content in your preferred language

Enabling Sync via Arcgis python code

145
2
a week ago
Rahul_Pandia
Occasional Contributor

I want to enable sync in the webmap !
My workflow is :
1. Create a zip of a gdb
2. Upload the zip
3. Extract and create the hosted feature layers
4. Enable Editor tracking on hosted feature layer
5. enable sync in the hosted feature layer
6. Create a Webmap
7. Add hosted layers to the webmap
5. enable sync in the webmap
Here is my code :

import arcpy
import os
import zipfile
from arcgis.gis import GIS
from datetime import datetime
from arcgis.features import FeatureLayerCollection
import time
import pandas as pd
from arcgis.map import Map
import re
import json

def get_always_hidden_fields(df, sheet_name):
if not {'Section', 'Name', 'AlwaysHidden'}.issubset(df.columns):
return []

hidden_rows = df[df['AlwaysHidden'] == 1]
return [f"{sheet_name}: {row['Name']}=1" for _, row in hidden_rows.iterrows()]

def build_fields_to_hide_dict(excel_path):
all_sheets = pd.read_excel(excel_path, sheet_name=None)
field_map = {}

for sheet_name, df in all_sheets.items():
hidden_fields = get_always_hidden_fields(df, sheet_name)
for item in hidden_fields:
try:
section, rest = item.split(":")
field_name, _ = rest.strip().split("=")
section = section.strip()
field_name = field_name.strip()

if section not in field_map:
field_map[section] = set()
field_map[section].add(field_name)
except Exception as e:
print(f"Skipping malformed entry '{item}': {e}")
return field_map

def hide_fields_in_layers(flc, field_map):
for lyr in flc.layers + flc.tables:
layer_name = lyr.properties.name

if layer_name not in field_map:
continue

_fields_to_hide = field_map[layer_name]
fields_definition_update = []

for field in lyr.properties.fields:
if field["name"] in _fields_to_hide:
fields_definition_update.append({
"name": field["name"],
"visible": False
})

if fields_definition_update:
update_payload = {"fields": fields_definition_update}
lyr.manager.update_definition(update_payload)
print(f"Updated visibility in: {layer_name}")
else:
print(f"No matching fields to hide in: {layer_name}")

# Function to list all feature classes and tables in the given geodatabase
def list_gdb_contents_detail(workspace):
arcpy.env.workspace = workspace
list_Paths = []
list_types = []

# List feature datasets and add empty string for root-level feature classes
datasets = arcpy.ListDatasets(feature_type='feature')
datasets = [''] + datasets if datasets is not None else []

# Loop through feature datasets and collect feature classes
for ds in datasets:
for fc in arcpy.ListFeatureClasses(feature_dataset=ds):
path = os.path.join(arcpy.env.workspace, ds, fc)
list_Paths.append(path)
list_types.append('Fc')

# List tables in the geodatabase
tables = arcpy.ListTables()
for table in tables:
list_Paths.append(table)
list_types.append('Tables')

return list_Paths, list_types

# Function to zip the given file geodatabase with a timestamp
def zipGDB(inputGDB):
gdbFile = str(inputGDB)
timestamp = datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
outFile = gdbFile[0:-4] + '_' + timestamp + '.zip'
gdbName = os.path.basename(gdbFile)

# Create a zip archive excluding .lock files
with zipfile.ZipFile(outFile, mode='w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) as myzip:
for f in os.listdir(gdbFile):
if f[-5:] != '.lock':
myzip.write(os.path.join(gdbFile, f), gdbName + '\\' + os.path.basename(f))

return str(outFile)
def enable_sync_in_webmap_layers(webmap_item_id, gis):
try:
webmap = gis.content.get(webmap_item_id)
if not webmap:
print(f"Web map with ID '{webmap_item_id}' not found.")
return

#webmap = gis.content.get(webmap_item_id)
webmap_dict = webmap.get_data()
operational_layers = webmap_dict.get("operationalLayers", [])
tables = webmap_dict.get("tables", [])
all_layers = operational_layers + tables
token = gis._con.token
#print(str(token))
processed_service_urls = set()

for layer in all_layers:
layer_url = layer.get("url")
if not layer_url:
continue

# Remove the trailing /layerId to get the base service URL
service_url = re.sub(r"/\d+$", "", layer_url)

if service_url in processed_service_urls:
continue # Already processed

processed_service_urls.add(service_url)

try:
# Get the service item using the URL
flc = FeatureLayerCollection(service_url, gis)
#capabilities = flc.properties.get("capabilities", "")

# If service requires token and you are authenticated, this will succeed
capabilities = flc.properties.get("capabilities", "")
if "Sync" not in capabilities:
new_capabilities = ",".join(sorted(set(capabilities.split(",") + ["Sync"])))
flc.manager.update_definition({"capabilities": new_capabilities})
print(f" Enabled Sync for: {service_url}")
else:
print(f" Sync already enabled for: {service_url}")

except Exception as inner_err:
print(f" Failed to enable sync for service: {service_url}\n Error: {inner_err}")

except Exception as e:
print(f"Error processing web map: {e}")

def enable_webmap_offline(webmap_item_id, gis):
try:
webmap_item = gis.content.get(webmap_item_id)
if not webmap_item:
print(f"Web map with ID '{webmap_item_id}' not found.")
return

# Check current offline status
item_properties = webmap_item.get_data()
if item_properties.get("offlineEnabled", False):
print(f"Web map '{webmap_item.title}' is already offline-enabled.")
return

# Update the item with offlineEnabled flag
update_dict = {
"text": json.dumps({**item_properties, "offlineEnabled": True})
}

webmap_item.update(item_properties=update_dict)
print(f"Web map '{webmap_item.title}' is now enabled for offline use.")

except Exception as e:
print(f"Failed to enable offline for Web Map: {e}")

if __name__ == "__main__":

# --- Configuration ---
_gdb_path = r"F:\test\10\PIT_v9.gdb"
_username = "test"
_password = "test@123"
_url = "https://test.maps.arcgis.com/"
_folder_name = "Test-Rahul"
_excel_file_path = r"F:\test\10\test_datamodel_def.xlsx"
_proj_name= "test"

field_map = build_fields_to_hide_dict(_excel_file_path)
# Record script start time
start_time = datetime.now()

# List feature classes and tables in GDB
list_Paths, list_types = list_gdb_contents_detail(_gdb_path)

# Fields for editor tracking
creator_field = "created_user"
creation_date_field = "created_date"
last_editor_field = "last_edited_user"
last_edit_date_field = "last_edited_date"

# Enable editor tracking and add GPS metadata fields
for path, typ in zip(list_Paths, list_types):
arcpy.management.EnableEditorTracking(path, creator_field, creation_date_field, last_editor_field, last_edit_date_field, "ADD_FIELDS", "UTC")
if typ == 'Fc':
arcpy.management.AddGPSMetadataFields(path)

print("GDB has been updated with Editor Tracking & GPS Fields")

# Zip the geodatabase
zipped_path = zipGDB(_gdb_path)
print("GDB has been Zipped at :" + str(zipped_path))

# Connect to ArcGIS Online
gis = GIS(_url, _username, _password)
print("Connected to Arcgis Online")

zip_name = os.path.basename(zipped_path)
new_name = zip_name.replace(".zip", "")
user = gis.users.me

# Check if the folder exists, if not, create it
existing_folders = [f.name for f in user.folders]
if _folder_name not in existing_folders:
gis.content.create_folder(folder=_folder_name)

print(f"Uploading {zip_name}...")
today = datetime.now().strftime("%d %B %Y")
# Upload the zipped GDB to the specified folder
target_folder = next((f for f in user.folders if f.name == _folder_name), None)
uploaded_item = target_folder.add(
item_properties={
'title': zip_name,
'type': 'File Geodatabase',
'tags': 'file geodatabase, gdb',
'description': 'Uploaded template file geodatabase for ' +str(_proj_name) +' on '+str(today)
},
file=zipped_path
).result()

print(f"Successfully uploaded {zip_name} with item ID: {uploaded_item.id}")

# Publish the uploaded GDB as a hosted feature layer
print("Extracting file geodatabase...")
extracted_items = uploaded_item.publish()

# Update the item title
item = gis.content.get(extracted_items.id)
item.update({"title": new_name})
print("Hosted Feature Layer Id..." + str(extracted_items.id))
time.sleep(3)
feature_layer_item = gis.content.get(str(extracted_items.id))
flc = FeatureLayerCollection.fromitem(feature_layer_item)

# Enable editor tracking in the hosted feature layer
edit_dict = {
"hasStaticData": False,
"capabilities": "Query, Editing, Create, Update, Delete, ChangeTracking",
"editorTrackingInfo": {
"enableEditorTracking": True,
"enableOwnershipAccessControl": False,
"allowOthersToUpdate": True,
"allowOthersToDelete": True,
"allowOthersToQuery": True,
"allowAnonymousToUpdate": True,
"allowAnonymousToDelete": True
}
}
flc.manager.update_definition(edit_dict)
print(f"Editor tracking enabled successfully!")

sync_result = flc.manager.update_definition({"syncEnabled": True})
if sync_result.get("success"):
print(f"Sync enabled for hosted layer: {new_name}")
else:
print(f"Failed to enable sync for hosted layer: {new_name}")
print("Result:", sync_result)
"""# Enable sync settings for offline use
update_dict = {
"syncEnabled": True,
"syncCapabilities": {
"supportsAsync": True,
"supportsRegisteringExistingData": True,
"supportsSyncDirectionControl": True,
"supportsPerLayerSync": True,
"supportsPerReplicaSync": False,
"supportsRollbackOnFailure": False,
"supportsAttachmentsSyncDirection": True
}
}
flc.manager.update_definition(update_dict)
print(f"Successfully enabled offline use for {item.title}")
time.sleep(5)
update_dict = {
"syncEnabled": True,
"syncCapabilities": {
"supportsAsync": True,
"supportsRegisteringExistingData": True,
"supportsSyncDirectionControl": True,
"supportsPerLayerSync": True,
"supportsPerReplicaSync": False,
"supportsRollbackOnFailure": False,
"supportsAttachmentsSyncDirection": True
}
}
flc.manager.update_definition(update_dict)"""
print("Sync enabled successfully!")

# --------- Update Individual Layers ---------
print("\nUpdating individual layers...")
for lyr in item.layers:
print(f"Updating layer: {lyr.properties.name}")
layer_def = {
"editingInfo": {
"enableEditorTracking": True,
"enableOwnershipAccessControl": False
}
}
res = lyr.manager.update_definition(layer_def)
print(" -> Layer update result:", res)
# --------- Update Individual Tables ---------
print("\nUpdating individual tables...")
for tbl in item.tables:
print(f"Updating table: {tbl.properties.name}")
table_def = {
"editingInfo": {
"enableEditorTracking": True,
"enableOwnershipAccessControl": False
}
}
res = tbl.manager.update_definition(table_def)
print(" -> Table update result:", res)

# --------- Final Status ---------
print("\nSync enabled:", flc.properties.syncEnabled)
hide_fields_in_layers(flc, field_map)
# Pause to allow server processes to complete
time.sleep(5)

# Create a new web map with the uploaded hosted feature layer
webmap = gis.map()
#webmap.basemap.basemap = "arcgis-light-gray"
webmap.basemap.basemap = "arcgis-streets"

webmap_properties = {
"title": "Web_Map_" + str(new_name),
"snippet": "Test",
"tags": ["webmap"],
"description": 'The webmap is for '+str(_proj_name)+' created on '+str(today)+' to serve as a template'
}


# Add layers and tables to the web map
for lyr in feature_layer_item.layers:
webmap.content.add(lyr)

for tbl in feature_layer_item.tables:
webmap.content.add(tbl)

# Save the web map and move it to the desired folder
webmap_item = webmap.save(item_properties=webmap_properties)
if _folder_name:
item = gis.content.get(webmap_item.id)
item.move(_folder_name)

print(f"Web map created successfully: {webmap_item.title} (ID: {webmap_item.id})")
time.sleep(5)
enable_sync_in_webmap_layers(webmap_item.id, gis)
time.sleep(5)
enable_webmap_offline(webmap_item.id, gis)


# Calculate and print the total duration
end_time = datetime.now()
duration = end_time - start_time
hours, remainder = divmod(duration.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
formatted_diff = f"{hours:02}:{minutes:02}:{seconds:02}"
print("Duration : ", formatted_diff)
print("End")



There are no errors but sync are not getting enabled in the webmap. Refer to the screen shot:

Rahul_Pandia_0-1754577679230.png

 Please let me know how to resolve this issue.


 

 

2 Replies
Jeremiah_Martin
New Contributor

I think you need to look at your content.  It says there are errors before it can be used in offline mode.

0 Kudos
Clubdebambos
MVP Regular Contributor

Hi @Rahul_Pandia,

You don't set the sync capability to a WebMap, you set it for the layers (the Feature Layers / Feature Service). Your Asset layer needs to be sync enabled.

Add Sync to your capabilities...

"capabilities": "Query, Editing, Create, Update, Delete, Sync, ChangeTracking"

 

Can you please also use the code snippet format to make the code easier to read?

Select the ellipsis (...)

Clubdebambos_0-1754983619722.png

Select Insert/Edit code sample...

Clubdebambos_1-1754983667519.png

Select Python from the language dropdown

Clubdebambos_2-1754983713597.png

All the best,

Glen

~ learn.finaldraftmapping.com
0 Kudos