Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract_Changes failing to return proper edit arrays #1822

Open
VFRoderick opened this issue May 11, 2024 · 3 comments
Open

Extract_Changes failing to return proper edit arrays #1822

VFRoderick opened this issue May 11, 2024 · 3 comments
Labels

Comments

@VFRoderick
Copy link

Describe the bug
A clear and concise description of what the bug is.
Extract_Changes should return features of adds, updates, and deletes when the appropriate boolean parameter is set for edit type. If all parameters are set to true should return all. When a single serverGen number is used should return the appropriate edit type for all changes up until that server generation. When using a list range should return changes between server Gens. Adds and updates seem to return properly but updates do not.
Tested using various servergen parameters [minServerGen],[serverGen],[minServerGen,serverGen] and the latest layers Server Gens. Furthermore, serverGens were periodically polled to receive the latest after edits were made to test layers.

To Reproduce
Steps to reproduce the behavior:
Create a new feature layer collection of point and table layers, with sync and change tracking enabled. Add a new string field 'Name' to both.
check feature layer collection to make sure extract_changes can be conducted.
return latest server gens.
Examine Extract_Changes for various parameters, all payloads should be empty since they're are no features in point layer or table.
Add features to layers and table.
Return latest server gens and extract changes. SHOULD ONLY SEE ADDS
Create new features and update previous features. Return latest server gens and extract changes. SHOULD ONLY SEE ADDS AND UPDATES.
Finally, delete all features.
Return latest server gens and extract changes. SHOULD SEE ADDS, UPDATES, and DELETES.

import arcgis
from arcgis.gis import GIS
gis = GIS("home")
pip install pyproj
from arcgis.features import FeatureLayerCollection, GeoAccessor, GeoSeriesAccessor, FeatureLayer, FeatureSet, Feature
from arcgis.geometry import Geometry, Point, Polyline, Polygon, MultiPoint, project
from random import uniform, randrange
import inspect
from pyproj import Transformer
import time
extract_changes_test_item = gis.content.get('cc1c210c14ba4525a2f97e141070f02d')
transformer = Transformer.from_crs("epsg:4326", "epsg:3857", always_xy=True)

def print_feature_sets(flc):
    for lyr in flc.layers + flc.tables:
        fs = lyr.query()
        print(fs)

def check_for_changes(payload):
    no_changes = True
    for edit in payload['edits']:
        if 'id' in edit.keys():
            layer_id = edit['id']
        if 'features' in edit.keys():
            adds,updates,deletes=[],[],[]
            features = edit['features']
            if 'adds' in features.keys():
                adds = features['adds']
            if 'updates' in features.keys():
                updates = features['updates']
            if 'deletes' in features.keys():
                deletes = features['deletes']
        
        if adds or updates or deletes:
            no_changes = False
            print(f"Changes detected in layer {layer_id}:")
            if adds:
                print(f"  Adds: {len(adds)}")
            if updates:
                print(f"  Updates: {len(updates)}")
            if deletes:
                print(f"  Deletes: {len(deletes)}")
    if no_changes:
        print("No changes detected across all layers.")
    
def return_current_flc_properties(flc):
    capabilities = flc.properties.capabilities
    print(f"Current capabilities are: {capabilities}")
    extractCapabilities = flc.properties.get("extractChangesCapabilities","no support")
    print(f"Current extract changes capabilities: {extractCapabilities}")
    ServerGens = []
    SRs = []
    changeTrackingInfo = flc.properties.changeTrackingInfo
    layerServerGens = changeTrackingInfo['layerServerGens']
    layer_ids = []
    for lyr in flc.layers + flc.tables:
        layer_ids.append(lyr.properties.id)
        if 'geometryType' in lyr.properties:
            print(f"ID: {lyr.properties.id} NAME: {lyr.properties.name} Type: {lyr.properties.type} GeoType: {lyr.properties.geometryType} Record Count: {lyr.query(where='1=1', return_count_only=True)}")
            SRs.append(lyr.properties.extent.spatialReference)
        else:
            print(f"ID: {lyr.properties.id} NAME: {lyr.properties.name} Type: {lyr.properties.type} GeoType: None Record Count: {lyr.query(where='1=1', return_count_only=True)}")
        ServerGens.append(lyr.properties.serverGens)
    for idx,SG in enumerate(ServerGens):
        id_mismatch,minServerGen_mismatch,serverGen_mismatch = False,False,False
        lyrSG = layerServerGens[idx]
        if idx != lyrSG['id']:
            id_mismatch = True
        if SG['minServerGen'] != lyrSG['minServerGen']:
            minServerGen_mismatch = True
        if SG['serverGen'] != lyrSG['serverGen']:
            serverGen_mismatch = True
        if id_mismatch or minServerGen_mismatch or serverGen_mismatch:
            print(f"Mismatch detected at idx {idx} for {SG} and {lyrSG}")
    print(f"ServerGens: {ServerGens}")
    print(f"layerServerGens: {layerServerGens}")
    
    return ServerGens,layerServerGens,layer_ids

