# import req libraries and packages
import arcpy
import os
import datetime
import pandas as pd
import datetime
import tempfile
import shutil
import schedule
def wm_update(): # if being called from script place req var in ()
import csv
# To allow overwriting outputs change overwriteOutput option to True.
global csv_file_path
arcpy.env.overwriteOutput = True
# variables
# dynamic var
now = datetime.datetime.now()
time_stamp = now.strftime("%m%d%Y")
AMI_export_Folder = r"C:\Users\AdamT\OneDrive - bentonvillear.com\Attachments\AMI_Export"
WaterMeter = []
print(f"Start Time: {now}")
for dirpath, dirnames, filenames in os.walk(AMI_export_Folder):
for file_name in filenames:
if file_name.endswith(".xlsx"):
print(f"Found XLSX: {file_name}")
input_file = os.path.join(AMI_export_Folder, file_name)
temp_csv_file = tempfile.NamedTemporaryFile(mode='w', delete=False, newline='')
# Use pandas to read the XLSX file and save it as CSV
df = pd.read_excel(input_file)
df.to_csv(temp_csv_file.name, index=False)
# Remove the first row from the CSV
with open(temp_csv_file.name, 'r', newline='') as file, tempfile.TemporaryFile(mode='w+', delete=False, newline='') as temp_file:
reader = csv.reader(file)
writer = csv.writer(temp_file)
for i, row in enumerate(reader):
if i > 2:
writer.writerow(row)
shutil.move(temp_file.name, input_file.replace(".xlsx", ".csv"))
WaterMeter.append(input_file.replace(".xlsx", ".csv"))
os.remove(input_file)
elif file_name.endswith(".csv"):
print(f"Found: {file_name}")
input_file = os.path.join(AMI_export_Folder, file_name)
temp_file = tempfile.NamedTemporaryFile(mode='w', delete=False, newline='')
# Remove the first row(s) from the CSV
with open(input_file, 'r', newline='') as file, temp_file:
reader = csv.reader(file)
writer = csv.writer(temp_file)
for i, row in enumerate(reader):
if i > 2:
writer.writerow(row)
shutil.move(temp_file.name, input_file)
WaterMeter.append(input_file)
print(f"{WaterMeter}")
# static var
existing_waterMeter_points = r"I:\GIS\Automation\Scripts\WaterMeter1\WaterMeter1.gdb\wWater_Meter1" # will need to be adjusted for sde feature
waterMeter_points_location = r"I:\GIS\a_SDE_Connections\Water_System_BWU_Edit.sde\Water_System_BWU.DBO.wUtility_Network\Water_System_BWU.DBO.Water_Meter_Locations" # will need to be adjusted for sde feature
processing_gdb = r"I:\GIS\ESRI_ArcPro_Edit\zDBO\default.gdb"
new_waterMeters = r'I:\GIS\ESRI_ArcPro_Edit\zDBO\default.gdb\New_WaterMeters' + time_stamp
out_table = r"I:\Total Data\Full Export usage\GIS_AMI_Export" + time_stamp + ".csv"
arcpy.CheckOutExtension("Spatial")
crs = arcpy.SpatialReference(4326)
arcpy.EnvManager(scratchWorkspace=arcpy.env.scratchGDB, workspace=processing_gdb)
# disconnect user from sde for script to implement
sde_connection =r"I:\GIS\a_SDE_Connections\Water_System_BWU.sde"
# start script
# create points
print(f"Starting Processing for: {WaterMeter}")
try:
for csv in WaterMeter:
water_meter_points = processing_gdb + "\\TempWaterMeterPoints" + time_stamp
# plot XY
arcpy.management.XYTableToPoint(in_table=csv, out_feature_class=water_meter_points, x_field='Longitude',
y_field='Latitude', z_field='', coordinate_system=crs)
# remove service type "Electric" and "NOT YET INSTALLED" value
print(f"Refining points: removing service type [ Electric ] and Service Serial Number [ NOT YET INSTALLED ]")
water_meter_refined = processing_gdb + "\\RefinedPoints" + time_stamp
arcpy.analysis.Select(
in_features=water_meter_points,
out_feature_class=water_meter_refined,
where_clause="ServiceType <> 'Electric' And OldSerialNumber <> 'NOT YET INSTALLED'"
)
# create field LocID for join calc field
print("Creating new field for LocID and calculating field")
arcpy.management.AddField(in_table=water_meter_refined, field_name="LocID", field_type="TEXT",
field_precision='', field_scale='', field_length='255',
field_alias='LocID', field_is_nullable='NULLABLE', field_is_required='NON_REQUIRED',
field_domain='')
arcpy.management.CalculateField(
in_table=water_meter_refined,
field="LocID",
expression="remove_chars(!SocketID!)",
expression_type="PYTHON3",
code_block="""def remove_chars(value):
return value[3:-5]""",
field_type="TEXT",
enforce_domains="NO_ENFORCE_DOMAINS"
)
# join from water meter 1 based on LOCID and SocketID and export features
print("Joining WaterMeter1 to new WaterMeter Location points")
out_layer = "WaterMeterRefined"
arcpy.management.MakeFeatureLayer(in_features=water_meter_refined, out_layer=out_layer, where_clause='',
workspace=processing_gdb, field_info='')
out_layer_wm = "watermeter1"
arcpy.management.MakeFeatureLayer(in_features=existing_waterMeter_points, out_layer=out_layer_wm,
where_clause='',
workspace=processing_gdb, field_info='')
arcpy.management.AddJoin(in_layer_or_view=out_layer, in_field='LocID', join_table=out_layer_wm,
join_field='LOCID', join_type='KEEP_ALL', index_join_fields='INDEX_JOIN_FIELDS')
# export ami water meter joined with water meter addresses
out_wmp = processing_gdb + "\\Watermeter_loc_join" + time_stamp
arcpy.conversion.ExportFeatures(in_features=out_layer, out_features=out_wmp, where_clause='',
use_field_alias_as_name='',
field_mapping='', sort_field='')
# before joining ami export to existing water meter locations calc fields in water meter loc
print("Setting Hist Read in current Water Meter Points")
arcpy.management.CalculateField(
in_table=waterMeter_points_location,
field="LastReading",
expression="!CurrentReading1!",
expression_type="PYTHON3",
code_block='',
field_type="double",
enforce_domains="NO_ENFORCE_DOMAINS"
)
# join the weekly ami export based on socketID
arcpy.DisconnectUser(sde_connection, "ALL")
print("Joining ami export meter points")
join_oldtoNew = "temp"
arcpy.management.MakeFeatureLayer(in_features=waterMeter_points_location, out_layer=join_oldtoNew,
where_clause='',
workspace=processing_gdb, field_info='')
join_newData = "newtemp"
arcpy.management.MakeFeatureLayer(in_features=out_wmp, out_layer=join_newData,
where_clause='',
workspace=processing_gdb, field_info='')
arcpy.management.AddJoin(in_layer_or_view=join_oldtoNew, in_field='SocketID', join_table=join_newData,
join_field='SocketID', join_type='KEEP_COMMON', index_join_fields='INDEX_JOIN_FIELDS')
# set new current reading and date based on the join
# disconnect users after ever calc to remove futura from automated connection
# could optionally turn connection properties off which script runs, then turn them back on upon completion
arcpy.DisconnectUser(sde_connection, "ALL")
print("Updating Following Fields")
print("...CurrentReading...")
arcpy.management.CalculateField(
in_table=join_oldtoNew,
field="Water_System_BWU.DBO.Water_Meter_Locations.CurrentReading1",
expression="!Watermeter_loc_join" + time_stamp+".CurrentReading1!",
expression_type="PYTHON3",
code_block='',
field_type="double",
enforce_domains="NO_ENFORCE_DOMAINS")
# current reading date
arcpy.DisconnectUser(sde_connection, "ALL")
print("...LastReadDateUTC...")
arcpy.management.CalculateField(
in_table=join_oldtoNew,
field="Water_System_BWU.DBO.Water_Meter_Locations.LastReadDateUTC",
expression="!Watermeter_loc_join" + time_stamp+".LastReadDateUTC!",
expression_type="PYTHON3",
code_block='',
field_type="double",
enforce_domains="NO_ENFORCE_DOMAINS")
# update meter METNUM
arcpy.DisconnectUser(sde_connection, "ALL")
print("...METNUM...")
arcpy.management.CalculateField(
in_table=join_oldtoNew,
field="Water_System_BWU.DBO.Water_Meter_Locations.METNUM",
expression="!Watermeter_loc_join" + time_stamp+".SerialNumber!",
expression_type="PYTHON3",
code_block='',
field_type="double",
enforce_domains="NO_ENFORCE_DOMAINS")
# update description
arcpy.DisconnectUser(sde_connection, "ALL")
print("...Description...")
arcpy.management.CalculateField(
in_table=join_oldtoNew,
field="Water_System_BWU.DBO.Water_Meter_Locations.Description",
expression="!Watermeter_loc_join" + time_stamp+".Description!",
expression_type="PYTHON3",
code_block='',
field_type="double",
enforce_domains="NO_ENFORCE_DOMAINS")
# update the ratecode
arcpy.DisconnectUser(sde_connection, "ALL")
print("...RateCode...")
arcpy.management.CalculateField(
in_table=join_oldtoNew,
field="Water_System_BWU.DBO.Water_Meter_Locations.RateCode",
expression="!Watermeter_loc_join" + time_stamp+".RateCode!",
expression_type="PYTHON3",
code_block='',
field_type="double",
enforce_domains="NO_ENFORCE_DOMAINS")
arcpy.DisconnectUser(sde_connection, "ALL")
print("...INSTALL_DAY...")
arcpy.management.CalculateField(
in_table=join_oldtoNew,
field="Water_System_BWU.DBO.Water_Meter_Locations.LOC_REMARK",
expression="!Watermeter_loc_join" + time_stamp+".InstallDateUTC!",
expression_type="PYTHON3",
code_block='',
field_type="TEXT",
enforce_domains="NO_ENFORCE_DOMAINS")
arcpy.DisconnectUser(sde_connection, "ALL")
print("...Size...")
arcpy.management.CalculateField(
in_table=join_oldtoNew,
field="Water_System_BWU.DBO.Water_Meter_Locations.SIZE",
expression="!Watermeter_loc_join" + time_stamp+".Size1!",
expression_type="PYTHON3",
code_block='',
field_type="TEXT",
enforce_domains="NO_ENFORCE_DOMAINS")
# remove join
print("...Remove join...")
arcpy.DisconnectUser(sde_connection, "ALL")
arcpy.management.RemoveJoin(
in_layer_or_view=join_oldtoNew,
join_name="Watermeter_loc_join" + time_stamp)
# calc the difference in use between reads in the hist change field
arcpy.DisconnectUser(sde_connection, "ALL")
print("...Historic Change...")
arcpy.management.CalculateField(
in_table=waterMeter_points_location,
field="HistoricChange",
expression="!CurrentReading1! - !LastReading!",
expression_type="PYTHON3",
code_block="",
field_type="TEXT",
enforce_domains="NO_ENFORCE_DOMAINS")
# find differences in socketID to find new meters
# Specify the field to compare
feature_class2 = water_meter_refined
feature_class1 = waterMeter_points_location
field_to_compare_1 = 'SocketID' # Field name in feature_class1 (old data)
field_to_compare_2 = 'SocketID' # Field name in feature_class2 (new data)
time_stamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
# Create a list to store the differences
differences = []
unique_values = set()
# Create a search cursor for the first feature class
print("finding new AMI exports")
# Create a search cursor for feature_class1 to populate unique_values set
with arcpy.da.SearchCursor(feature_class1, [field_to_compare_1]) as cursor1:
for row1 in cursor1:
value = row1[0]
unique_values.add(value)
# Create a search cursor for feature_class2 to compare with unique_values
with arcpy.da.SearchCursor(feature_class2, ['OID@', field_to_compare_2, 'SHAPE@', '*']) as cursor2:
for row2 in cursor2:
value2 = row2[1]
if value2 not in unique_values:
differences.append(row2)
# Print the differences
print(f"{differences}")
getcount = len(differences)
print(f"Differences found: {getcount}")
# Export to new fc
# Create a query string for the selection
if differences:
print("Exporting...")
oid_list = [str(difference[0]) for difference in differences]
oid_list_string = ', '.join(oid_list)
query_string = f"OBJECTID IN ({oid_list_string})"
arcpy.analysis.Select(in_features=feature_class2, out_feature_class=new_waterMeters,
where_clause=query_string)
# Find the field map for each field and update its output field name
try:
print("Appending new meters to sde layer")
# Use the arcpy Append tool to append the data from the input table to the target table
arcpy.management.Append(inputs=new_waterMeters, target=waterMeter_points_location, schema_type="NO_TEST",
field_mapping='', subtype='', expression='',
match_fields='', update_geometry='')
except arcpy.ExecuteError or AttributeError:
print(arcpy.GetMessages())
now2 = datetime.datetime.now()
print(f"End time: {now2}")
# calc field for use type
print("Calculating use type[COMMERCIAL,IRRIGATION (COMMERCIAL),IRRIGATION (RESIDENTIAL),RESIDENTIAL")
code_block = """
def calculate_rate_category(rate_code):
myList1 = ["BT/I/C/WA", "BT/I/E/SW", "BT/I/E/WA", "BT/I/F/WA", "BT/I/G/SW", "BT/I/G/WA", "BT/I/H/WA", "BT/I/I/WA", "BT/I/LP/WA", "BT/I/M/WA", "BT/O/C/WA", "BT/O/J/WA", "BT/O/OB/WA", "BT/O/RL/WA", "CT/I/C/WA"]
myList2 = ["BT/I/C/IR", "BT/I/I/IR", "BT/I/LP/IR", "BT/I/M/IR", "BT/I/W/IR", "BT/O/C/IR", "CT/I/C/IR"]
myList3 = ["BT/I/R/IR", "BT/O/R/IR"]
myList4 = ["BT/I/R/WA", "BT/O/R/WA"]
if rate_code in myList1:
return "COMMERCIAL"
elif rate_code in myList2:
return "IRRIGATION (COMMERCIAL)"
elif rate_code in myList3:
return "IRRIGATION (RESIDENTIAL)"
elif rate_code in myList4:
return "RESIDENTIAL"
else:
return None
"""
arcpy.management.CalculateField(in_table=waterMeter_points_location, field='Grouping', expression="calculate_rate_category(!RateCode!)",
expression_type="PYTHON3",code_block=code_block, field_type='TEXT',enforce_domains="")
else:
print("No new meter values to add")
# export the new table
arcpy.conversion.ExportTable(in_table=waterMeter_points_location, out_table=out_table, where_clause='', use_field_alias_as_name="USE_ALIAS", field_mapping='',
sort_field='')
#
print("Clean up workspace")
arcpy.Delete_management([out_layer, water_meter_refined, water_meter_points])
# reconcile and post version
except arcpy.ExecuteError:
print("Failure")
arcpy.AddMessage(arcpy.GetMessages())
now2 = datetime.datetime.now()
print(f"End time: {now2}")
# end script
print("Complete")
now2 = datetime.datetime.now()
print(f"End time: {now2}")
if __name__ == '__main__':
wm_update()