Create a unique id based on the max value by created user

716
5
02-08-2023 11:06 AM
BrettGovernanti1
New Contributor III

Hi I'm trying to create a method to write new integer feature ID's based on the highest ID last used by the created user. I'm basing it off of the famous autoIncrement() calculate field python expression and using a search cursor to find the last value used by the creator and incrementing by 1. I am trying to include logic to create an ID even when the user does not exist or when a null value is encountered while looping through. In this particular example, I'm trying to write new FITID's for 4 selected water fitting features. The expression evaluates as valid, but the process completes with a "Warning 002858: WARNING 002858: Certain rows set to NULL due to error while evaluating python expression: File "<string>", line 5, in id". This is similar to the 2016 US Election post where the same error appears and that one had a problem with not having passed arguments into the function properly. Do you have any ideas that I might be able to try? 

FITID = 
id(!FITID!)

Code Block
last_id = 0
created_user = ""
def id(FITID):
global last_id
rows = arcpy.da.SearchCursor([FITID,created_user],
"{0} = '{1}'".format(created_user, "USER"))
for row in rows:
if row[0] is not None and isinstance(row[0], int) and row[0] > last_id:
last_id = row[0]
user_exists = True
if not user_exists:
last_id = "100000"
pStart = last_id + 1
pInterval = 1 #adjust interval value, if req'd
if (rec == 0):
rec = pStart
else:
rec = rec + pInterval
return rec   

 

Tags (2)
0 Kudos
5 Replies
by Anonymous User
Not applicable

it looks like your formatting is backwards and the field position in the sql is getting set to "".

 

created_user = ""
...
rows = arcpy.da.SearchCursor([FITID,created_user], "{0} = '{1}'".format(created_user, "USER"))

 

which gets set to:

 

rows = arcpy.da.SearchCursor([FITID, ""], "" = 'USER'"

 

Think you want something like this:

 

rows = arcpy.da.SearchCursor([FITID, 'USER'], "USER = '{}'".format(created_user))

 

Edit: It's also missing the path to the featureclass in the cursor (don't think that it knows/ can to refer to itself?)

 

Kepa
by Esri Contributor
Esri Contributor

Hi there,

Just adding up JeffK answer, on that snippet of code rec seems undefined to me.

Regards,

BrettGovernanti1
New Contributor III

Thanks for the tips. Ok, so I have a couple of updates. The scripts below sort of work. But with my requirement to give new features new unique ID's based off of the max ID for the created user + 1, the scripts skip the last_id by created_user and instead starts at the fallback number for when the created_user is not found.

When does it work? When the field is calculated, only the newly created records are selected. As we know, ArcGIS is going perform the calculation on the selected records only. Most of the time this is a blessing and a free benefit, but in this case, I am not sure how to have the script look in all values attributed to the created user and not just those that are selected in order to find the last ID used by created user and to create the next ID based on that max value. The scripts work when one existing record with an existing ID by created_user exists, then the ID field will first add +1 to the existing ID and calculate the following new records incrementally. See what I mean by sort of works? But not quite, hahaha.

The plan had been to get this calculate to work, then switch out feature names for arcpy.GetParameterAsText() values so that the end user could have a GUI experience and be able to select the feature and column needing new ID's. But alas, I guess I'll just stay with the regular way of doing things, until we can implement the unique id functionality in ArcGIS Pro after some updates on the horizon, but I thought I'd at least share what I have in case it might be a spark for someone. 

Version #1

FITID = newid()
def newid():
    global last_id
    created_user = "BGOVERNANTI"
    user_exists = False
    last_id = 0
    try:
        with arcpy.da.SearchCursor("Version\\WaterFitting", ["FITID", "created_user"],
                                      where_clause="{0} = '{1}'".format("created_user", created_user)) as cursor:
            for row in cursor:
                if row[0] is not None and isinstance(row[0], int) and row[0] > last_id:
                    last_id = row[0]
                    user_exists = True
        if user_exists:
            last_id = last_id + 1
        else:
            last_id = 10000000 + 1
        return last_id
    except Exception as e:
        print("Error occurred: {}".format(e))
    finally:
        del cursor

 

Verson #2

FITID = newid()
def newid():
    global last_id
    created_user = "BGOVERNANTI"
    user_exists = False
    last_id = 0
    try:
        with arcpy.da.SearchCursor("Version\\WaterFitting", ["FITID", "created_user"]) as cursor:
            for row in cursor:
                if row[1] == created_user:
                    if row[0] and isinstance(row[0], int) and row[0] > last_id:
                        last_id = row[0]
                        user_exists = True
        if user_exists:
            last_id = last_id + 1
        else:
            last_id = 10000000 + 1
        return last_id
    except Exception as e:
        print("Error occurred: {}".format(e))
    finally:
        del cursor

 

0 Kudos
by Anonymous User
Not applicable

Knowing that this will eventually be a script tool, you have a lot more options to create this uniqueid.  I'd bring the data into a dictionary with a SearchCursor, then work against the dictionary as the items are inserted, getting and setting the most recent unique id they are currently on.

created_user = 'new guy?'

# get the max value of the user's ID as value to their name or return 100000000000 if they do not exist.
userDict = {created_user: max([row[1] for row in arcpy.da.SearchCursor("Version\\WaterFitting", ["created_user", "IDFIELD"],
                                                     "created_user = 'BGOVERNANTI'")], default=10000000)}

with arcpy.da.UpdateCursor("Version\\WaterFitting", ["created_user", "IDFIELD"], 'where_clause') as uCur:
    for row in uCur:
        userDict[created_user] += 1 #<- Increment the uniqueId from the previous value
        row[0] = row[0] if not row[0] else created_user #<- uses old name or uses the new name, if row[0] is null (assuming here)
        row[1] = userDict.get(created_user) #<- set the unique ID
        uCur.updateRow(row)
RhettZufelt
MVP Frequent Contributor

Not where I can test it, but once you have the layer, you could make a feature layer from the layer datasource :

fl = arcpy.management.MakeFeatureLayer(lyr.dataSource, lyr.name + "_layer")

Then feed that feature layer into the search cursor.

That should allow you to search all values in the dataset for ID's, not just the "selected" features.

R_