def check_extract_changes_by_diff_servergens(flc,ServerGens,layer_ids):
    # check using different servergen parameters
    minServerGen = ServerGens[0]['minServerGen']
    serverGen = ServerGens[0]['serverGen']
    serverGen_options = [[minServerGen],[serverGen],[minServerGen,serverGen]]
    for option in serverGen_options:
        print(f"servergen={option}\n\n")
        try:
            print("If changes should be adds only")
            insert_only = flc.extract_changes(layers=layer_ids,servergen=option,layer_servergen=None,return_inserts=True,return_updates=False,return_deletes=False,return_attachments=False,return_geometry_updates=False)
            check_for_changes(insert_only)
            print("\nIf changes should be updates only")
            updates_only = flc.extract_changes(layers=layer_ids,servergen=option,layer_servergen=None,return_inserts=False,return_updates=True,return_deletes=False,return_attachments=False,return_geometry_updates=False)
            check_for_changes(updates_only)
            print("\nIf changes should be deletes only")
            deletes_only = flc.extract_changes(layers=layer_ids,servergen=option,layer_servergen=None,return_inserts=False,return_updates=False,return_deletes=True,return_attachments=False,return_geometry_updates=False)
            check_for_changes(deletes_only)
            print("\nIf changes should RETURN ALL CHANGES")
            all_only = flc.extract_changes(layers=layer_ids,servergen=option,layer_servergen=None,return_inserts=True,return_updates=True,return_deletes=True,return_attachments=True,return_geometry_updates=True)
            check_for_changes(all_only)
        except Exception as e:
            print(e)
            
def check_extract_changes_by_layer_servergen(flc,layerServerGens,layer_ids):
    # Check using layer_servergen parameter
    print(f"layer_servergen={layerServerGens}\n\n")
    try:
        print("If changes should be adds only")
        insert_only = flc.extract_changes(layers=layer_ids,servergen=None,layer_servergen=layerServerGens,return_inserts=True,return_updates=False,return_deletes=False,return_attachments=False,return_geometry_updates=False)
        check_for_changes(insert_only)
        print("\nIf changes should be updates only")
        updates_only = flc.extract_changes(layers=layer_ids,servergen=None,layer_servergen=layerServerGens,return_inserts=False,return_updates=True,return_deletes=False,return_attachments=False,return_geometry_updates=False)
        check_for_changes(updates_only)
        print("\nIf changes should be deletes only")
        deletes_only = flc.extract_changes(layers=layer_ids,servergen=None,layer_servergen=layerServerGens,return_inserts=False,return_updates=False,return_deletes=True,return_attachments=False,return_geometry_updates=False)
        check_for_changes(deletes_only)
        print("\nIf changes should RETURN ALL CHANGES")
        all_only = flc.extract_changes(layers=layer_ids,servergen=None,layer_servergen=layerServerGens,return_inserts=True,return_updates=True,return_deletes=True,return_attachments=True,return_geometry_updates=True)
        check_for_changes(all_only)
    except Exception as e:
        print(e)
            
def gen_random_coord():
    # Generate random longitude and latitude
    longitude = round(uniform(-120, -119), 2)
    latitude = round(uniform(30, 31), 2)

    # Transform the coordinates from WGS 84 to Web Mercator
    x_3857, y_3857 = transformer.transform(longitude, latitude)
    return x_3857, y_3857

def create_point():
    x, y = gen_random_coord()
    # Create a point geometry
    pt = Point({
        "x": x,
        "y": y,
        "spatialReference": {"wkid": 3857}
    })
    if pt.is_valid():
        return Geometry(pt)
    
