|
POST
|
JSON backups Similarly, I also backup the content JSON files incase schema on a hosted layer gets changed or a webmap/dashboard/app gets inadvertently changed or broken. ESRI webmaps, dashboards, etc. have two parts to their JSON data. Description Data The Data is where all the customizations you’ve made to the content are stored. The script is set to download all the following JSON Data for the following item types in your Portal: Web Map Web Mapping Application Feature Layer Application Dashboard Web Experience item_types If a new item of that type is added to Portal, a new folder is created from the item name. If the content was modified in the last 24 hours, a new JSON file is saved. import arcgis
from arcgis import gis
from arcgis.gis import GIS
import json
import os
import glob
gis = arcgis.gis.GIS("https://yourorg.maps.arcgis.com", "username", "password")
import datetime
now = datetime.datetime.now()
dateString = now.strftime("%Y-%m-%d")
folder_location = r"C:\ArcGISOnline\BackupFiles\JSON"
item_types = { 'Web Map', 'Web Mapping Application', 'Feature Layer','Application', 'Dashboard', 'Web Experience'}
for itemType in item_types:
folder_name = itemType
subfolder_path = os.path.join(folder_location,folder_name)
try:
os.mkdir(subfolder_path)
except:
pass
print("\n"+itemType)
itemType_items = gis.content.search(query="*",item_type=itemType,max_items=10000)
listOfItemIDs = []
for item in itemType_items:
listOfItemIDs.append(item.id)
for iid in listOfItemIDs:
my_item = gis.content.get(iid)
item_title = my_item.title
removestring ="%:/,.\\[]<>*?$"
item_title = ''.join([c for c in item_title if c not in removestring]).strip()
file_name = item_title+"_"+itemType+"_"+str(dateString)+".json"
file_path_item_folder = os.path.join(subfolder_path ,item_title)
file_path = os.path.join(file_path_item_folder ,file_name)
item = my_item.get_data(try_json=True)
if len(item)>0: # only proceed if json dump is not empty
try:
#print(file_path_item_folder)
os.mkdir(file_path_item_folder)
except:
a=1
full_path = os.path.join(file_path_item_folder,file_name)
#get date of newest file in folder
list_of_files = glob.glob(file_path_item_folder+"\*")
if len(list_of_files)>0:
latest_file = max(list_of_files, key=os.path.getctime)
newestFileDate = os.path.getmtime(latest_file )
else:
newestFileDate = 0
#note: does not run on empty folders with no json exports already
if newestFileDate == 0 or my_item.modified/1000>newestFileDate : # if modified date newer than last json or no json in folder
print("\t"+item_title)
print(full_path)
with open (full_path, "w") as file_handle:
file_handle.write(json.dumps(item))
print("\n\nCompleted")
... View more
05-06-2024
09:48 AM
|
2
|
0
|
2591
|
|
POST
|
Geodatabase Backups I recommend adding a Tag to any content you want to backup. Then via Python script, you can download any content with that Tag to gdb and schedule the script (via Task Scheduler) to run daily or weekly as you need. I use the tag: GDBNightlyBackup import datetime
startTime = datetime.datetime.now()
TodaysDate = datetime.date.today().isoformat()
print (startTime)
##############
import arcgis
from arcgis.gis import GIS
import os
#enter AGOL sign-in credentials
gis = GIS("https://yourorg.maps.arcgis.com", "username", "password",verify_cert=False)
now = datetime.datetime.now()
folderName = now.strftime("%Y-%m-%d")
#print (folderName)
parent_dir = r"C:\ArcGISOnline\BackupFiles\FeatureLayers"
path = os.path.join(parent_dir, folderName)
#create folder if it doesn't exist
if os.path.isdir(path):
pass
else:
os.mkdir(path)
print("Directory '% s' created" % folderName)
def downloadItems(downloadFormat):
try:
download_items = [i for i in gis.content.search(query="tags: = 'GDBNightlyBackup'", item_type='Feature Layer',
max_items=-1)]
#print(download_items)
# Loop through each item and if equal to Feature service then download it
for item in download_items:
if item.type == 'Feature Service':
print(item)
result = item.export(f'{item.title}', downloadFormat)
#r'file path of where to store the download'
result.download(path)
# Delete the item after it downloads to save on space
result.delete()
except Exception as e:
print(e)
downloadItems(downloadFormat='File Geodatabase')
#############
endTime = datetime.datetime.now()
td = endTime - startTime
hours, remainder = divmod(td.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
TimeElapsed='{:02}:{:02}:{:02}'.format(int(hours), int(minutes), int(seconds))
print ("")
print ("Done!")
print ("")
print ("Ended at " + str(endTime))
print ("Time elapsed " + str(td)) The result is I have backup folders every day for all the hosted content I've been downloading.
... View more
05-06-2024
09:44 AM
|
5
|
1
|
2591
|
|
POST
|
Awesome 🎉 Don't get too frustrated as it gets easier the more you use Arcpy. The more code you write, the more you can "steal" ideas from your other scripts, or ESRI samples or GIS Stack Exchange, etc. Even ChatGPT sometimes can produce usable Esri Python code!
... View more
04-25-2024
12:31 PM
|
1
|
0
|
615
|
|
POST
|
Python IDLE is probably putting a lock on those mxds. If you close the Python results Window, you should be able to edit in ArcMap.
... View more
04-25-2024
12:15 PM
|
1
|
0
|
2379
|
|
POST
|
@AlfredBaldenweck is correct. If you're ever curious what a variable is you can type: print(type(mxd)) Also, the df part need to be tabbed over: from arcpy import env
# Set the workspace for the folder containing MXDs
arcpy.env.workspace = r"S:\Workgroups\Test"
mxdList = arcpy.ListFiles("*.mxd")
# Iterate through all MXDs in the workspace
for mxd in mxdList:
mxdpath = env.workspace + "\\" + mxd
print (mxd + "Is being processed")
mxdNext = arcpy.mapping.MapDocument(mxdpath)
for df in arcpy.mapping.ListDataFrames(mxdNext,"Layers"):
for lyr in arcpy.mapping.ListLayers(mxd, "Backup", df):
if lyr == "Backup":
lyr.replaceDataSource(r"S:\Workgroups", "SHAPEFILE_WORKSPACE", "Backup.shp",
r"S:\Workgroups\Data.gdb\Final", "FILEGDB_WROKSPACE")
if lyr.name == "Backup":
lyr.name = "Final"
print("lyr.name")
mxdNext.save()
arcpy.RefreshTOC() Otherwise it's only going to run on the last MXD in the folder, not all the MXDs.
... View more
04-25-2024
12:06 PM
|
1
|
0
|
2386
|
|
POST
|
After making changes to your mxd via Python, you need to save those changes with: mxd.save()
... View more
04-25-2024
11:42 AM
|
3
|
2
|
2410
|
|
POST
|
I've used variations of this script: Updating and fixing data sources with arcpy.mapping import arcpy
mxd = arcpy.mapping.MapDocument(r"C:\Project\Project.mxd")
for lyr in arcpy.mapping.ListLayers(mxd):
if lyr.supports("DATASOURCE"):
if lyr.dataSource == r"C:\Project\Data\Parcels.gdb\MapIndex":
lyr.findAndReplaceWorkspacePath(r"Data", r"Data2")
mxd.saveACopy(r"C:\Project\Project2.mxd")
del mxd
... View more
04-24-2024
12:54 PM
|
0
|
2
|
2406
|
|
POST
|
Can the Workspace be a feature class or feature dataset? I've always set the workspace as a .gdb or .sde I'm curious if it works if you set the workspace as: r"C:\Users\User\AppData\Roaming\Esri\ArcGISPro\Favorites\sdeConnection.sde
... View more
04-23-2024
12:04 PM
|
1
|
1
|
2376
|
|
POST
|
One option is to hard-code the desired field order into the script. import arcpy
import pandas as pd
fc = r'https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/USA_States_Generalized_Boundaries/FeatureServer/0'
fieldnames=[]
fields = arcpy.ListFields(fc)
# get list of field names from fc
for field in fields:
print(field.name)
fieldnames.append(field.name)
# convert FC to df
data = [row for row in arcpy.da.SearchCursor(fc, fieldnames)]
fc_dataframe = pd.DataFrame(data, columns=fieldnames)
print(fc_dataframe)
# remove specific fields
ListOfFieldsToRemove = ['OBJECTID','Shape__Area','Shape__Length','Shape']
fc_dataframe.drop(ListOfFieldsToRemove , axis=1, inplace = True)
print("printing dataframe...")
print(fc_dataframe)
# reorder fields
df = fc_dataframe[['STATE_ABBR', 'STATE_FIPS', 'STATE_NAME', 'POPULATION', 'POP_SQMI', 'SQMI']]
df_reordered = fc_dataframe[['STATE_ABBR', 'POP_SQMI', 'SQMI', 'STATE_FIPS', 'STATE_NAME','POPULATION']]
print("printing dataframe (with fields re-ordered)...")
print(df_reordered)
# export to csv
print("exporting to csv")
df.to_csv(r'C:\Temp\CSV_export.csv', index=False) Another option is to have a template .csv file and read the field order from that and then program your output to match the template.
... View more
04-22-2024
07:39 AM
|
0
|
0
|
861
|
|
POST
|
My understanding is if you point a tool at a layer in the Table of Contents, it will run on the selected records (if any are selected) and on the entire layer if none are selected. 99% of my Python scripts run outside ArcMap but that's the way it works in standalone scripts.
... View more
04-22-2024
05:53 AM
|
1
|
0
|
1583
|
|
POST
|
Never tried to do this but thinking in my head... Using a Loop... Do a near analysis and get the OID of the nearest feature. Then make a feature layer but def query to display all features EXCEPT the OID returned Run Near Analysis again against the queried feature layer to get the 2nd closest feature
... View more
04-19-2024
11:18 AM
|
0
|
0
|
1794
|
|
POST
|
This is what I use: #note: this must be run in Python 3.x
#This script will list all ArcGIS Online content, what it is, who owns it, the dates it was created & modified
import arcgis, time
import csv
gis=GIS(url,username,password)
print ("Organization Name: "+str(gis.properties.name))
print ("Portal Name: "+str(gis.properties.portalName))
#print ("URL: "+str(gis.properties.customBaseUrl))
print ("ArcGIS Online Credits Available: "+str("{:,}".format(gis.properties.availableCredits)))
print("ELA Expiration Date: "+str(time.strftime('%m/%d/%Y %H:%M:%S', time.gmtime(gis.properties.subscriptionInfo.expDate/1000.0))))
print("Max Users per Level: ")
for key,val in gis.properties.subscriptionInfo.maxUsersPerLevel.items():
print (key, "=", val)
print ("\n")
#set item_type if you only want to search for ex. "Feature Layer"
search_result = gis.content.search(query="", item_type="",max_items=10000) #max number is set so it will return more than the default 10 results
outCSVfile=r"L:\Scripts\ArcGISOnline\ArcGISOnline_ContentDetails.csv"
csvfile = open(outCSVfile, 'w',newline='')
writer = csv.writer(csvfile)
writer.writerow(["Title","ItemType","Sharing","Description","Snippet","Tags","Categories","AccessInformation","Protected","Status","OwnedBy","DateCreated","DateLastModified","View Count","Item_ID"])
print("There are: "+str(len(search_result))+" total items in our ArcGIS Online.")
recordNumber = 0
for i in search_result:
recordNumber+=1
try:
description = str(i.description.encode('unicode-escape').decode('utf-8')) #removes emojis
except:
description = str(i.description)
#print(i.title+","+","+i.type+","+i.access+","+description+str(i.snippet)+","+str(i.tags)+","+str(i.categories)+","+str(i.accessInformation)+","+str(i.protected)+","+str(i.content_status)+i.owner+","+","+str(time.strftime('%m/%d/%Y %H:%M:%S', time.gmtime(i.created/1000.0))+","+str(time.strftime('%m/%d/%Y %H:%M:%S', time.gmtime(i.modified/1000.0))))+","+str(i.numViews)+","+(i.id))
#print (recordNumber) # so we know the script is doing something
writer.writerow([i.title, i.type,i.access,description,str(i.snippet),str(i.tags),str(i.categories),str(i.accessInformation),str(i.protected),str(i.content_status),str(i.owner),str(time.strftime('%m/%d/%Y %H:%M:%S', time.gmtime(i.created/1000.0))),str(time.strftime('%m/%d/%Y %H:%M:%S', time.gmtime(i.modified/1000.0))),i.numViews,i.id])
csvfile.close()
... View more
03-27-2024
12:55 PM
|
1
|
1
|
1038
|
|
POST
|
I add something like this if I want to remind myself that I'm about to edit or overwrite critical data... answer = input("Do you want to continue? (yes/no): ").lower()
if answer == "yes" or answer == "y":
print("Continuing...")
# Add your code here for further execution
else:
print("Exiting...")
sys.exit()
... View more
03-26-2024
05:28 AM
|
2
|
0
|
2187
|
|
POST
|
Yes, that is correct. You need to use update cursor to edit records whereas a search cursor is only used for read-only.
... View more
03-20-2024
04:13 PM
|
1
|
0
|
1271
|
|
POST
|
To get unique values in a field, I've used. import arcpy
featureClass = r"C:\Default.gdb\Export_Output_10"
fields = ['FEATUREID']
listOfValues=[]
with arcpy.da.SearchCursor(featureClass, fields) as cursor:
for row in cursor:
listOfValues.append(row[0]) Then you can convert the list to a set. # set() to convert list to set
set_of_values = set(listOfValues) Sets can be subtracted from each other to find the differences. Python Set | difference() - GeeksforGeeks
... View more
03-14-2024
05:47 PM
|
0
|
0
|
1160
|
| Title | Kudos | Posted |
|---|---|---|
| 2 | 04-09-2026 11:12 AM | |
| 2 | 05-06-2024 09:48 AM | |
| 5 | 05-06-2024 09:44 AM | |
| 1 | 04-25-2024 12:31 PM | |
| 1 | 04-25-2024 12:15 PM |
| Online Status |
Offline
|
| Date Last Visited |
yesterday
|