|
IDEA
|
When downloading mobile map packages created from WebMaps in ArcGIS Online > Settings > Manage Offline Areas, include the layers of the map package and not just the basemap. We want to be able to create detail rich offline maps for use in Survey123, but limit file sizes. Our data also changes regularly, so utilising a webmap with scheduled repackaging makes sense. But only the basemap shows up in Survey123 - no map layers. The only way it seems to get imagery and map layers at present is to create a .tpk in ArcGIS Pro - slow and manual. No thanks!
... View more
04-07-2025
10:23 PM
|
1
|
2
|
1275
|
|
BLOG
|
You got! I implemented the 3 changes you suggested, and it worked for rows that had null area values, but not incorrect. I then removed the 2nd suggestion (if valueDict[keyValue] != None and updateRow[1] == None:) and it worked for all missing and incorrect values. It also only "edited" the features requiring an update an nothing else (though I've now implemented disabling and enabling of editor tracking in the script so this is less important from an editor tracking perspective, but I'm sure is still a huge performance boon). print("Updating data")
with arcpy.da.UpdateCursor(featureservice, updateFieldsList) as updateRows:
for updateRow in updateRows:
print(updateRow)
# store the Join value of the row being updated in a keyValue variable
keyValue = updateRow[0].upper()
# verify that the keyValue is in the Dictionary
if keyValue in valueDict:
print(f"KeyValue found: {keyValue}")
if valueDict[keyValue] != updateRow[1]:
print("Yep")
# A newer value exists
for n in range (1,len(sourceFieldsList)):
print(n)
updateRow[n] = valueDict[keyValue]
updateRows.updateRow(updateRow)
# transfer the values stored under the keyValue from the dictionary to the updated fields.
del valueDict
print(f"AreaHa field updated successfully for {fc_name}") Thank you again for your assistance. It's been invaluable.
... View more
03-17-2025
12:14 AM
|
0
|
0
|
10523
|
|
BLOG
|
We're working beyond my knowledge base here, thus my inept adaptations of your original code. At that point, it should just be getting the keyValue that matches the updateRow and updating the associated Area value.
... View more
03-16-2025
09:42 PM
|
0
|
0
|
10533
|
|
IDEA
|
Just went searching for this idea. I've started implementing MS Power Automate/Flow and webhooks for form emailing functionality, but it has limitations! Once a form gets too big, Flow won't import the data (even in a truncated format). This means that either the form needs to be completely rebuilt using sub-forms (something that was intentionally not done the first time). It would be good if Survey123 had even just a simple in-built email function that could send a copy of the record in csv or pdf format to either a preset email address or email addresses entered into an email question/s in the submitted form. After moving our corporate system from iForms to Survey123, it's frustrating that this function isn't easier to implement!
... View more
03-16-2025
07:54 PM
|
0
|
0
|
622
|
|
BLOG
|
Woohoo! Progress. Thank you. Now on to the next bit... float object is not iterable.... I've put in some print statements to debug - looks like this is the problem line: list(valueDict[keyValue])
... View more
03-12-2025
08:49 PM
|
0
|
0
|
10610
|
|
BLOG
|
I have no idea what is going on here, but it seems that something is not playing nice between ArcGIS Online and ArcGIS Pro/arcpy. In AGOL, the GlobalID is all lowercase: but the same feature in ArcGIS Pro is all uppercase: So I have no idea what to do now. Found this thread, but seems it's still not resolved. I wonder if this is what is causing my issues.
... View more
03-12-2025
08:34 PM
|
0
|
0
|
10618
|
|
BLOG
|
Getting Exception: name 'upper' is not defined for the keyValue = upper(updateRow[0]) line. I've tried applying it this way as well, but still not working despite not throwing an exception: keyValue = (updateRow[0].upper)
... View more
03-12-2025
08:21 PM
|
0
|
0
|
10620
|
|
BLOG
|
Hi @RichardFairhurst. I'm having a go at implementing this in another application. This time, seemingly a lot simpler, but I must still be missing something. I'm only trying to check and update an area field (AreaHa) and want to use the GlobalID as the join field. Below is the code I'm currently working with and the print statements. I'm guessing it isn't happy because the GlobalID's don't match case, but I can't seem to force this to be consistent. Also tried to store the original ObjectID's in a temp field to use and couldn't get that to work either (couldn't get the field to export). # Create a temporary directory
temp_dir = tempfile.mkdtemp()
# Define the path for the temporary File Geodatabase
temp_gdb = os.path.join(temp_dir, "tempAreaHa.gdb")
# Create the File Geodatabase
arcpy.CreateFileGDB_management(temp_dir, "tempAreaHa.gdb")
# Set the workspace to the temporary File Geodatabase
arcpy.env.workspace = temp_gdb
def remove_last_part(url):
# Split the URL by '/' and join all parts except the last one
return '/'.join(url.split('/')[:-1])
def extract_fc_name(url):
# Extract the feature class name from the URL and clean it
path_parts = url.split('/')
fc_name = path_parts[-3]
return fc_name
# Read the CSV file containing feature service URLs
# Example service url value in csv: https://services-ap1.arcgis.com/#############/arcgis/rest/services/Plantation_Concept_Areas_Test/FeatureServer/0
with open('FeatureServiceAreaUpdates.csv', mode='r') as file:
csv_reader = csv.reader(file)
next(csv_reader) # Skip header row if present
for row in csv_reader:
featureservice = row[0]
fc_name = extract_fc_name(featureservice)
with arcpy.EnvManager(preserveGlobalIds=True):
temp_Areas = arcpy.conversion.ExportFeatures(
in_features=featureservice,
out_features=arcpy.env.workspace + "\\" + fc_name,
where_clause="",
use_field_alias_as_name="NOT_USE_ALIAS",
field_mapping=f'AreaHa "Area (ha)" true true false 0 Double 0 0,First,#,{fc_name},AreaHa,-1,-1;GlobalID "GlobalID" false false true 38 GlobalID 0 0,First,#,{fc_name},GlobalID,-1,-1',
sort_field=None
)
print(f"Features exported for {fc_name}")
arcpy.management.CalculateGeometryAttributes(
in_features=temp_Areas,
geometry_property="AreaHa AREA",
length_unit="",
area_unit="HECTARES",
coordinate_system='PROJCS["GDA_1994_MGA_Zone_50",GEOGCS["GCS_GDA_1994",DATUM["D_GDA_1994",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",10000000.0],PARAMETER["Central_Meridian",117.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]]',
coordinate_format="SAME_AS_INPUT"
)
print(f"Areas calculated for {fc_name}")
# Remove the last part of the URL
modified_url = remove_last_part(featureservice)
# Access the feature layer collection
flc = FeatureLayerCollection(modified_url, gis)
# Disable editor tracking
disable_params = {
"editorTrackingInfo": {
"enableEditorTracking": False
}
}
flc.manager.update_definition(disable_params)
print(f"Editor tracking disabled for {fc_name}")
# Create a dictionary from the exported table
sourceFieldsList = ['GlobalID', 'AreaHa']
valueDict = {r[0]: r[1] for r in arcpy.da.SearchCursor(temp_Areas, sourceFieldsList)}
print(valueDict)
# Update the feature service using the dictionary
updateFieldsList = ['GlobalID', 'AreaHa']
with arcpy.da.UpdateCursor(featureservice, updateFieldsList) as updateRows:
for updateRow in updateRows:
print(updateRow)
# store the Join value of the row being updated in a keyValue variable
keyValue = updateRow[0]
# verify that the keyValue is in the Dictionary
if keyValue in valueDict:
print(keyValue)
if list(valueDict[keyValue]) and updateRow[1:1]:
# A newer value exists
changeCnt += 1
for n in range (1,len(sourceFieldsList)):
print(n)
updateRow[n] = valueDict[keyValue][n-1]
updateRows.updateRow(updateRow)
# transfer the values stored under the keyValue from the dictionary to the updated fields.
del valueDict
print(f"AreaHa field updated successfully for {fc_name}")
# Re-enable editor tracking
enable_params = {
"editorTrackingInfo": {
"enableEditorTracking": True,
"enableOwnershipAccessControl": True,
"allowOthersToUpdate": True,
"allowOthersToDelete": True
}
}
flc.manager.update_definition(enable_params)
print(f"Editor tracking re-enabled for {fc_name}")
endTime = time.time()
print(f"Script completed in {endTime - startTime} seconds.")
... View more
03-12-2025
05:14 PM
|
0
|
0
|
10634
|
|
BLOG
|
Nice work. I've implemented and tested the revised code and can confirm that it only updates fields that have changed and editor tracking info is only updated for those specific features. It's great having these full code snippets to work with too. Completely understand and appreciate having the extra functionality in the Insert/Delete available. I'm sure I will need it myself at some point!
... View more
03-03-2025
11:02 PM
|
0
|
0
|
10765
|
|
BLOG
|
Thanks for trying to figure this out for me @RichardFairhurst. I tested your new code and it seemed to update all found rows with the same value. I did remove the Insert and Delete sections as that wasn't required. I just experiemented with the code I provided above that was working and included a bit extra to disable and renenable editor tracking on the feature service around the Update section. This worked well and is sufficient for my needs. Thanks again for your efforts (especially on your phone!). Updated code below showing editor tracking disable/enable. del valueDict
print("Temp data prepped")
# Update feature service
def remove_last_part(url):
# Split the URL by '/' and join all parts except the last one
return '/'.join(url.split('/')[:-1])
# Remove the last part of the URL
modified_url = remove_last_part(fs2)
# Access the feature layer collection
flc = FeatureLayerCollection(modified_url, gis)
# Disable editor tracking
disable_params = {
"editorTrackingInfo": {
"enableEditorTracking": False
}
}
flc.manager.update_definition(disable_params)
sourceFieldsList = ['Plantation', 'PlantingYear', 'insp_2_weeks', 'insp_4_weeks', 'insp_6_weeks', 'insp_8_weeks', 'insp_10_weeks', 'insp_12_weeks', 'insp_4_months', 'insp_5_months', 'insp_6_months', 'insp_9_months', 'insp_12_months', 'insp_15_months', 'insp_18_months', 'insp_21_months', 'insp_24_months']
# Use list comprehension to build a dictionary from a da SearchCursor where the key values are based on 2 separate feilds
valueDict = {str(r[0]) + "," + str(r[1]):(r[2:]) for r in arcpy.da.SearchCursor(Table2, sourceFieldsList)}
updateFieldsList = ['Plantation', 'PlantingYear', 'insp_2_weeks', 'insp_4_weeks', 'insp_6_weeks', 'insp_8_weeks', 'insp_10_weeks', 'insp_12_weeks', 'insp_4_months', 'insp_5_months', 'insp_6_months', 'insp_9_months', 'insp_12_months', 'insp_15_months', 'insp_18_months', 'insp_21_months', 'insp_24_months']
with arcpy.da.UpdateCursor(fs2, updateFieldsList) as updateRows:
for updateRow in updateRows:
# store the Join value by combining 2 field values of the row being updated in a keyValue variable
keyValue = updateRow[0]+ "," + str(updateRow[1])
# verify that the keyValue is in the Dictionary
if keyValue in valueDict:
if list(updateRow[2:]) != list(valueDict[keyValue]):
# transfer the values stored under the keyValue from the dictionary to the updated fields.
for n in range (2,len(sourceFieldsList)):
if valueDict[keyValue][n-2] == None:
pass
else:
updateRow[n] = valueDict[keyValue][n-2]
updateRows.updateRow(updateRow)
del valueDict
# Enable editor tracking
enable_params = {
"editorTrackingInfo": {
"enableEditorTracking": True
}
}
flc.manager.update_definition(enable_params)
print("Fields updated successfully.")
... View more
03-03-2025
06:03 PM
|
0
|
0
|
10789
|
|
BLOG
|
I inserted the print statements at Lines 7 & 8 below and caught the data for a record that should NOT be updated. Row A is the target table, row B is the source table. If Row B contains values where Row A is None or a differing value, then it would update Row A. In this case, Row B contains None values in some fields (those should be ignored outright) and the values it does contain already match those in Row A (so can also be ignored). with arcpy.da.UpdateCursor(fs2, updateFieldsList) as updateRows:
for updateRow in updateRows:
# store the Join value by combining 2 field values of the row being updated in a keyValue variable
keyValue = updateRow[0]+ "," + str(updateRow[1])
# verify that the keyValue is in the Dictionary
if keyValue in valueDict:
print(f"A: {list(updateRow[2:])}")
print(f"B: {list(valueDict[keyValue])}")
if list(updateRow[2:]) != list(valueDict[keyValue]): A: ['Done previously on iForms', 'Done previously on iForms', 'Done previously on iForms', 'Done previously on iForms', 'Done previously on iForms', 'Done previously on iForms', 'john.doe on 2024-12-11', 'john.doe on 2025-01-24', None, None, None, None, None, None, None]
B: [None, None, None, None, None, None, 'john.doe on 2024-12-11', 'john.doe on 2025-01-24', None, None, None, None, None, None, None] Here's my code in full - a lot of it is data prep with the stuff we've been discussing at the end (from Line 197). import arcpy
from datetime import datetime
from arcgis.gis import GIS
import certifi
import urllib3
import ssl
import warnings
from urllib3.exceptions import InsecureRequestWarning
# Create a default SSL context with certificate verification
ssl_context = ssl.create_default_context(cafile=certifi.where())
http = urllib3.PoolManager(ssl_context=ssl_context)
# Make a request to verify the setup
response = http.request('GET', 'https://maps.arcgis.com')
print("http response: " + str(response.status))
# Suppress only the single InsecureRequestWarning from urllib3 if necessary
warnings.simplefilter('ignore', InsecureRequestWarning)
# Create GIS object
print("Connecting to AGOL")
client_id = '##########'
client_secret = '##################'
gis = GIS("https://maps.arcgis.com", client_id=client_id, client_secret=client_secret)
print("Logged in as: " + gis.properties.user.username)
arcpy.env.overwriteOutput = True
arcpy.env.preserveGlobalIds=True
fs1 = "https://services-ap1.arcgis.com/###########/arcgis/rest/services/service_df503b7953e7471a975eb99994681d72/FeatureServer/0"
fs2 = "https://services-ap1.arcgis.com/###############/arcgis/rest/services/Post_Planting_Inspection_Tracking___TEST/FeatureServer/0"
# Update the "username" field with the "Creator" field value if "username" is NULL
with arcpy.da.UpdateCursor(fs1, ["username", "Creator"]) as cursor:
for row in cursor:
if row[0] is None or row[0] == "":
row[0] = row[1]
cursor.updateRow(row)
print("Username field updated successfully.")
with arcpy.EnvManager(preserveGlobalIds=True):
export_table = arcpy.conversion.ExportTable(
in_table=fs1,
out_table=r"C:\Temp\scratch.gdb\FPC416_survey_ExportTable",
where_clause="inspection_number <> 'Ad-hoc Inspection'",
use_field_alias_as_name="NOT_USE_ALIAS",
field_mapping=r'globalid "GlobalID" false false true 38 GlobalID 0 0,First,#,FPC416 - Plantation & Property Inspection Form\FPC416_survey,globalid,-1,-1;username "USERNAME" true true false 255 Text 0 0,First,#,FPC416 - Plantation & Property Inspection Form\FPC416_survey,username,0,254;date_of_inspection "DATE OF INSPECTION" true true false 255 Date 0 1,First,#,FPC416 - Plantation & Property Inspection Form\FPC416_survey,date_of_inspection,-1,-1;plantation "PLANTATION" true true false 255 Text 0 0,First,#,FPC416 - Plantation & Property Inspection Form\FPC416_survey,plantation,0,254;tree_species "TREE SPECIES" true true false 255 Text 0 0,First,#,FPC416 - Plantation & Property Inspection Form\FPC416_survey,tree_species,0,254;tenure "TENURE" true true false 255 Text 0 0,First,#,FPC416 - Plantation & Property Inspection Form\FPC416_survey,tenure,0,254;years_planted "YEAR/S PLANTED" true true false 255 Text 0 0,First,#,FPC416 - Plantation & Property Inspection Form\FPC416_survey,years_planted,0,254;inspection_number "INSPECTION NUMBER" true true false 255 Text 0 0,First,#,FPC416 - Plantation & Property Inspection Form\FPC416_survey,inspection_number,0,254',
sort_field=None
)
# Delete fields
arcpy.management.DeleteField(
in_table=export_table,
drop_field="tree_species;tenure;inspection_type;fbs_type;fbs_respons;fbs_respons_other;fbs_adeq;fbs_trafcbl;fbs_issues;fbs_issues_other;fbs_action_tkn;fbs_action_tkn_other;fbs_fup_reqd;wtrpts_accessbl;wtrpts_sign_adeq;wtrpts_dry_summer;wtrpts_issues;wtrpts_issues_other;wtrpts_action_tkn;wtrpts_action_tkn_other;wtrpts_fup_reqd;pwrlne_clr_accept;pwrlne_clr_respons;pwrlne_clr_respons_other;pwrlne_clr_issues;pwrlne_clr_issues_other;pwrlne_clr_action_tkn;pwrlne_clr_action_tkn_other;pwrlne_clr_fup_reqd;nut_def_evident;nut_def_impact;nut_def_issues;nut_def_issues_other;nut_def_percent_aff;nut_def_action_tkn;nut_def_action_tkn_other;nut_def_fup_reqd;drought_evident;drought_impact;drought_issues;drought_issues_other;drought_percent_aff;drought_action_tkn;drought_action_tkn_other;drought_fup_reqd;frost_evident;frost_impact;frost_issues;frost_issues_other;frost_percent_aff;frost_action_tkn;frost_action_tkn_other;frost_fup_reqd;insects_evident;insects_type;insects_type_other;insects_plant_part;insects_plant_part_other;insects_impact;insects_percent_aff;insects_action_tkn;insects_action_tkn_other;insects_fup_reqd;vert_pests_evident;vert_pests_type;vert_pests_type_other;vert_pests_plant_part;vert_pests_plant_part_other;vert_pests_impact;vert_pests_percent_aff;vert_pests_action_tkn;vert_pests_action_tkn_other;vert_pests_fup_reqd;diseases_evident;diseases_type;diseases_type_other;diseases_plant_part;diseases_plant_part_other;diseases_impact;diseases_percent_aff;diseases_myrtle_rust_new_event;diseases_action_tkn;diseases_action_tkn_other;diseases_fup_reqd;weeds_impct_gwth;dec_weeds_prsnt;weeds_type;weeds_type_other;weeds_impact;weeds_issues;weeds_issues_other;weeds_percent_aff;weeds_action_tkn;weeds_action_tkn_other;weeds_fup_reqd;remnt_veg_evident;remnt_veg_type;remnt_veg_type_other;wind_dmg_evident;wind_dmg_impact;wind_dmg_issues;wind_dmg_issues_other;wind_dmg_percent_aff;wind_dmg_action_tkn;wind_dmg_action_tkn_other;wind_dmg_fup_reqd;sun_scorch_evident;sun_scorch_impact;sun_scorch_issues;sun_scorch_issues_other;sun_scorch_percent_aff;sun_scorch_action_tkn;sun_scorch_action_tkn_other;sun_scorch_fup_reqd;inundtn_evident;inundtn_impact;inundtn_issues;inundtn_issues_other;inundtn_percent_aff;inundtn_action_tkn;inundtn_action_tkn_other;inundtn_fup_reqd;stem_dfrms_evident;stem_dfrms_impact;stem_dfrms_issues;stem_dfrms_issues_other;stem_dfrms_percent_aff;stem_dfrms_action_tkn;stem_dfrms_action_tkn_other;stem_dfrms_fup_reqd;erosion_evident;erosion_impact;erosion_issues;erosion_issues_other;erosion_percent_aff;erosion_action_tkn;erosion_action_tkn_other;erosion_fup_reqd;stkhldr_obs;detail_stkhldr_obs;tree_types;tree_types_other;height_m;height;basal_area_m2;basal_area;stems_ha;stems;cone_dvpt;host_gwth_assess;sndlwd_stkng;sndlwd_stkng_other;sndlwd_gwth_assess;flwrng_seed_assess;flwrng_seed_assess_other;sndlwd_actions_tkn;sndlwd_actions_tkn_other;sndlwd_fup_action_reqd;rdsigns_vsble;rdsigns_issues;rdsigns_issues_other;rdsigns_actions_tkn;rdsigns_actions_tkn_other;rdsigns_fup_action_reqd;prop_signs_vsbl;prop_signs_issues;prop_signs_issues_other;propsigns_actions_tkn;propsigns_actions_tkn_other;propsigns_fup_action_reqd;fences_adeq;fences_issues;fences_issues_other;fences_actions_tkn;fences_actions_tkn_other;fences_fup_action_reqd;padlocks_reqd;padlock_issues;padlocks_actions_tkn;padlocks_actions_tkn_other;padlocks_fup_action_reqd;fire_canisters_intact;fire_canister_maps_unspoiled_current;fire_canisters_fup_action_reqd;bldgs_adeq;bldgs_fireprf;bldgs_issues;bldgs_issues_other;bldgs_actions_tkn;bldgs_actions_tkn_other;bldgs_fup_action_reqd;utils_accesbl;utils_fireprf;utils_issues;utils_issues_other;utils_actions_tkn;utils_actions_tkn_other;utils_fup_action_reqd;why_inspctn_missed;why_inspctn_missed_other;compltd_survey_date;CreationDate;Creator;EditDate;Editor;comments",
method="DELETE_FIELDS"
)
# Define the Inspection list
lookup_list = ['2 weeks', '4 weeks', '6 weeks', '8 weeks', '10 weeks', '12 weeks', '4 months', '5 months', '6 months', '9 months', '12 months', '15 months', '18 months', '21 months', '24 months']
# Define the lookup dictionary
lookup_dict = {
'2 weeks': 'insp_2_weeks',
'4 weeks': 'insp_4_weeks',
'6 weeks': 'insp_6_weeks',
'8 weeks': 'insp_8_weeks',
'10 weeks': 'insp_10_weeks',
'12 weeks': 'insp_12_weeks',
'4 months': 'insp_4_months',
'5 months': 'insp_5_months',
'6 months': 'insp_6_months',
'9 months': 'insp_9_months',
'12 months': 'insp_12_months',
'15 months': 'insp_15_months',
'18 months': 'insp_18_months',
'21 months': 'insp_21_months',
'24 months': 'insp_24_months'
}
print("Lookup table created")
# Add text fields to the table for each value in the lookup dictionary
for field_name in lookup_dict.values():
print(f"Creating {field_name} field")
arcpy.AddField_management(export_table, field_name, "TEXT")
# Function to parse date with different formats
def parse_date(date_str):
for fmt in ("%d/%m/%Y %I:%M:%S %p", "%d/%m/%Y", "%d/%m/%Y %I:%M:%S.%f %p", "%d/%m/%Y %I:%M:%S %p"):
try:
return datetime.strptime(date_str, fmt).strftime("%Y-%m-%d")
except ValueError:
pass
raise ValueError(f"Date format for {date_str} is not recognized")
# Update the fields based on the inspection_number using CalculateField
for v in lookup_list:
update_field = lookup_dict[v]
update_view = arcpy.management.MakeTableView(
in_table=export_table,
out_view="TableView",
where_clause=f"inspection_number = '{v}'",
workspace=None,
field_info=f"globalid globalid VISIBLE NONE;username username VISIBLE NONE;date_of_inspection date_of_inspection VISIBLE NONE;plantation plantation VISIBLE NONE;years_planted years_planted VISIBLE NONE;inspection_number inspection_number VISIBLE NONE;{update_field} {update_field} VISIBLE NONE"
)
expression = f"!username! + ' on ' + (datetime.strptime(!date_of_inspection!, '%d/%m/%Y %I:%M:%S %p') if '/' in !date_of_inspection! else datetime.strptime(!date_of_inspection!, '%d/%m/%Y')).strftime('%Y-%m-%d')"
arcpy.management.CalculateField(update_view, update_field, expression, "PYTHON3")
print("Fields added and updated successfully.")
flat_table = arcpy.analysis.Statistics(
in_table=export_table,
out_table=r"C:\Temp\scratch.gdb\FlattenedInspectionTable",
statistics_fields="insp_2_weeks FIRST;insp_4_weeks FIRST;insp_6_weeks FIRST;insp_8_weeks FIRST;insp_10_weeks FIRST;insp_12_weeks FIRST;insp_4_months FIRST;insp_5_months FIRST;insp_6_months FIRST;insp_9_months FIRST;insp_12_months FIRST;insp_15_months FIRST;insp_18_months FIRST;insp_21_months FIRST;insp_24_months FIRST",
case_field="plantation;years_planted",
concatenation_separator=""
)
print("Table flattened")
# List all fields in the table
flat_fields = arcpy.ListFields(flat_table)
# Iterate through the fields and rename if they start with "FIRST_"
for field in flat_fields:
if field.name.startswith("FIRST_"):
new_field_name = field.name.replace("FIRST_", "", 1)
arcpy.AlterField_management(flat_table, field.name, new_field_name, new_field_name)
print("Fields renamed successfully in flattend table.")
arcpy.management.CalculateField(
in_table=flat_table,
field="join_field",
expression='!plantation!+"_"+str(!years_planted!)',
expression_type="PYTHON3",
code_block="",
field_type="TEXT",
enforce_domains="NO_ENFORCE_DOMAINS"
)
print("Join field created in flattened table")
with arcpy.EnvManager(preserveGlobalIds=True):
Table2 = arcpy.conversion.ExportTable(
in_table=fs2,
out_table=r"C:\temp\scratch.gdb\PostPlantingInspectionTracking_TargetTable",
where_clause="",
use_field_alias_as_name="NOT_USE_ALIAS",
field_mapping='Plantation "Plantation" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,Plantation,0,49;PlantingYear "Planting Year" true true false 0 Long 0 0,First,#,Post Planting Inspection Tracking - TEST,PlantingYear,-1,-1;insp_2_weeks "2 week inspection" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,insp_2_weeks,0,49;insp_4_weeks "4 week inspection" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,insp_4_weeks,0,49;insp_6_weeks "6 week inspection" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,insp_6_weeks,0,49;insp_8_weeks "8 week inspection" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,insp_8_weeks,0,49;insp_10_weeks "10 week inspection" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,insp_10_weeks,0,49;insp_12_weeks "12 week inspection" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,insp_12_weeks,0,49;insp_4_months "4 month inspection" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,insp_4_months,0,49;insp_5_months "5 month inspection" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,insp_5_months,0,49;insp_6_months "6 month inspection" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,insp_6_months,0,49;insp_9_months "9 month inspection" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,insp_9_months,0,49;insp_12_months "12 month inspection" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,insp_12_months,0,49;insp_15_months "15 month inspection" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,insp_15_months,0,49;insp_18_months "18 month inspection" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,insp_18_months,0,49;insp_21_months "21 month inspection" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,insp_21_months,0,49;insp_24_months "24 month inspection" true true false 50 Text 0 0,First,#,Post Planting Inspection Tracking - TEST,insp_24_months,0,49',
sort_field=None
)
print("Inspection tracking table exported")
arcpy.management.CalculateField(
in_table=Table2,
field="join_field",
expression='!Plantation!+"_"+str(!PlantingYear!)',
expression_type="PYTHON3",
code_block="",
field_type="TEXT",
enforce_domains="NO_ENFORCE_DOMAINS"
)
print("Join field created in tracking table")
sourceFieldsList = ['join_field', 'insp_2_weeks', 'insp_4_weeks', 'insp_6_weeks', 'insp_8_weeks', 'insp_10_weeks', 'insp_12_weeks', 'insp_4_months', 'insp_5_months', 'insp_6_months', 'insp_9_months', 'insp_12_months', 'insp_15_months', 'insp_18_months', 'insp_21_months', 'insp_24_months']
# Use list comprehension to build a dictionary from a da SearchCursor
valueDict = {r[0]:(r[1:]) for r in arcpy.da.SearchCursor(flat_table, sourceFieldsList)}
updateFieldsList = ['join_field', 'insp_2_weeks', 'insp_4_weeks', 'insp_6_weeks', 'insp_8_weeks', 'insp_10_weeks', 'insp_12_weeks', 'insp_4_months', 'insp_5_months', 'insp_6_months', 'insp_9_months', 'insp_12_months', 'insp_15_months', 'insp_18_months', 'insp_21_months', 'insp_24_months']
changeCnt = 0
with arcpy.da.UpdateCursor(Table2, updateFieldsList) as updateRows:
for updateRow in updateRows:
# store the Join value of the row being updated in a keyValue variable
keyValue = updateRow[0]
# verify that the keyValue is in the Dictionary
if keyValue in valueDict:
if list(valueDict[keyValue]) and updateRow[1:15]:
# A newer value exists
changeCnt += 1
for n in range (1,len(sourceFieldsList)):
updateRow[n] = valueDict[keyValue][n-1]
updateRows.updateRow(updateRow)
# transfer the values stored under the keyValue from the dictionary to the updated fields.
del valueDict
print("Temp data prepped")
# Update feature service
sourceFieldsList = ['Plantation', 'PlantingYear', 'insp_2_weeks', 'insp_4_weeks', 'insp_6_weeks', 'insp_8_weeks', 'insp_10_weeks', 'insp_12_weeks', 'insp_4_months', 'insp_5_months', 'insp_6_months', 'insp_9_months', 'insp_12_months', 'insp_15_months', 'insp_18_months', 'insp_21_months', 'insp_24_months']
# Use list comprehension to build a dictionary from a da SearchCursor where the key values are based on 2 separate feilds
valueDict = {str(r[0]) + "," + str(r[1]):(r[2:]) for r in arcpy.da.SearchCursor(Table2, sourceFieldsList)}
updateFieldsList = ['Plantation', 'PlantingYear', 'insp_2_weeks', 'insp_4_weeks', 'insp_6_weeks', 'insp_8_weeks', 'insp_10_weeks', 'insp_12_weeks', 'insp_4_months', 'insp_5_months', 'insp_6_months', 'insp_9_months', 'insp_12_months', 'insp_15_months', 'insp_18_months', 'insp_21_months', 'insp_24_months']
with arcpy.da.UpdateCursor(fs2, updateFieldsList) as updateRows:
for updateRow in updateRows:
# store the Join value by combining 2 field values of the row being updated in a keyValue variable
keyValue = updateRow[0]+ "," + str(updateRow[1])
# verify that the keyValue is in the Dictionary
if keyValue in valueDict:
if list(updateRow[2:]) != list(valueDict[keyValue]):
# transfer the values stored under the keyValue from the dictionary to the updated fields.
for n in range (2,len(sourceFieldsList)):
if valueDict[keyValue][n-2] == None:
pass
else:
updateRow[n] = valueDict[keyValue][n-2]
updateRows.updateRow(updateRow)
del valueDict
print("Fields updated successfully.") Interestingly, when I tried to run this from our server, it failed to run Line 35 because Pro wasn't signed in (doesn't want to use the login process at Line 26) but alas, that's an issue for a different post!
... View more
02-28-2025
12:13 AM
|
0
|
0
|
10954
|
|
POST
|
No luck with that one. Layer output from Make Feature Layer is still missing the joined fields (e.g. table2.field) and the source table fields show as the original - not as table1.field name.
... View more
02-27-2025
10:42 PM
|
1
|
0
|
3162
|
|
BLOG
|
Half way there! It left rows alone that were not present in the update table at all, but rows which were present but had no updates still had the edit date updated.
... View more
02-27-2025
10:30 PM
|
0
|
0
|
10987
|
| Title | Kudos | Posted |
|---|---|---|
| 1 | Friday | |
| 3 | a week ago | |
| 6 | Wednesday | |
| 2 | a week ago | |
| 2 | a week ago |
| Online Status |
Offline
|
| Date Last Visited |
yesterday
|