def create_adds(flc):
    # Add Features to each layer and table
    for lyr in flc.layers + flc.tables:
        fields = lyr.properties.fields
        features = []
        attributes = {'Name': 'ADD'}
        for i in range(5):
            if 'geometryType' in lyr.properties:
                if lyr.properties.geometryType == 'esriGeometryPoint':
                    geom = create_point()
                if geom:
                    feature = Feature(geometry=geom, attributes=attributes)
                    features.append(feature)
            else:
                feature = Feature(attributes=attributes)
                features.append(feature)
        if features:
            feature_set = FeatureSet(features=features,fields=fields)
            if feature_set:
                add_result = lyr.edit_features(adds=feature_set)
                print("Add Results:", add_result)
                
def create_updates(flc):      
    # Update Features by changing 'Name' from 'ADD' to 'UPDATE'
    for lyr in flc.layers + flc.tables:
        fs = lyr.query()
        for feature in fs.features:
            feature.set_value(field_name='Name',value='UPDATE')
        if fs:
            update_result = lyr.edit_features(updates=fs,use_global_ids=True)
            print("Update Results:", update_result)

            
def create_deletes(flc):
    for lyr in flc.layers + flc.tables:
        fs = lyr.query()
        if fs:
            delete_results = lyr.edit_features(deletes=fs)
            print("Delete Results:", delete_results)

flc = FeatureLayerCollection.fromitem(extract_changes_test_item)
print_feature_sets(flc)
ServerGens,layerServerGens,layer_ids = return_current_flc_properties(flc)
check_extract_changes_by_diff_servergens(flc,ServerGens,layer_ids)
check_extract_changes_by_layer_servergen(flc,layerServerGens,layer_ids)

print("################################################################################################################################")
create_adds(flc)
print_feature_sets(flc)
time.sleep(60)
print("Should see Adds ONLY")
ServerGens,layerServerGens,layer_ids = return_current_flc_properties(flc)
check_extract_changes_by_diff_servergens(flc,ServerGens,layer_ids)
check_extract_changes_by_layer_servergen(flc,layerServerGens,layer_ids)

print("################################################################################################################################")
create_updates(flc)
create_adds(flc)
print_feature_sets(flc)
print("Should see Adds ONLY for inserts Only, should see UPDATES only for updates only, should see BOTH with all")
time.sleep(60)
ServerGens,layerServerGens,layer_ids = return_current_flc_properties(flc)
check_extract_changes_by_diff_servergens(flc,ServerGens,layer_ids)
check_extract_changes_by_layer_servergen(flc,layerServerGens,layer_ids)

error:

<copy the full error message here>

Screenshots

Expected behavior
when updates are applied to layers or tables Extract_changes method is not detecting or returning them.

Platform (please complete the following information):

  • OS: Windows 11
  • Browser chrome
  • Python API Version 2.2.0.1
    Additional context
    Add any other context about the problem here, attachments etc.
@VFRoderick VFRoderick added the bug label May 11, 2024
@VFRoderick
Copy link
Author

some other weird phenomenon it looks as though updates are getting blended in with adds. when using first getting the latest servergen parameter using the latest values using:

changeTrackingInfo = flc.properties.changeTrackingInfo
last_sync_date, layer_server_gen = changeTrackingInfo.get('lastSyncDate', None), changeTrackingInfo.get('layerServerGens', [])
minserverGen = layer_server_gen[0]['minServerGen']
serverGen = layer_server_gen[0]['serverGen']
 print(flc.extract_changes(layers=[0, 1, 2, 3], servergen=[minserverGen,serverGen], layer_servergen=None, return_inserts=True, return_updates=True, return_deletes=True, return_attachments=True, return_geometry_updates=True))

This will show true adds where the 'Name' field wasn't updated, but no updates. Below will show the updates but these will be in the 'adds' array.

print(flc.extract_changes(layers=[0, 1, 2, 3], servergen=[minserverGen,serverGen], layer_servergen=None, return_inserts=True, return_updates=True, return_deletes=False, return_attachments=True, return_geometry_updates=True))

@nanaeaubry
Copy link
Contributor

@VFRoderick
Are you using ArcgisOnline or Enterprise for this workflow?

@VFRoderick
Copy link
Author

@nanaeaubry I'm using AGOL with a standard Notebook. That being said I'm seeing the same issues with connecting to data with our 11.1 Enterprise Portal environments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants