Select to view content in your preferred language

How to use Python to report version changes

128
2
a week ago
Labels (3)
kduerksen_nwic
Emerging Contributor

I am running ArcGIS Pro 3.4.3, with its Python 3.11 interpreter. I have created a tool written in Python that takes a SDE file as input and loops through the child versions listed within that SDE file, trying to gather and report the version differences (inserts, updates, deletes) for each of those child versions. The code reports different difference data and values for each of the different child versions, but does not appear to even closely resemble the difference information reported within ArcGIS Pro's "Version Changes" tool button.

For example, within ArcGIS Pro the Version Differences for the version I'm testing the code on displays:

dbo.report_points

--Delete (1)

dbo.report_polys:

--Insert (3)

--Delete (7)

--Update (1)

dbo.report_other:

--Delete (1)

--Update (1)

dbo.report_cfmou

--Insert (1)

 

However, my code's output (debug and other messages) displays:

Processing Feature Layer: DBO.informal_resource_lines
my_layer=DBO.informal_resource_lines, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.informal_resource_lines
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.informal_resource_lines':
Added features: 0
Deleted features: 0
Updated features: 2
Processing Feature Layer: DBO.informal_resource_points
my_layer=DBO.informal_resource_points, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.informal_resource_points
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.informal_resource_points':
Added features: 15
Deleted features: 6
Updated features: 1
Processing Feature Layer: DBO.informal_resource_polys
my_layer=DBO.informal_resource_polys, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.informal_resource_polys
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.informal_resource_polys':
Added features: 23
Deleted features: 8
Updated features: 3
Processing Feature Layer: DBO.report_lines
my_layer=DBO.report_lines, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.report_lines
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.report_lines':
Added features: 5
Deleted features: 193
Updated features: 15
Processing Feature Layer: DBO.report_points
my_layer=DBO.report_points, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.report_points
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.report_points':
Added features: 26
Deleted features: 201
Updated features: 14
Processing Feature Layer: DBO.report_polys
my_layer=DBO.report_polys, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.report_polys
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.report_polys':
Added features: 123
Deleted features: 4526
Updated features: 162
Processing Feature Layer: DBO.report_aprxloc
my_layer=DBO.report_aprxloc, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.report_aprxloc
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.report_aprxloc':
Added features: 25
Deleted features: 449
Updated features: 34
Processing Feature Layer: DBO.report_other
my_layer=DBO.report_other, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.report_other
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.report_other':
Added features: 28
Deleted features: 310
Updated features: 22
Processing Feature Layer: DBO.report_restricted
my_layer=DBO.report_restricted, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.report_restricted
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.report_restricted':
Added features: 0
Deleted features: 0
Updated features: 0
Processing Feature Layer: DBO.report_cfmou
my_layer=DBO.report_cfmou, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.report_cfmou
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.report_cfmou':
Added features: 1
Deleted features: 0
Updated features: 0
Processing Feature Layer: DBO.resource_districts
my_layer=DBO.resource_districts, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.resource_districts
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.resource_districts':
Added features: 1
Deleted features: 24
Updated features: 12
Processing Feature Layer: DBO.resource_lines
my_layer=DBO.resource_lines, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.resource_lines
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.resource_lines':
Added features: 21
Deleted features: 388
Updated features: 76
Processing Feature Layer: DBO.resource_points
my_layer=DBO.resource_points, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.resource_points
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.resource_points':
Added features: 42
Deleted features: 1055
Updated features: 21
Processing Feature Layer: DBO.resource_polys
my_layer=DBO.resource_polys, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.resource_polys
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.resource_polys':
Added features: 135
Deleted features: 2627
Updated features: 353
Processing Feature Layer: DBO.resource_aprxloc
my_layer=DBO.resource_aprxloc, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.resource_aprxloc
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.resource_aprxloc':
Added features: 58
Deleted features: 627
Updated features: 103
Processing Feature Layer: DBO.resource_restricted
my_layer=DBO.resource_restricted, ver_name.name="NWIC\RINERG".GR_report_voids_1, parent ver=dbo.DEFAULT
Creating Feature Class DBO.resource_restricted
Comparison between 'dbo.DEFAULT' and '"NWIC\RINERG".GR_report_voids_1' for feature 'DBO.resource_restricted':
Added features: 1
Deleted features: 2
Updated features: 5
"NWIC\RINERG".GR_report_voids_1 Version Changes = 11743

 

If someone can review my code and pass along any tips regarding what I am missing or doing incorrectly, it would be appreciated.

The main code I have performing the version processing for differences is as follows:

    # start the outer loop through the list of versions
    for ver_name in versions_list:
        if ver_name in db_roots:															# check for versions to ignore
            arcpy.AddMessage("**SKIPPING** ROOT VERSION {0}".format(ver_name.name))
            continue

        arcpy.AddMessage("PROCESSING VERSION {0}".format(ver_name.name))
        VersionChangesTally = 0

        for curr_feature in feature_list:
            arcpy.AddMessage("Processing Feature Layer: {0}".format(curr_feature))

            arcpy.AddMessage("my_layer={0}, ver_name.name={1}, parent ver={2}".format(curr_feature, ver_name.name, ver_name.parentVersionName))

            # Get add, update, delete counts
            # --- Define workspace and versions ---
            parent_version = "dbo.DEFAULT" 
            child_version = ver_name.name

            # --- Create temporary table views for comparison ---
            parent_view = "parent_view"
            child_view = "child_view"

            # Ensure temporary views are cleared
            if arcpy.Exists(parent_view):
                arcpy.Delete_management(parent_view)
            if arcpy.Exists(child_view):
                arcpy.Delete_management(child_view)

            arcpy.AddMessage("Creating Feature Class {0}".format(curr_feature))

            # Make table views for the parent and child versions
            try:
                # Set the workspace to the parent version and make a table view
                arcpy.env.workspace = sde_file
                parent_layer = arcpy.management.MakeFeatureLayer(curr_feature, parent_view)
                arcpy.ChangeVersion_management(parent_view, "TRANSACTIONAL", parent_version)

                # Set the workspace to the child version and make a table view
                child_layer = arcpy.management.MakeFeatureLayer(curr_feature, child_view)
                arcpy.ChangeVersion_management(child_view, "TRANSACTIONAL", child_version)
                arcpy.MakeTableView_management(curr_feature, child_view) # NOTE: this code seems to have no effect on execution

                # --- Extract OIDs into Python sets for efficient comparison ---
                parent_oids = set()
                with arcpy.da.SearchCursor(parent_view, "OBJECTID") as cursor:
                    for row in cursor:
                        parent_oids.add(row[0])

                child_oids = set()
                with arcpy.da.SearchCursor(child_view, "OBJECTID") as cursor:
                    for row in cursor:
                        child_oids.add(row[0])
                try:
                    add_oids = child_oids - parent_oids
                    adds = len(add_oids)

                    del_oids = parent_oids - child_oids
                    deletes = len(del_oids)

                    update_oids = set() # empty set to be added to below
                    updates = 0

                    common_oids = parent_oids.intersection(child_oids)
                finally:
                    parent_oids.clear()
                    child_oids.clear()

                # A more robust update check would involve comparing a field value
                # (e.g., a last_edited_date) or hashing the row values.
                with arcpy.da.SearchCursor(parent_view, ["OBJECTID", "SHAPE@JSON"]) as p_cursor, \
                        arcpy.da.SearchCursor(child_view, ["OBJECTID", "SHAPE@JSON"]) as c_cursor:
                    parent_dict = {row[0]: row[1] for row in p_cursor if row[0] in common_oids}
                    child_dict = {row[0]: row[1] for row in c_cursor if row[0] in common_oids}

                with arcpy.da.SearchCursor(child_view, ["OBJECTID", "OtherID"]) as c_cursor:
                    child_otherid_dict = {row[0]: row[1] for row in c_cursor} #if row[0] in common_oids}

                with arcpy.da.SearchCursor(parent_view, ["OBJECTID", "OtherID"]) as p_cursor:
                    parent_otherid_dict = {row[0]: row[1] for row in p_cursor} #if row[0] in common_oids}

                for oid in common_oids:
                    if parent_dict[oid] != child_dict[oid]:
                        updates += 1
                        update_oids.add(oid)

                # --- Report results ---
                arcpy.AddMessage(f"Comparison between '{parent_version}' and '{child_version}' for feature '{curr_feature}':")
                arcpy.AddMessage(f"Added features: {adds}")
                arcpy.AddMessage(f"Deleted features: {deletes}")
                arcpy.AddMessage(f"Updated features: {updates}")

                # innermost loop: go through the 3 kinds of differences (inserts, deletes, updates)
                for oid in del_oids:
                    VersionChangesTally += 1
                    Primary = "<unknown>"

                    xls_file.row(child_version, curr_feature, "Deletes", Primary, oid, parent_otherid_dict[oid]) #"other")

                for oid in update_oids:
                    VersionChangesTally += 1
                    Primary = "<unknown>"
                    xls_file.row(child_version, curr_feature, "Updates", Primary, oid, child_otherid_dict[oid]) #"other")

                for oid in add_oids:
                    VersionChangesTally += 1
                    Primary = "<unknown>"
                    xls_file.row(child_version, curr_feature, "Inserts", Primary, oid, child_otherid_dict[oid]) #"other")


                #arcpy.AddMessage(f"VERSION CHANGE COUNT: {VersionChangesTally}")

            except arcpy.ExecuteError:
                arcpy.AddMessage(arcpy.GetMessages(2))
            finally:
                # Clean up temporary table views
                if arcpy.Exists(parent_view):
                    arcpy.Delete_management(parent_view)
                if arcpy.Exists(child_view):
                    arcpy.Delete_management(child_view)

        # report tallied changes
        arcpy.AddMessage("{0} Version Changes = {1}".format(ver_name.name, VersionChangesTally))

 

0 Kudos
2 Replies
RobertKrisher
Esri Regular Contributor

If you're using branch versioning, you can use the Differences method on the Version object to get all the differences.

When the parent view is default, it's not enough to make a view looking at default. Instead, you must look at the ancestor moment of default that the child version references.

0 Kudos
kduerksen_nwic
Emerging Contributor
Thanks for your feedback, but I am not using branch versioning, so no
Version object or Differences method.
0 Kudos