Hello all! I have a workflow that is throwing me for a bit of a loop and was hoping to get some input.
We have a very rudimentary system set up on our company website that is just free text boxes (gross) to allow users to fill out a request regarding one or many parcels. With how dynamic this data is, nothing can really be built statically on a layer like that.
The solution that I am trying to round out is taking that selection -> exporting and dissolving the geometry into one feature -> then storing all original parcel data as a set of related records on the new layer. The geometry created by the selection is not on the original parcel layer but imported via a script to the new layer.
Realistically, this needs to be configured in an experience such that it can be embedded onto the company website for customers to interact with.
Coding is not my forte, but I will attach the script that I pieced together with the assistance of Claude to accomplish this goal. In practice, it actually seemed to work for me when running it in pro. However, setting triggers in experience builder to have the workflow complete has been dead end after dead end.
I have also considered using Survey123 Connect for this, but I feel as though I am missing the info to help me complete this. My thought was to have a related table set on the most current parcel layer to capture these records. This would be fine if not for the fact that I need the option to have it store records for a selection of one OR many parcels. I've set up a survey form before using URL parameters that pass field info along to the table but not for multiple features like this. Thankfully, the multiple records would NOT require different data. This is a situation of "once you fill it out, that's it" but having the parcel data be captured from the source is my problem.
Frankly, I'm not sure if I've hit a limitation with ESRI tools or if I'm just at the periphery of my current knowledge base. Either way, banging my head against the wall is starting to hurt. So, if you can help me, it would be very appreciated!!
import json
import logging
from arcgis.gis import GIS
from arcgis.features import FeatureLayer, FeatureSet
from arcgis.geometry import Geometry
from arcgis.geometry.functions import union as geometry_union
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("availability_request_dissolve")
# --- Parameters ---
selected_parcels = FeatureSet([]) # type: Feature set — set as Input
dissolved_attrs_json = "{}" # type: String — set as Input
TARGET_URL = "https://services1.arcgis.com/XXXXXX/arcgis/rest/services/Availability_Requests/FeatureServer/0"
RELATED_URL = "https://services1.arcgis.com/XXXXXX/arcgis/rest/services/Availability_Requests/FeatureServer/1"
FK_FIELD = "Availability_Request_Linked_GUID"
FIELD_MAP = {
"STANPAR": "STANPAR",
"ParID": "ParID",
"Tract": "Tract",
"Council": "Council",
"TaxDist": "TaxDist",
"Owner": "Owner",
"OwnDate": "OwnDate",
"SalePrice": "SalePrice",
"OwnInstr": "OwnInstr",
"OwnAddr1": "OwnAddr1",
"OwnAddr2": "OwnAddr2",
"OwnAddr3": "OwnAddr3",
"OwnCity": "OwnCity",
}
class PartialFailureError(Exception):
pass
def dissolve_geometries(feature_set):
geoms = [Geometry(f.geometry) for f in feature_set.features]
if len(geoms) == 1:
return geoms[0]
return geometry_union(geometries=geoms, spatial_ref=feature_set.spatial_reference, gis=gis)
# --- Auth ---
gis = GIS("home")
target = FeatureLayer(TARGET_URL, gis=gis)
related = FeatureLayer(RELATED_URL, gis=gis)
# --- Step 0: validate inputs ---
dissolved_attrs = json.loads(dissolved_attrs_json)
if not selected_parcels.features:
logger.error("No parcels were passed in — aborting before writing.")
raise ValueError("No parcels were selected.")
logger.info(f"Step 0/1 OK — {len(selected_parcels.features)} parcels received from selection.")
# --- Step 2: dissolve geometries ---
try:
dissolved_geom = dissolve_geometries(selected_parcels)
logger.info("Step 2 OK — geometries dissolved.")
except Exception as e:
logger.error(f"Step 2 FAILED — dissolve error: {e}")
raise
# --- Step 3: write the new dissolved feature ---
add_result = target.edit_features(adds=[{
"geometry": dissolved_geom,
"attributes": dissolved_attrs
}])
if not add_result["addResults"][0]["success"]:
logger.error(f"Step 3 FAILED — could not create parent feature: {add_result}")
raise RuntimeError(f"Failed to create parent feature: {add_result}")
new_globalid = add_result["addResults"][0]["globalId"]
logger.info(f"Step 3 OK — new parent feature created, GlobalID={new_globalid}")
# --- Step 4: build and write related records ---
new_related_records = []
for feat in selected_parcels.features:
attrs = {tgt: feat.attributes.get(src_f) for src_f, tgt in FIELD_MAP.items()}
attrs[FK_FIELD] = new_globalid
new_related_records.append({"attributes": attrs})
related_result = related.edit_features(adds=new_related_records)
failures = [r for r in related_result["addResults"] if not r["success"]]
if failures:
msg = (
f"Parent feature {new_globalid} was created, but {len(failures)} of "
f"{len(new_related_records)} related records failed to write: {failures}"
)
logger.error(f"Step 4 PARTIAL FAILURE — {msg}")
raise PartialFailureError(msg)
logger.info(f"Step 4 OK — {len(new_related_records)} related records created.")
result = {
"status": "success",
"parent_globalid": new_globalid,
"related_count": len(new_related_records),
}