Hello everyone,
I am getting the following error after running my script:
The script runs fine when I am not in a version but once I change it to a version, it breaks after the first UpdateCursor. Note: the imported "cursors" module is from this post: https://community.esri.com/t5/python-questions/the-requested-operation-is-invalid-on-a-closed-state/...
import arcpy
import datetime
import cursors
import os
def calcNorthing():
# userInput_Layer = "{}_Layer".format(userInput)
# arcpy.MakeFeatureLayer_management(userInput, userInput_Layer)
try:
# calculate northing
if 'Northing' not in lstFields:
arcpy.AddField_management(userInput, "Northing", "DOUBLE")
print("Calculating Northing Field")
arcpy.AddMessage("Calculating Northing Field")
with cursors.UpdateCursor(userInput, ['Northing', 'Shape@'], "Northing IS NULL") as cursor:
for row in cursor:
if row[1] is not None:
pt = row[1].projectAs('2236').centroid
row[0] = round(pt.Y, 3)
cursor.updateRow(row)
print("Completed Northing calculation")
arcpy.AddMessage("Completed Northing calculation")
# del cursor
except():
arcpy.AddMessage("Failed Northing calculation.")
# finally:
def calcEasting():
try:
# calculate easting
if 'Easting' not in lstFields:
arcpy.AddField_management(userInput, "Easting", "DOUBLE")
print("Calculating Easting Field")
arcpy.AddMessage("Calculating Easting Field")
with cursors.UpdateCursor(userInput, ['Easting', 'Shape@'], "Easting IS NULL") as cursor:
for row in cursor:
if row[1] is not None:
pt = row[1].projectAs('2236').centroid
row[0] = round(pt.X, 3)
cursor.updateRow(row)
except():
arcpy.AddMessage("Failed Easting calculation.")
finally:
print("Completed Easting calculation")
arcpy.AddMessage("Completed Easting calculation")
def calcLongitude():
try:
# calculate longitude
if 'Longitude' not in lstFields:
arcpy.AddField_management(userInput, "Longitude", "DOUBLE")
print("Calculating Longitude Field")
arcpy.AddMessage("Calculating Longitude Field")
with cursors.UpdateCursor(userInput, ['Longitude', 'Shape@'], "Longitude IS NULL or Longitude < -82") as cursor:
for row in cursor:
if row[1] is not None:
pt = row[1].projectAs('4326').centroid
row[0] = round(pt.X, 6)
cursor.updateRow(row)
finally:
print("Completed Longitude calculation")
arcpy.AddMessage("Completed Longitude calculation")
def calcLatitude():
try:
# calculate latitude
if 'Latitude' not in lstFields:
arcpy.AddField_management(userInput, "Latitude", "DOUBLE")
print("Calculating Latitude Field")
arcpy.AddMessage("Calculating Latitude Field")
with cursors.UpdateCursor(userInput, ['Latitude', 'Shape@'], "Latitude IS NULL or Latitude < 0") as cursor:
for row in cursor:
if row[1] is not None:
pt = row[1].projectAs('4326').centroid
row[0] = round(pt.Y, 6)
cursor.updateRow(row)
finally:
print("Completed Latitude calculation")
arcpy.AddMessage("Completed Latitude calculation")
def calcSLOCSEQ():
try:
# calculate SERVICE_LOC_SEQ
if 'S_LOC_SEQ' in lstFields:
print("Calculating SERVICE_LOC_SEQ")
arcpy.AddMessage("Calculating SERVICE_LOC_SEQ")
with cursors.UpdateCursor(userInput, ['S_LOC_SEQ', 'METER_NUMB']) as cursor:
for row in cursor:
if row[1] is None:
print ("Bad value")
elif row[1].isnumeric():
row[0] = row[1]
# row[7] = datetime.datetime.now()
cursor.updateRow(row)
else:
print("Field does not exist.")
arcpy.AddMessage("Field does not exist. No calculation performed.")
finally:
arcpy.AddMessage("Completed SERVICE_LOC_SEQ calculation")
def calcDuplicates():
try:
if 'METER_NUMB' in lstFields:
arcpy.AddMessage("Searching for Duplicates. A value more than 1 in the \"Point_ID\" field represents number of duplicae values")
infeature = userInput
field_in = 'METER_NUMB'
field_out = 'Point_ID'
# creating the list with all the values in the field, including duplicates
lista = []
with arcpy.da.SearchCursor(infeature, ['METER_NUMB']) as cursor1:
for row in cursor1:
lista.append(row[0])
# updating the count field with the number on occurrences of field_in values
# in the previously created list
cursor2 = arcpy.UpdateCursor(infeature)
for row in cursor2:
i = row.getValue(field_in)
occ = lista.count(i)
row.setValue(field_out, occ)
cursor2.updateRow(row)
del cursor2
del row
print("Done.")
else:
print("METER_NUMB field does not exist. No duplicate check performed.")
except():
arcpy.AddMessage("Calculating duplicate process failed")
finally:
arcpy.AddMessage("Completed duplicate calculation")
if __name__ == '__main__':
startTime = datetime.datetime.now().replace(microsecond=0)
print("Operation started on {}".format(startTime))
arcpy.AddMessage("Operation started on {}".format(startTime))
arcpy.env.overwriteOutput = True
userInput = arcpy.GetParameterAsText(0) #point data here
lstFields = [f.name for f in arcpy.ListFields(userInput)]
calcNorthing()
calcEasting()
calcLongitude()
calcLatitude()
calcSLOCSEQ()
calcDuplicates()
arcpy.SelectLayerByAttribute_management(userInput, "CLEAR_SELECTION") #attempt to refresh table
endTime = datetime.datetime.now().replace(microsecond=0)
dur = endTime - startTime
dur = str(dur)
print('Duration: {}'.format(dur))
arcpy.AddMessage('Duration: {}'.format(dur))
Solved! Go to Solution.
So after moving my edit start and stop sessions to every conceivable combination, I decided to take a look at the custom cursor module again since it starts and stops for me. After some research, I found some changes to the cursor module. Well, lo and behold, I got my code working again.
Thanks all for your help!
Code for UpdateCursor Module:
# for python 2.7
import arcpy
import os
def find_ws(path, ws_type=''):
"""finds a valid workspace path for an arcpy.da.Editor() Session
Required:
path -- path to features or workspace
Optional:
ws_type -- option to find specific workspace type (FileSystem|LocalDatabase|RemoteDatabase)
"""
# try original path first
if os.sep not in path:
path = arcpy.Describe(path).catalogPath
desc = arcpy.Describe(path)
if hasattr(desc, 'workspaceType'):
if ws_type and ws_type == desc.workspaceType:
return path
elif not ws_type:
return path
# search until finding a valid workspace
SPLIT = filter(None, path.split(os.sep))
if path.startswith('\\\\'):
SPLIT[0] = r'\\{0}'.format(SPLIT[0])
# find valid workspace
for i in xrange(1, len(SPLIT)):
sub_dir = os.sep.join(SPLIT[:-i])
desc = arcpy.Describe(sub_dir)
if hasattr(desc, 'workspaceType'):
if ws_type and ws_type == desc.workspaceType:
return sub_dir
elif not ws_type:
return sub_dir
class UpdateCursor(object):
"""wrapper class for arcpy.da.UpdateCursor, to automatically
implement editing (required for versioned data, and data with
geometric networks, topologies, network datasets, and relationship
classes"""
def __init__(self, *args, **kwargs):
"""initiate wrapper class for update cursor. Supported args:
in_table, field_names, where_clause=None, spatial_reference=None,
explode_to_points=False, sql_clause=(None, None)
"""
self.args = args
self.kwargs = kwargs
self.edit = None
def __enter__(self):
ws = None
if self.args:
ws = find_ws(self.args[0])
elif 'in_table' in self.kwargs:
ws = find_ws(self.kwargs['in_table'])
self.edit = arcpy.da.Editor(ws)
self.edit.startEditing()
self.edit.startOperation()
return arcpy.da.UpdateCursor(*self.args, **self.kwargs)
def __exit__(self, type, value, traceback):
self.edit.stopOperation()
self.edit.stopEditing(True)
self.edit = None
And my code :
# works for python2.7
import arcpy
import datetime
import cursors
def calcNorthing():
try:
# calculate northing
# edit.startEditing(False, True)
# edit.startOperation()
if 'Northing' not in lstFields:
arcpy.AddField_management(userInput, "Northing", "DOUBLE")
# edit.stopOperation(True)
# edit.stopEditing()
print("Calculating Northing Field")
arcpy.AddMessage("Calculating Northing Field")
with cursors.UpdateCursor(userInput, ['Northing', 'Shape@'], "Northing IS NULL") as cursor:
for row in cursor:
if row[1] is not None:
pt = row[1].projectAs('2236').centroid
row[0] = round(pt.Y, 3)
cursor.updateRow(row)
print("Completed Northing calculation")
arcpy.AddMessage("Completed Northing calculation")
except():
arcpy.AddMessage("Failed Northing calculation.")
def calcEasting():
try:
# calculate easting
if 'Easting' not in lstFields:
arcpy.AddField_management(userInput, "Easting", "DOUBLE")
print("Calculating Easting Field")
arcpy.AddMessage("Calculating Easting Field")
with cursors.UpdateCursor(userInput, ['Easting', 'Shape@'], "Easting IS NULL") as cursor:
for row in cursor:
if row[1] is not None:
pt = row[1].projectAs('2236').centroid
row[0] = round(pt.X, 3)
cursor.updateRow(row)
except():
arcpy.AddMessage("Failed Easting calculation.")
finally:
print("Completed Easting calculation")
arcpy.AddMessage("Completed Easting calculation")
def calcLongitude():
try:
# calculate longitude
if 'Longitude' not in lstFields:
arcpy.AddField_management(userInput, "Longitude", "DOUBLE")
print("Calculating Longitude Field")
arcpy.AddMessage("Calculating Longitude Field")
with cursors.UpdateCursor(userInput, ['Longitude', 'Shape@'], "Longitude IS NULL or Longitude < -82") as cursor:
for row in cursor:
if row[1] is not None:
pt = row[1].projectAs('4326').centroid
row[0] = round(pt.X, 6)
cursor.updateRow(row)
finally:
print("Completed Longitude calculation")
arcpy.AddMessage("Completed Longitude calculation")
def calcLatitude():
try:
# calculate latitude
if 'Latitude' not in lstFields:
arcpy.AddField_management(userInput, "Latitude", "DOUBLE")
print("Calculating Latitude Field")
arcpy.AddMessage("Calculating Latitude Field")
with cursors.UpdateCursor(userInput, ['Latitude', 'Shape@'], "Latitude IS NULL or Latitude < 0") as cursor:
for row in cursor:
if row[1] is not None:
pt = row[1].projectAs('4326').centroid
row[0] = round(pt.Y, 6)
cursor.updateRow(row)
finally:
print("Completed Latitude calculation")
arcpy.AddMessage("Completed Latitude calculation")
def calcSLOCSEQ():
try:
# calculate SERVICE_LOC_SEQ
if 'S_LOC_SEQ' in lstFields:
print("Calculating SERVICE_LOC_SEQ")
arcpy.AddMessage("Calculating SERVICE_LOC_SEQ")
with cursors.UpdateCursor(userInput, ['S_LOC_SEQ', 'METER_NUMB', 'OBJECTID'], 'S_LOC_SEQ IS NULL') as cursor:
for row in cursor:
if row[1] is None:
print ("OBJECTID #{} has no meter number".format(row[2]))
elif row[1].isnumeric():
row[0] = row[1]
# row[7] = datetime.datetime.now()
elif not row[1].isnumeric():
print ("Could not calculate OBJECTID #{}. Check for a bad number.".format(row[2]))
cursor.updateRow(row)
else:
print("Field does not exist.")
arcpy.AddMessage("Field does not exist. No calculation performed.")
finally:
arcpy.AddMessage("Completed SERVICE_LOC_SEQ calculation")
def calcDuplicates():
try:
if 'METER_NUMB' in lstFields:
arcpy.AddMessage("Searching for Duplicates. A value more than 1 in the \"Point_ID\" field represents number of duplicae values")
infeature = userInput
field_in = 'METER_NUMB'
field_out = 'Point_ID'
# creating the list with all the values in the field, including duplicates
lista = []
with arcpy.da.SearchCursor(infeature, ['METER_NUMB']) as cursor1:
for row in cursor1:
lista.append(row[0])
# updating the count field with the number on occurrences of field_in values
# in the previously created list
cursor2 = arcpy.UpdateCursor(infeature)
for row in cursor2:
i = row.getValue(field_in)
occ = lista.count(i)
row.setValue(field_out, occ)
cursor2.updateRow(row)
del cursor2
del row
print("Done.")
else:
print("METER_NUMB field does not exist. No duplicate check performed.")
except():
arcpy.AddMessage("Calculating duplicate process failed")
finally:
arcpy.AddMessage("Completed duplicate calculation")
if __name__ == '__main__':
startTime = datetime.datetime.now().replace(microsecond=0)
print("Operation started on {}".format(startTime))
arcpy.AddMessage("Operation started on {}".format(startTime))
arcpy.env.overwriteOutput = True
userInput = arcpy.GetParameterAsText(0)
lstFields = [f.name for f in arcpy.ListFields(userInput)]
workspace = "Database Connections\\wGISProd@GISdb.OSA.sde"
edit = arcpy.da.Editor(workspace)
calcNorthing()
calcEasting()
calcLongitude()
calcLatitude()
calcSLOCSEQ()
calcDuplicates()
arcpy.SelectLayerByAttribute_management(userInput, "CLEAR_SELECTION")
endTime = datetime.datetime.now().replace(microsecond=0)
dur = endTime - startTime
dur = str(dur)
print('Duration: {}'.format(dur))
arcpy.AddMessage('Duration: {}'.format(dur))
you tried the stopediting(true) then starting editing again as per the post? I have seen some issues using the 'with' statement also, perhaps try the cursors without that. unaware of the cursors module bit will have a look.
I'm not familiar with importing the cursors module. Instead I suggest you use the da.UpdateCursor.
When editing an enterprise geodatabase (aka SDE), you need to start an edit session and close it it as well:
ws = r'X:\some\path\to\your\enterprise.gdb'
edit = arcpy.da.Editor(ws)
edit.startEditing(False,False) #see the documentation on the parameters
edit.startOperation()
with arcpy.da.UpdateCursor(blah, blah bla) as cursor:
for row in cursor:
blah blah blah
cursor.updateRow(row)
edit.stopOperation()
edit.stopEditing(True)
@DavidPike , using 'with' in any da cursor is traditionally the way to go; I've used that approach hundreds of times without an issue...
Sure, I just remember seeing something about it before rather than from experience. More of a lead or something to try at least. When you bring in enterprise GDBs into the equation, this may be an underlying database you've not dealt with before. Although you'd hope the SDE interactions would be the same across all of them, Esri isn't without its 'quirks' surely?
So maybe I am misunderstanding something about edit sessions within functions. Right now I tried testing it like this but I now get this error:
def calcNorthing():
try:
# calculate northing
edit.startEditing(False, True)
edit.startOperation()
if 'Northing' not in lstFields:
arcpy.AddField_management(userInput, "Northing", "DOUBLE")
print("Calculating Northing Field")
arcpy.AddMessage("Calculating Northing Field")
with arcpy.da.UpdateCursor(userInput, ['Northing', 'Shape@'], "Northing IS NULL") as cursor:
for row in cursor:
if row[1] is not None:
pt = row[1].projectAs('2236').centroid
row[0] = round(pt.Y, 3)
cursor.updateRow(row)
print("Completed Northing calculation")
arcpy.AddMessage("Completed Northing calculation")
edit.stopOperation()
edit.stopEditing(True)
# del cursor
except():
arcpy.AddMessage("Failed Northing calculation.")
if __name__ == '__main__':
startTime = datetime.datetime.now().replace(microsecond=0)
print("Operation started on {}".format(startTime))
arcpy.AddMessage("Operation started on {}".format(startTime))
arcpy.env.overwriteOutput = True
userInput = arcpy.GetParameterAsText(0)
lstFields = [f.name for f in arcpy.ListFields(userInput)]
workspace = "Database Connections\\wGISProd@GISdb.OSA.sde"
edit = arcpy.da.Editor(workspace)
# edit.startEditing(False, True)
# edit.startOperation()
calcNorthing()
# calcEasting()
# calcLongitude()
# calcLatitude()
# calcSLOCSEQ()
# calcDuplicates()
arcpy.SelectLayerByAttribute_management(userInput, "CLEAR_SELECTION")
endTime = datetime.datetime.now().replace(microsecond=0)
dur = endTime - startTime
dur = str(dur)
print('Duration: {}'.format(dur))
arcpy.AddMessage('Duration: {}'.format(dur))
Do you have an edit session started in any Desktop GIS software (ie ArcPro or ArcMap)?
I did not. Right now my code works correctly whether or not I am in an edit session.
The only thing I can think of is perhaps make your edit calls outside of the try/except block:
def calcNorthing():
edit.StartEditing(False,True)
edit.startOperation()
try:
do your thing
except:
trap the exception
edit.stopOperation()
edit.stopEditing(True)
You could also check the edit status in main() to be sure if it's stopped or not before making the call to your function.
Take a look at this too..
So after moving my edit start and stop sessions to every conceivable combination, I decided to take a look at the custom cursor module again since it starts and stops for me. After some research, I found some changes to the cursor module. Well, lo and behold, I got my code working again.
Thanks all for your help!
Code for UpdateCursor Module:
# for python 2.7
import arcpy
import os
def find_ws(path, ws_type=''):
"""finds a valid workspace path for an arcpy.da.Editor() Session
Required:
path -- path to features or workspace
Optional:
ws_type -- option to find specific workspace type (FileSystem|LocalDatabase|RemoteDatabase)
"""
# try original path first
if os.sep not in path:
path = arcpy.Describe(path).catalogPath
desc = arcpy.Describe(path)
if hasattr(desc, 'workspaceType'):
if ws_type and ws_type == desc.workspaceType:
return path
elif not ws_type:
return path
# search until finding a valid workspace
SPLIT = filter(None, path.split(os.sep))
if path.startswith('\\\\'):
SPLIT[0] = r'\\{0}'.format(SPLIT[0])
# find valid workspace
for i in xrange(1, len(SPLIT)):
sub_dir = os.sep.join(SPLIT[:-i])
desc = arcpy.Describe(sub_dir)
if hasattr(desc, 'workspaceType'):
if ws_type and ws_type == desc.workspaceType:
return sub_dir
elif not ws_type:
return sub_dir
class UpdateCursor(object):
"""wrapper class for arcpy.da.UpdateCursor, to automatically
implement editing (required for versioned data, and data with
geometric networks, topologies, network datasets, and relationship
classes"""
def __init__(self, *args, **kwargs):
"""initiate wrapper class for update cursor. Supported args:
in_table, field_names, where_clause=None, spatial_reference=None,
explode_to_points=False, sql_clause=(None, None)
"""
self.args = args
self.kwargs = kwargs
self.edit = None
def __enter__(self):
ws = None
if self.args:
ws = find_ws(self.args[0])
elif 'in_table' in self.kwargs:
ws = find_ws(self.kwargs['in_table'])
self.edit = arcpy.da.Editor(ws)
self.edit.startEditing()
self.edit.startOperation()
return arcpy.da.UpdateCursor(*self.args, **self.kwargs)
def __exit__(self, type, value, traceback):
self.edit.stopOperation()
self.edit.stopEditing(True)
self.edit = None
And my code :
# works for python2.7
import arcpy
import datetime
import cursors
def calcNorthing():
try:
# calculate northing
# edit.startEditing(False, True)
# edit.startOperation()
if 'Northing' not in lstFields:
arcpy.AddField_management(userInput, "Northing", "DOUBLE")
# edit.stopOperation(True)
# edit.stopEditing()
print("Calculating Northing Field")
arcpy.AddMessage("Calculating Northing Field")
with cursors.UpdateCursor(userInput, ['Northing', 'Shape@'], "Northing IS NULL") as cursor:
for row in cursor:
if row[1] is not None:
pt = row[1].projectAs('2236').centroid
row[0] = round(pt.Y, 3)
cursor.updateRow(row)
print("Completed Northing calculation")
arcpy.AddMessage("Completed Northing calculation")
except():
arcpy.AddMessage("Failed Northing calculation.")
def calcEasting():
try:
# calculate easting
if 'Easting' not in lstFields:
arcpy.AddField_management(userInput, "Easting", "DOUBLE")
print("Calculating Easting Field")
arcpy.AddMessage("Calculating Easting Field")
with cursors.UpdateCursor(userInput, ['Easting', 'Shape@'], "Easting IS NULL") as cursor:
for row in cursor:
if row[1] is not None:
pt = row[1].projectAs('2236').centroid
row[0] = round(pt.X, 3)
cursor.updateRow(row)
except():
arcpy.AddMessage("Failed Easting calculation.")
finally:
print("Completed Easting calculation")
arcpy.AddMessage("Completed Easting calculation")
def calcLongitude():
try:
# calculate longitude
if 'Longitude' not in lstFields:
arcpy.AddField_management(userInput, "Longitude", "DOUBLE")
print("Calculating Longitude Field")
arcpy.AddMessage("Calculating Longitude Field")
with cursors.UpdateCursor(userInput, ['Longitude', 'Shape@'], "Longitude IS NULL or Longitude < -82") as cursor:
for row in cursor:
if row[1] is not None:
pt = row[1].projectAs('4326').centroid
row[0] = round(pt.X, 6)
cursor.updateRow(row)
finally:
print("Completed Longitude calculation")
arcpy.AddMessage("Completed Longitude calculation")
def calcLatitude():
try:
# calculate latitude
if 'Latitude' not in lstFields:
arcpy.AddField_management(userInput, "Latitude", "DOUBLE")
print("Calculating Latitude Field")
arcpy.AddMessage("Calculating Latitude Field")
with cursors.UpdateCursor(userInput, ['Latitude', 'Shape@'], "Latitude IS NULL or Latitude < 0") as cursor:
for row in cursor:
if row[1] is not None:
pt = row[1].projectAs('4326').centroid
row[0] = round(pt.Y, 6)
cursor.updateRow(row)
finally:
print("Completed Latitude calculation")
arcpy.AddMessage("Completed Latitude calculation")
def calcSLOCSEQ():
try:
# calculate SERVICE_LOC_SEQ
if 'S_LOC_SEQ' in lstFields:
print("Calculating SERVICE_LOC_SEQ")
arcpy.AddMessage("Calculating SERVICE_LOC_SEQ")
with cursors.UpdateCursor(userInput, ['S_LOC_SEQ', 'METER_NUMB', 'OBJECTID'], 'S_LOC_SEQ IS NULL') as cursor:
for row in cursor:
if row[1] is None:
print ("OBJECTID #{} has no meter number".format(row[2]))
elif row[1].isnumeric():
row[0] = row[1]
# row[7] = datetime.datetime.now()
elif not row[1].isnumeric():
print ("Could not calculate OBJECTID #{}. Check for a bad number.".format(row[2]))
cursor.updateRow(row)
else:
print("Field does not exist.")
arcpy.AddMessage("Field does not exist. No calculation performed.")
finally:
arcpy.AddMessage("Completed SERVICE_LOC_SEQ calculation")
def calcDuplicates():
try:
if 'METER_NUMB' in lstFields:
arcpy.AddMessage("Searching for Duplicates. A value more than 1 in the \"Point_ID\" field represents number of duplicae values")
infeature = userInput
field_in = 'METER_NUMB'
field_out = 'Point_ID'
# creating the list with all the values in the field, including duplicates
lista = []
with arcpy.da.SearchCursor(infeature, ['METER_NUMB']) as cursor1:
for row in cursor1:
lista.append(row[0])
# updating the count field with the number on occurrences of field_in values
# in the previously created list
cursor2 = arcpy.UpdateCursor(infeature)
for row in cursor2:
i = row.getValue(field_in)
occ = lista.count(i)
row.setValue(field_out, occ)
cursor2.updateRow(row)
del cursor2
del row
print("Done.")
else:
print("METER_NUMB field does not exist. No duplicate check performed.")
except():
arcpy.AddMessage("Calculating duplicate process failed")
finally:
arcpy.AddMessage("Completed duplicate calculation")
if __name__ == '__main__':
startTime = datetime.datetime.now().replace(microsecond=0)
print("Operation started on {}".format(startTime))
arcpy.AddMessage("Operation started on {}".format(startTime))
arcpy.env.overwriteOutput = True
userInput = arcpy.GetParameterAsText(0)
lstFields = [f.name for f in arcpy.ListFields(userInput)]
workspace = "Database Connections\\wGISProd@GISdb.OSA.sde"
edit = arcpy.da.Editor(workspace)
calcNorthing()
calcEasting()
calcLongitude()
calcLatitude()
calcSLOCSEQ()
calcDuplicates()
arcpy.SelectLayerByAttribute_management(userInput, "CLEAR_SELECTION")
endTime = datetime.datetime.now().replace(microsecond=0)
dur = endTime - startTime
dur = str(dur)
print('Duration: {}'.format(dur))
arcpy.AddMessage('Duration: {}'.format(dur))