With ArcGIS GeoEvent it is very easy to setup a notification when a new feature is added to an ArcGIS Online Hosted Feature Service. Instructions can be found here. If you do not have GeoEvent you can still accomplish this using Python. Below is an example on how to do this.
When editor tracking is enabled, new features are recorded in a field called created_date. The script queries this field within the service and compares the time to the current time (minus one hour). If the time is greater than the current time, it will send an e-mail with specified field values for the new feature(s).
Windows Task Scheduler can be setup to execute this script at a given interval (i.e. every hour). This will report any new features within that last hour.
The below example is written for the Citizen Problem Report application. It utilizes 2 fields (reqtype & status) when querying the service. These variables can be commented out, or updated to reference different fields, if working with a different service:
The SUBJECT and TEXT variables in the sendEmail function can be updated to whatever you would like to include in the e-mail:
from arcgis.gis import GIS
from arcgis.features import FeatureLayer
import datetime, time, smtplib
from datetime import timedelta
from email.mime.text import MIMEText
# Variables
username = 'jskinner_rats' # AGOL Username
password = '********' # AGOL Password
fsURL = 'https://services.arcgis.com/dfRzIozUOVYoi65F/arcgis/rest/services/Requests/FeatureServer/0' # Feature Service URL
dateField = 'created_date' # Date field to query
hours = 1 # Number of hours to check when a feature was added
fromEmail = 'no-reply@esri.com' # From e-mail address
toEmail = ['user1@esri.com', 'user2@esri.com'] # To e-mail address(es)
smtpServer = 'smtp.esri.com' # SMTP Server Name
portNumber = 25 # SMTP Server port
# Function to send email
def sendEmail():
SUBJECT = 'Problem Reported'
TEXT = f"{reqtype} was created with status {status}"
smtpObj = smtplib.SMTP(host=smtpServer, port=portNumber)
msg = MIMEText(TEXT)
msg['Subject'] = SUBJECT
msg['From'] = fromEmail
msg['To'] = ", ".join(toEmail)
smtpObj.sendmail(fromEmail, toEmail, msg.as_string())
print("Successfully sent email")
smtpObj.quit()
# Generate AGOL token
print('Connecting to AGOL')
gis = GIS('https://www.arcgis.com', username, password)
# Reference feature service from url
fLyr = FeatureLayer(fsURL)
# Query service and check if date field is within the last hour
print("Querying feature service")
queryResults = fLyr.query(where='1=1', out_fields="*", return_geometry=False)
for results in queryResults.features:
if results.attributes[dateField] != None:
createDate = results.attributes[dateField] / 1000
reqtype = results.attributes['reqtype']
status = results.attributes['status']
t = datetime.datetime.now() - timedelta(hours=hours)
t = time.mktime(t.timetuple())
if createDate > t:
sendEmail()
Probably not the best way to do this but here it is:
import requests, json, datetime, time, smtplib
from datetime import timedelta
import datetime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# Disable warnings
requests.packages.urllib3.disable_warnings()
print('This file is from sendEmail_addfields.py')
# Variables
username = '' # AGOL Username or Portal username
password = '' # AGOL Password or Portal password
URL = '' # Feature Service URL
uniqueID = 'objectid' # i.e. OBJECTID
dateField = 'created_date' # Date field to query
hoursValue = 0.083 # Number of hours to check when a feature was added
#0.083
fromEmail = '' # Email sender
TO = [''] # Email receiver
smtpServer = '' # SMTP Server Name
portNumber = 25 # SMTP Server port
AGOL = True # True if service is hosted in AGOL, False if service is hosted in Portal
portalServer = 'portal.esri.com' # Server name portal is installed on (not web adaptor)
# Create empty list for uniqueIDs
datesighted = []
name = []
phone = []
email = []
aggressive = []
numanimal = []
animaldoing = []
reaction = []
probtype = []
oidList = []
publicview = []
details = []
# Generate AGOL token
if AGOL == True:
try:
print('Generating Token')
tokenURL = 'https://www.arcgis.com/sharing/rest/generateToken'
params = {'f': 'pjson', 'username': username, 'password': password, 'referer': 'http://www.arcgis.com'}
r = requests.post(tokenURL, data=params, verify=False)
response = json.loads(r.content)
token = data['token']
except:
token = ''
else:
# Generate Portal token
tokenURL = '/'.format(portalServer)
params = {'f': 'pjson', 'username': username, 'password': password, 'referer': ''.format(portalServer)}
r = requests.post(tokenURL, data = params, verify=False)
response = json.loads(r.content)
token = response['token']
# Query service and check if created_date time is within the last hour
params = {'f': 'pjson', 'where': "1=1", 'outFields' : '*', 'statisticType' : 'count', 'returnGeometry' : 'false'}
r = requests.post(URL, data=params, verify=False)
response = json.loads(r.content)
print(params)
for feat in response['features']:
createDate = feat['attributes']['created_date']
createDate = int(str(createDate)[0:-3])
t = datetime.datetime.now() - timedelta(hours=hoursValue)
t = time.mktime(t.timetuple())
if createDate > t:
datesighted.append(str(feat['attributes']['datesighted']))
name.append(str(feat['attributes']['name']))
phone.append(str(feat['attributes']['phone']))
email.append(str(feat['attributes']['email']))
animaldoing.append(str(feat['attributes']['animaldoing']))
numanimal.append(str(feat['attributes']['numanimal']))
aggressive.append(str(feat['attributes']['aggressive']))
reaction.append(str(feat['attributes']['reaction']))
probtype.append(str(feat['attributes']['probtype']))
oidList.append(str(feat['attributes']['objectid']))
details.append(str(feat['attributes']['details']))
publicview .append(str(feat['attributes']['publicview']))
print(oidList)
mergelist = zip(probtype, details, publicview)
print(mergelist)
# Email Info
FROM = fromEmail
SUBJECT = 'New Wildlife Sighting Has been Reported'
Total= "Features with " + " " + str(publicview) + " " + " were added."
TotalAnimals = (Total.count("Yes"))
print(TotalAnimals)
#TEXT = "There were " + " " + str(TotalAnimals) + " " + " sighting(s) added the Cougar Map within the past hour."
#TEXT = '<a href="http://maps.google.com/maps?z=12&t=m&q=loc:'+str(p['latitude'])+'+'+str(p['longitude'])+'">Click Here</a>'
tom = datesighted[0]
chosen_number = int(tom)
s = chosen_number / 1000.0
print s
dFormat = "%Y-%m-%d %H:%M:%S.%f"
datesightedconvert = datetime.datetime.fromtimestamp(s).strftime('%Y-%m-%d %H:%M:%S')
# Create the body of the message (a plain-text and an HTML version).
TotalSighted = "There were " + " " + str(TotalAnimals) + " " + " reports(s) added the Wildlife Map within the past 5 min."
Sighted = "\nAnimal Sighted : " + str(probtype)
TheDateSighted = "\nDate Sighted : " + str(datesightedconvert) + " " + " ***\n"
reported = "\nReporter Name: " + str(name)
PhoneNumber = "\nReporter Phone Number: " + str(phone)
ReportedEmail = "\nReporter Phone Email: " + str(email)
Aggressiveness = "'\n'Was animal Aggressive?: " + str(aggressive)
ReportedReaction = "'\nWhat was the animal Reaction?: " + str(reaction)
ReportedDetails = "'\n'More Details about the sighting: " + str(details)
AnimalDoing = "'\n'What was the Animal(s) Doing?: " + str(animaldoing)
AnimalSeen = "'\n'How Many were seen?: " + str(numanimal)
Link = "'\n\n'View more information on the : \ \n\n\n ***if there are more than 1 reports then only the first Date Sighted will be in this Email.***"
Text = TotalSighted + TheDateSighted + Sighted + reported + PhoneNumber + ReportedEmail + Aggressiveness + ReportedReaction + ReportedDetails + AnimalDoing + AnimalSeen + Link
print ''.join(Text)
message = """\
From: %s
To: %s
Subject: %s
%s
""" % (FROM, ", ".join(TO), SUBJECT, Text)
# If new features exist, send email
if len(oidList) > 0:
smtpObj = smtplib.SMTP(host=smtpServer, port=portNumber)
smtpObj.sendmail(FROM, TO, message)
print("Successfully sent email")
smtpObj.quit()
Here is some of the output. Is there a way to remove the []
'Was animal Aggressive?: ['No']'
What was the animal Reaction?: ['Didnt see me']'
'More Details about the sighting: ['Mama and two cubs filmed by the motion-detecting doorbell camera as they passed across the yard']'
For your e-mail text, try cleaning up the extra apostrophes. For example, you have:
ReportedReaction = "'\nWhat was the animal Reaction?: " + str(reaction)
Try this instead:
ReportedReaction = "\nWhat was the animal Reaction?: " + str(reaction)
You won't need any quotes around \n, as long as it's enclosed by quotes, python will read it is a new line.
To remove the [] around the answers, you have two options:
1. Since you are appending to a list, you can specify the first value of the list using a [0]. Ex:
Sighted = "\nAnimal Sighted : " + str(probtype)[0]
2. Instead of creating a list (i.e. probtype = []), you can remove this and set the probtype as a variable:
probtype = (str(feat['attributes']['probtype']))
I would recommend the second option. In doing so, you should move everything within the for loop. This will allow separate e-mails to be sent in the event there is more than 1 feature added within the allotted time the script executes:
for feat in response['features']:
createDate = feat['attributes']['created_date']
createDate = int(str(createDate)[0:-3])
t = datetime.datetime.now() - timedelta(hours=hoursValue)
t = time.mktime(t.timetuple())
if createDate > t:
datesighted.append(str(feat['attributes']['datesighted']))
name.append(str(feat['attributes']['name']))
phone.append(str(feat['attributes']['phone']))
email.append(str(feat['attributes']['email']))
animaldoing.append(str(feat['attributes']['animaldoing']))
numanimal.append(str(feat['attributes']['numanimal']))
aggressive.append(str(feat['attributes']['aggressive']))
reaction.append(str(feat['attributes']['reaction']))
probtype.append(str(feat['attributes']['probtype']))
oidList.append(str(feat['attributes']['objectid']))
details.append(str(feat['attributes']['details']))
publicview .append(str(feat['attributes']['publicview']))
print(oidList)
mergelist = zip(probtype, details, publicview)
print(mergelist)
# Email Info
FROM = fromEmail
SUBJECT = 'New Wildlife Sighting Has been Reported'
Total= "Features with " + " " + str(publicview) + " " + " were added."
TotalAnimals = (Total.count("Yes"))
print(TotalAnimals)
#TEXT = "There were " + " " + str(TotalAnimals) + " " + " sighting(s) added the Cougar Map within the past hour."
#TEXT = '<a href="http://maps.google.com/maps?z=12&t=m&q=loc:'+str(p['latitude'])+'+'+str(p['longitude'])+'">Click Here</a>'
tom = datesighted[0]
chosen_number = int(tom)
s = chosen_number / 1000.0
print s
dFormat = "%Y-%m-%d %H:%M:%S.%f"
datesightedconvert = datetime.datetime.fromtimestamp(s).strftime('%Y-%m-%d %H:%M:%S')
# Create the body of the message (a plain-text and an HTML version).
TotalSighted = "There were " + " " + str(TotalAnimals) + " " + " reports(s) added the Wildlife Map within the past 5 min."
Sighted = "\nAnimal Sighted : " + str(probtype)
TheDateSighted = "\nDate Sighted : " + str(datesightedconvert) + " " + " ***\n"
reported = "\nReporter Name: " + str(name)
PhoneNumber = "\nReporter Phone Number: " + str(phone)
ReportedEmail = "\nReporter Phone Email: " + str(email)
Aggressiveness = "'\n'Was animal Aggressive?: " + str(aggressive)
ReportedReaction = "'\nWhat was the animal Reaction?: " + str(reaction)
ReportedDetails = "'\n'More Details about the sighting: " + str(details)
AnimalDoing = "'\n'What was the Animal(s) Doing?: " + str(animaldoing)
AnimalSeen = "'\n'How Many were seen?: " + str(numanimal)
Link = "'\n\n'View more information on the : \ \n\n\n ***if there are more than 1 reports then only the first Date Sighted will be in this Email.***"
Text = TotalSighted + TheDateSighted + Sighted + reported + PhoneNumber + ReportedEmail + Aggressiveness + ReportedReaction + ReportedDetails + AnimalDoing + AnimalSeen + Link
print ''.join(Text)
message = """\
From: %s
To: %s
Subject: %s
%s
""" % (FROM, ", ".join(TO), SUBJECT, Text)
# If new features exist, send email
if len(oidList) > 0:
smtpObj = smtplib.SMTP(host=smtpServer, port=portNumber)
smtpObj.sendmail(FROM, TO, message)
print("Successfully sent email")
smtpObj.quit()
Your suggestions have made my email script way better thank you!
I am getting this error when someone inputs a ' in a word for their response
Traceback (most recent call last):
File "C:Loc", line 81, in <module>
reaction.append(str(feat['attributes']['reaction']))
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2019' in position 5: ordinal not in range(128)
In this case the don't is giving me that error.
@RickeyFighttry the following:
reaction.append(str(feat['attributes']['reaction']).replace(u"\u2018", "'").replace(u"\u2019", "'"))
Yes that works. But you recommended that I switch to:
reaction = (str(feat['attributes']['reaction'])
How would I add to the modified code above?
.replace(u"\u2018", "'").replace(u"\u2019", "'"))
reaction = str(feat['attributes']['reaction']).replace(u"\u2018", "'").replace(u"\u2019", "'")
I must have done something wrong when I added it the first time because it works now.
Thank you for your help!
Hi @JakeSkinner
I have the following situation.
I have a feature class with related table via globalId. Both the feature class and table are editable and stored in the AGS.
In the e-mail I would like to have the information both from the related table and from the feature class. SO the script will be looking in two URL's, and the connection with the globalID = relglobalID.
Additionaly, I would like to send the e-mail, when a related table will be modified, to a person who originally created a FC.
Can you please advice how I can change the script?
Thank for help!
Hi @JakeSkinner
I realize that this is an older post so I may not get a response. My organization is trying to set up the Esri Citizen Problem Reporter. There have been some issues with the send emails script included with the solution and our webmail server so I have been trying to find a different way to trigger an email when a new feature is added. I have tried your provided code but just cannot seem to get it to work. I just get an error of:
Something went wrong...
Generating Token
[]
I have tried running just the part of the code that sends an email and it works without any issues. I'm not sure what is going wrong.
@Michael_Wozniakcan you share the service to an AGOL Group and invite my user account (jskinner_CountySandbox)? It looks like there are no new features added within the timeframe you're querying. The default in the code is 1 hour.
@Michael_Wozniak I was able to get the script to work with the service you shared. Try the one below. It sends multiple e-mails instead of one e-mail with a list of OBJECTIDs for the features that were created.
You will just need to update the username and password.
import requests, json, datetime, time, smtplib, math
from datetime import timedelta
from email.mime.text import MIMEText
# Disable warnings
requests.packages.urllib3.disable_warnings()
# Variables
username = 'jskinner_CountySandbox' # AGOL Username
password = '*******' # AGOL Password
URL = 'https://services9.arcgis.com/8FOQ9nDvQJjqML1o/arcgis/rest/services/CitizenProblems_4ccc650e579c469697018f64006e9acc/FeatureServer/0/query' # Feature Service URL
dateField = 'CreationDate' # Date field to query
hoursValue = 1 # Number of hours to check when a feature was added
fromEmail = 'no-reply@esri.com' # Email sender
toEmail = ['jskinner@esri.com'] # Email receiver(s)
smtpServer = 'smtp.esri.com' # SMTP Server Name
portNumber = 25 # SMTP Server port
# Function to send email
def sendEmail():
SUBJECT = 'Problem Reported'
TEXT = "{0} was created with status {1} and has been assigned to {2}".format(typeProb, status, assignedto)
smtpObj = smtplib.SMTP(host=smtpServer, port=portNumber)
msg = MIMEText(TEXT)
msg['Subject'] = SUBJECT
msg['From'] = fromEmail
msg['To'] = ", ".join(toEmail)
smtpObj.sendmail(fromEmail, toEmail, msg.as_string())
print("Successfully sent email")
smtpObj.quit()
# Generate AGOL token
try:
print('Generating Token')
tokenURL = 'https://www.arcgis.com/sharing/rest/generateToken'
params = {'f': 'pjson', 'username': username, 'password': password, 'referer': 'http://www.arcgis.com'}
r = requests.post(tokenURL, data=params, verify=False)
response = json.loads(r.content)
token = response['token']
except:
token = ''
# Return largest ObjectID
whereClause = '1=1'
params = {'where': whereClause, 'returnIdsOnly': 'true', 'token': token, 'f': 'json'}
r = requests.post(URL, data = params, verify = False)
response = json.loads(r.content)
try:
response['objectIds'].sort()
except Exception as e:
print("Error: {0}".format(e))
count = len(response['objectIds'])
# Query service and check if editDate time is within the last hour
if count < 1000:
params = {'f': 'pjson', 'where': "1=1", 'outFields' : '*', 'returnGeometry' : 'false', 'token' : token}
r = requests.post(URL, data=params, verify=False)
response = json.loads(r.content)
for feat in response['features']:
typeProb = feat['attributes']['probtype']
status = feat['attributes']['status']
assignedto = feat['attributes']['assignedto']
createDate = feat['attributes'][dateField]
createDate = int(str(createDate)[0:-3])
t = datetime.datetime.now() - timedelta(hours=hoursValue)
t = time.mktime(t.timetuple())
if createDate > t:
sendEmail()
Thank you so much. This works perfectly.
I'm wondering if it is possible to send an email based on which Problem Category is selected. For instance an Animal Control problem would only send an email to our Animal Control officer or a road problem would only send an email to the Public Works supervisor.
Thanks again for your help.
@Michael_Wozniak you can accomplish this with the following steps:
1. Remove the toEmail under Variables
2. Add a dictionary of the Problem Categories followed by the e-mail(s) that should be notified. Ex:
# E-mail Dictionary
emailDict = {'Animal Control': ['jskinner@esri.com'], 'Public Works': ['jdoe@esri.com', 'awalker@esri.com']}
3. Add a new toEmail variable (line 10 below) referencing the dictionary:
# Query service and check if editDate time is within the last hour
if count < 1000:
params = {'f': 'pjson', 'where': "1=1", 'outFields' : '*', 'returnGeometry' : 'false', 'token' : token}
r = requests.post(URL, data=params, verify=False)
response = json.loads(r.content)
for feat in response['features']:
typeProb = feat['attributes']['probtype']
status = feat['attributes']['status']
assignedto = feat['attributes']['assignedto']
toEmail = emailDict[assignedto]
createDate = feat['attributes'][dateField]
createDate = int(str(createDate)[0:-3])
t = datetime.datetime.now() - timedelta(hours=hoursValue)
t = time.mktime(t.timetuple())
if createDate > t:
sendEmail()
Thank you once again. Everything works perfectly.
Thank you for taking the time to create this extremely helpful thread.
It seems as though the majority of the responses in this thread are about sending emails when new features are created, however I am hoping to create a script that can be initiated by Windows Task Scheduler that will send an email to notify a coworker every time 1 specific field is updated from one status to another. Additionally, I would need the script to only send an email when it notices a change to the field update, and it needs to be unaffected by fields with null values.
I am a novice when it comes to scripting so any guidance that you can provide would be tremendously appreciated. Thank you in advance.
@JoshConrad1 instead of using the CreationDate field, you can use the EditDate when Editor Tracking is enabled. Not quite sure if this will work since the EditDate field will update when any field is edited.
Thank you for clarifying. I am trying to automate our workorder workflows by having an email sent to managers when a "status" field is updated and was exploring work arounds to webhooks.
I thank you once again for your time and response.
This feed was super helpful.
However, I simply want to notify two people via email when there is a new Citizen Problem reported as a reminder for them to review it. I am brand new to Python scripting and any assistance you can provide would be greatly apprecieated. Essentially, I just need to know what lines I can remove from the above scripts but still have it work and send an email as notification to those managing our Citizen Propblem Reporter.
Also, thank you to everyone that has contributed to this thread. The GIS community is the best at supporting one another!
-Cody
@cmerwin you will just have to update the # Variables section in the script with your information. Scroll to the right to see a description on each variable.
# Variables username = 'jskinner_CountySandbox' # AGOL Username password = '*******' # AGOL Password URL = 'https://services.arcgis.com/dlFJXQQtlWFB4qUk/arcgis/rest/services/CitizenProblems/FeatureServer/0' # Feature Service URL dateField = 'CreationDate' # Date field to query hoursValue = 1 # Number of hours to check when a feature was added fromEmail = 'no-reply@esri.com' # Email sender toEmail = ['user1@esri.com', 'user2@esri.com'] # Email receiver(s) smtpServer = 'smtp.esri.com' # SMTP Server Name portNumber = 25 # SMTP Server port
Hi @JakeSkinner ,
I have added a field called address_mail into my original Feature Class. How can I modify the script so that email is sent to the address contained in this in the Feature Class.
Any advice would be much appreciated.
Regards,
Monika
See below, I removed the toEmail variable under the # Variables section. I also added line 92:
toEmail = feat['attributes']['email']
Replace 'email' with the name of the field containing the email addresses.
import requests, json, datetime, time, smtplib
from datetime import timedelta
from email.mime.text import MIMEText
# Disable warnings
requests.packages.urllib3.disable_warnings()
# Variables
username = 'jskinner_CountySandbox' # AGOL Username
password = '*******' # AGOL Password
URL = 'https://services.arcgis.com/dlFJXQQtlWFB4qUk/arcgis/rest/services/CitizenProblems/FeatureServer/0' # Feature Service URL
dateField = 'CreationDate' # Date field to query
hoursValue = 1 # Number of hours to check when a feature was added
fromEmail = 'no-reply@esri.com' # Email sender
receiver(s)
smtpServer = 'smtp.esri.com' # SMTP Server Name
portNumber = 25 # SMTP Server port
# Function to send email
def sendEmail():
SUBJECT = 'Problem Reported'
TEXT = "{0} was created with status {1}".format(typeProb, status)
smtpObj = smtplib.SMTP(host=smtpServer, port=portNumber)
msg = MIMEText(TEXT)
msg['Subject'] = SUBJECT
msg['From'] = fromEmail
msg['To'] = ", ".join(toEmail)
smtpObj.sendmail(fromEmail, toEmail, msg.as_string())
print("Successfully sent email")
smtpObj.quit()
# Generate AGOL token
try:
print('Generating Token')
tokenURL = 'https://www.arcgis.com/sharing/rest/generateToken'
params = {'f': 'pjson', 'username': username, 'password': password, 'referer': 'http://www.arcgis.com'}
r = requests.post(tokenURL, data=params, verify=False)
response = json.loads(r.content)
token = response['token']
except:
token = ''
# Return largest ObjectID
whereClause = '1=1'
params = {'where': whereClause, 'returnIdsOnly': 'true', 'token': token, 'f': 'json'}
r = requests.post(URL + '/query', data = params, verify = False)
response = json.loads(r.content)
try:
response['objectIds'].sort()
except Exception as e:
print("Error: {0}".format(e))
count = len(response['objectIds'])
# Query service and check if CreationDate time is within the last hour
if count < 1000:
params = {'f': 'pjson', 'where': "1=1", 'outFields' : '*', 'returnGeometry' : 'false', 'token' : token}
r = requests.post(URL + '/query', data=params, verify=False)
response = json.loads(r.content)
for feat in response['features']:
typeProb = feat['attributes']['probtype']
status = feat['attributes']['status']
createDate = feat['attributes'][dateField]
createDate = int(str(createDate)[0:-3])
t = datetime.datetime.now() - timedelta(hours=hoursValue)
t = time.mktime(t.timetuple())
if createDate > t:
sendEmail()
else:
y = minOID
x = minOID + 1000
ids = response['objectIds']
newIteration = (math.ceil(iteration/1000.0) * 1000)
while y < newIteration:
if x > int(newIteration):
x = newIteration
where = OID + '>' + str(y) + ' AND ' + OID + '<=' + str(x)
print('Querying features with ObjectIDs from ' + str(y) + ' to ' + str(x))
params = {'f': 'pjson', 'where': where, 'outFields' : '*', 'returnGeometry' : 'false', 'token' : token}
r = requests.post(URL + '/query', data=params, verify=False)
response = json.loads(r.content)
for feat in response['features']:
typeProb = feat['attributes']['probtype']
status = feat['attributes']['status']
toEmail = feat['attributes']['email']
createDate = feat['attributes'][dateField]
createDate = int(str(createDate)[0:-3])
t = datetime.datetime.now() - timedelta(hours=hoursValue)
t = time.mktime(t.timetuple())
if createDate > t:
sendEmail()
x += 1000
y += 1000
Hi @JakeSkinner
Thanks for your response!
Just one more: how can I modify the script to to send an e-mail to define person (like in a original script) and additionally sent an email to a person added in a new field called email?
Regards,
Monika
Thanks to @JakeSkinner I've been using this script successfully for several years now (thanks so much Jake!).
However, for the past month I've been getting the TimeoutError: [WinError 10060] A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
Has anyone ran into this issue recently?
Hi @Karlie_Kendall,
Can you configure the script to another feature service as a test and see if you get the same error?
@JakeSkinner I just tried testing it from another feature service, as well as a feature service from a different AGO account, and tried running the script on a different computer, but unfortunately got the same error each time.
I am using enterprise portal and my script does not seem to work now.
We have decided to lock down the service to only internal employees and now the script fails.
I am using a variation of the script @JakeSkinner provided. I thought it worked but now that ii is locked down it is not. Any suggestions on why its not working?
@RickeyFight can you describe what your doing when you say you locked down the service? Are you only sharing the service to your Organization?
@JakeSkinner Yes it is shared with organization only now.
Change the below lines:
tokenURL = 'https://www.arcgis.com/sharing/rest/generateToken'
params = {'f': 'pjson', 'username': username, 'password': password, 'referer': 'http://www.arcgis.com'}
to the following:
tokenURL = 'https://<portalURL>/<webadaptor>/sharing/rest/generateToken/'
params = {'f': 'pjson', 'username': username, 'password': password, 'referer': '<portalURL>'}
Replace the <portalURL>/<webadaptor> with the URL to your portal (i.e. https://gis.esri.com/portal). And, the referer to the portal URL (i.e. https://gis.esri.com).
I now get this error
Traceback (most recent call last):
File "C:\Users\user\Desktop\TestEmailSend.py", line 68, in <module>
for feat in response['features']:
KeyError: 'features'
@JakeSkinner
This file is from testemailsend.py
Generating Token
{'returnGeometry': 'false', 'outFields': '*', 'statisticType': 'count', 'where': '1=1', 'f': 'pjson'}
{u'error': {u'message': u'Token Required', u'code': 499, u'details': []}}
Traceback (most recent call last):
File "C:\Users\user\Desktop\TestEmailSend.py", line 68, in <module>
for feat in response['features']:
KeyError: 'features'
@RickeyFight check to make sure your token is being generated, (i.e. Add a statement: print(token) ).
@JakeSkinner
I added print (token) nothing happened.
@RickeyFight can you post your script?
@JakeSkinner
I removed Email and username and passwords etc
I changed the AGOL section using the code you provided above
import requests, json, datetime, time, smtplib
from datetime import timedelta
import datetime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# Disable warnings
requests.packages.urllib3.disable_warnings()
print('This file is from testemailsend.py')
# Variables
username = 'username' # AGOL Username or Portal username
password = 'password' # AGOL Password or Portal password
URL = 'https://gis.ashland.or.us/arcgis/rest/services/Hosted/wildlife_reporter/FeatureServer/0/query' # Feature Service URL
uniqueID = 'fid' # i.e. OBJECTID
dateField = 'created_date' # Date field to query
hoursValue = 24 # Number of hours to check when a feature was added 0.08285 is 5 min
fromEmail = 'gis@ashland.or.us' # Email sender
TO = ['MyEmail'] # Email receiver
smtpServer = '**' # SMTP Server Name
portNumber = ** # SMTP Server port
AGOL = True # True if service is hosted in AGOL, False if service is hosted in Portal
portalServer = 'ServerName' # Server name portal is installed on (not web adaptor)
# Create empty list for uniqueIDs
datesighted = []
name = []
phone = []
email = []
aggressive = []
numanimal = []
animaldoing = []
reaction = []
probtype = []
oidList = []
publicview = []
details = []
# Generate AGOL token
if AGOL == True:
try:
print('Generating Token')
tokenURL = 'https://gis.ashland.or.us/arcgis/sharing/rest/generateToken/'
params = {'f': 'pjson', 'username': username, 'password': password, 'referer': 'https://gis.ashland.or.us/portal'}
r = requests.post(tokenURL, data=params, verify=False)
response = json.loads(r.content)
token = data['token']
print(token)
except:
token = ''
else:
# Generate Portal token
tokenURL = 'https://gis.ashland.or.us/portal/arcgis/sharing/rest/generateToken/'.format(portalServer)
params = {'f': 'pjson', 'username': username, 'password': password, 'referer': 'https://gis.ashland.or.us/portal'.format(portalServer)}
r = requests.post(tokenURL, data = params, verify=False)
response = json.loads(r.content)
token = response['token']
print(token)
# Query service and check if created_date time is within the last hour
params = {'f': 'pjson', 'where': "1=1", 'outFields' : '*', 'statisticType' : 'count', 'returnGeometry' : 'false'}
r = requests.post(URL, data=params, verify=False)
response = json.loads(r.content)
print(params)
print(response)
for feat in response['features']:
createDate = feat['attributes']['created_date']
createDate = int(str(createDate)[0:-3])
t = datetime.datetime.now() - timedelta(hours=hoursValue)
t = time.mktime(t.timetuple())
if createDate > t:
datesighted.append(str(feat['attributes']['datesighte']))
name = (str(feat['attributes']['name']))
phone = (str(feat['attributes']['phone']))
email = (str(feat['attributes']['email']))
animaldoing = (str(feat['attributes']['animaldoin'])).replace(u"\u2018", "'").replace(u"\u2019", "'")
numanimal = (str(feat['attributes']['numanimal']))
if isinstance(aggressive, list):
aggressive = ', '.join(aggressive)
aggressive = aggressive.encode('utf-8')
reaction = str(feat['attributes']['reaction']).replace(u"\u2018", "'").replace(u"\u2019", "'")
probtype = (str(feat['attributes']['probtype']))
oidList = (str(feat['attributes']['fid']))
details = (str(feat['attributes']['details'])).replace(u"\u2018", "'").replace(u"\u2019", "'")
publicview = (str(feat['attributes']['publicview']))
print(oidList)
mergelist = zip(probtype, details, publicview)
print(mergelist)
# Email Info
FROM = fromEmail
SUBJECT = 'New Wildlife Sighting Has been Reported'
Total= "Features with " + " " + str(publicview) + " " + " were added."
TotalAnimals = (Total.count("Yes"))
print(TotalAnimals)
#TEXT = "There were " + " " + str(TotalAnimals) + " " + " sighting(s) added the Cougar Map within the past hour."
#TEXT = '<a href="http://maps.google.com/maps?z=12&t=m&q=loc:'+str(p['latitude'])+'+'+str(p['longitude'])+'">Click Here</a>'
tom = datesighted[0]
chosen_number = int(tom)
s = chosen_number / 1000.0
print s
dFormat = "%Y-%m-%d %H:%M:%S.%f"
datesightedconvert = datetime.datetime.fromtimestamp(s).strftime('%Y-%m-%d %H:%M:%S')
# Create the body of the message (a plain-text and an HTML version).
#TotalSighted = "There were " + " " + str(TotalAnimals) + " " + " reports(s) added the Wildlife Map within the past 5 min."
TotalSighted = "A Report has been added the Wildlife Map within the past 5 min."
Sighted = "\nAnimal Sighted : " + str(probtype)
TheDateSighted = "\nDate Sighted : " + str(datesightedconvert) + " " + " \n"
reported = "\nReporter Name: " + str(name)
PhoneNumber = "\nReporter Phone Number: " + str(phone)
ReportedEmail = "\nReporter Phone Email: " + str(email)
Aggressiveness = "\nWas animal Aggressive?: " + str(aggressive)
ReportedReaction = "\nWhat was the animal Reaction?: " + str(reaction)
ReportedDetails = "\nMore Details about the sighting: " + str(details)
AnimalDoing = "\nWhat was the Animal(s) Doing?: " + str(animaldoing)
AnimalSeen = "\nHow Many were seen?: " + str(numanimal)
Link = "\n\nView more information on the Cougar Webmap: \nhttps://gis.ashland.or.us/cougar \n\n\n "
Text = TotalSighted + TheDateSighted + Sighted + reported + PhoneNumber + ReportedEmail + Aggressiveness + ReportedReaction + ReportedDetails + AnimalDoing + AnimalSeen + Link
print ''.join(Text)
message = """\
From: %s
To: %s
Subject: %s
%s
""" % (FROM, ", ".join(TO), SUBJECT, Text)
# If new features exist, send email
if len(oidList) > 0:
smtpObj = smtplib.SMTP(host=smtpServer, port=portNumber)
smtpObj.sendmail(FROM, TO, message)
print("Successfully sent email")
smtpObj.quit()
@RickeyFight if the token is not printing, it's erroring within that portion. Do you know if you have Windows Authentication enabled for your Portal? For example, if you go to Portal, does it sign you in automatically?
@JakeSkinnerI dont think we have that enabled. I have to sign in sometimes
@RickeyFight looks like your portal URL is incorrect for generating the token. Replace your # Generate Token portion of the script with the following:
# Generate Portal token
tokenURL = 'https://gis.ashland.or.us/portal/sharing/rest/generateToken/'
params = {'f': 'pjson', 'username': username, 'password': password, 'referer': 'https://gis.ashland.or.us'}
r = requests.post(tokenURL, data = params, verify=False)
response = json.loads(r.content)
token = response['token']
print(token)
@JakeSkinnerThe token printed!
after the token I got this error:
{'returnGeometry': 'false', 'outFields': '*', 'statisticType': 'count', 'where': '1=1', 'f': 'pjson'}
{u'error': {u'message': u'Token Required', u'code': 499, u'details': []}}
Traceback (most recent call last):
File "C:\Users\user\Desktop\TestEmailSend.py", line 69, in <module>
for feat in response['features']:
KeyError: 'features'
You will need to apply the token to your params when querying the service:
params = {'f': 'pjson', 'where': "1=1", 'outFields' : '*', 'statisticType' : 'count', 'returnGeometry' : 'false', 'token' : token}
@JakeSkinner
That got it to run! Thank you!
Took way longer then expected and loaded a bunch of data then froze a few times. I am not sure why it keeps freezing. I tired running at a shorter interval (5min) and it still hangs up. I am afraid that if I run this on task scheduler that it will break my server. Any ideas?
I think it might be loading all features and there are over 1000
Edit: Before I edited the code it would just load the features that were created last and send an email. I am not sure what has changed besides logging in.
EDIT EDIT: I took out the print(response) and it fixed the issue.
Hi @JakeSkinner, you pointed me in this direction from a previous post (Send E-mail Notifications with ArcGIS Notebooks - ... - Esri Community) and i'm trying to understand what is needed and not for what i'm trying to do.
As a reminder, i'm looking to check my whole dataset and then send an email if the date field is within the next 6 months. I've got confidence to change the variables in the first half of the code (up to line 48), but i'm unsure whether I need to do lines 48 below in the way it's done above (original posted code).
I think i'd be okay to create a list of all features within the service and then somehow check for 6 months into the future? But there are a lot of variable/functions in the '#Query...' section of the if statement i'm not sure if i'd need also. I know I haven't got any code to go off but if you could offer a pointer or two about how would be best to do what i've mentioned above that'd be amazing!
@James_Shreeve can you share the service you are working with to a Group in AGOL and invite my user account: jskinner_rats. If you are able to, please indicate which date field you are looking to query, and how you plan on obtaining the e-mail addresses to notify.
Hi @JakeSkinner , thank you for this. I should have invited you to the group which contains the feature service i'd like to query/alert on (it's a copy of our live data but the same structure)...the date field which i'd like to alert against is the "Agreement_Date_End".
In terms of email addresses, I was going to have them statically in the code (within line 2) if my understanding of the below is correct? It won't be many email addresses and won't likely change.
fromEmail = 'no-reply@esri.com' # Email sender
toEmail = ['user1@esri.com', 'user2@esri.com'] # Email receiver(s)
smtpServer = 'smtp.esri.com' # SMTP Server Name
portNumber = 25
@James_Shreeve try the below. You will want to add all the users you want notified to a Group in AGOL. Then, update the allUsersGroupID variable in the script with ID of that Group.
from arcgis.gis import GIS
from arcgis.features import FeatureLayer
import datetime, time
from datetime import timedelta
# Variables
fsURL = 'https://services6.arcgis.com/h0X6cdaELg2mgWNZ/arcgis/rest/services/My_Sites_Copy/FeatureServer/0'
dateField = 'Agreement_Date_End'
months = 6
allUsersGroupID = '0a7e3ca5f5814ed884cbab31abb465bf'
emailSubject = 'Agreement Date'
emailMessage = 'Some message to include in e-mail'
# Generate AGOL
print('Connecting to AGOL')
gis = GIS("home")
# Get group members
allUsersGroup = gis.groups.get(allUsersGroupID)
userList = allUsersGroup.get_members()['users']
# Reference feature service from url
fLyr = FeatureLayer(fsURL)
# Query service and check if date field is within the last hour
print("Querying feature service")
queryResults = fLyr.query(where='1=1', out_fields="*", return_geometry=False)
for results in queryResults.features:
if results.attributes[dateField] != None:
createDate = results.attributes[dateField] / 1000
t = datetime.datetime.now() + timedelta(months=months)
t = time.mktime(t.timetuple())
if createDate > t:
# Send e-mail
print('Sending e-mail')
allUsersGroup.notify(users=userList, subject=emailSubject, message=emailMessage)
does this require GeoEvent Server license? If so we have that. I assume this can work with hosted features on Portal if it works on AGOL. Would it work with feature classes in a regular SDE? (i.e. via REST to a regular SQL Server SDE federated to portal but Referenced Data living on on-prem SDE)? I was trying to accomplish this exact same thing --- notify our GIS team when a point gets added, moved or deleted. Basically compare geometry of the FC today to yesterday. I wrote a script to export it out daily and compare with Feature Compare but noticed strange unexpected results when using Geometry_Only. https://community.esri.com/t5/arcgis-pro-questions/how-can-we-compare-geometry-between-two-fcs-to-se...
Whatever approach works I'm open to! Will try this one, if it can work with data on SDE. Jake as always for quite a long time thank you for your in-depth replies and guidance.
Hi @JakeSkinner, apologies i've only just seen this reply...I didn't seem to get a notification.
I'll look at testing this code over the next couple of days. Thank you so much for helping out with this! Will be such a powerful addition to our appplication if it works. I'll update you on the progress
@JakeSkinner I've had a go with this today and have managed to get it to work, i've had to change the 'months' parameter and timedelta(months=months) to make it work as it didn't like the 'months' argument.
The result of this is that it's sent 10 separate emails (great!) to all users in the group (apart from myself, the group owner). However, there is no relationship between the email and which feature it is referring to. Is there a way to input dynamic text into the email based on the feature it finds during the code?
from arcgis.gis import GIS
from arcgis.features import FeatureLayer
import datetime, time
from datetime import timedelta
# Variables
fsURL = 'https://services6.arcgis.com/h0X6cdaELg2mgWNZ/arcgis/rest/services/My_Sites_Copy/FeatureServer/0'
dateField = 'Agreement_Date_End'
weeks_check = 26
allUsersGroupID = 'aed962a2e2574c0895644002c512a37a'
emailSubject = 'TEST - URGENT - Agreement Date'
emailMessage = 'This is a test email. Please ignore. Some message to include in e-mail'
# Generate AGOL
print('Connecting to AGOL')
gis = GIS("home")
# Get group members
allUsersGroup = gis.groups.get(allUsersGroupID)
userList = allUsersGroup.get_members()['users']
# Reference feature service from url
fLyr = FeatureLayer(fsURL)
# Query service and check if date field is within the last hour
print("Querying feature service")
queryResults = fLyr.query(where='1=1', out_fields="*", return_geometry=False)
for results in queryResults.features:
if results.attributes[dateField] != None:
createDate = results.attributes[dateField] / 1000
t = datetime.datetime.now() + timedelta(weeks=weeks_check)
t = time.mktime(t.timetuple())
if createDate > t:
# Send e-mail
print('Sending e-mail')
allUsersGroup.notify(users=userList, subject=emailSubject, message=emailMessage)