Send Email When a Feature is Added to an ArcGIS Online Hosted Feature Service

49078
176
06-02-2017 05:46 AM

Send Email When a Feature is Added to an ArcGIS Online Hosted Feature Service

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 'CreationDate'.  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 a specified field's values for the new features.

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 (probtype & status) when querying the service.  These variables can be commented out, or updated to reference different fields, if working with a different service:

JakeSkinner_4-1627925313899.png

 

The SUBJECT and TEXT variables in the sendEmail function can be updated to whatever you would like to include in the e-mail:

JakeSkinner_5-1627925339918.png

 

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
toEmail = ['user1@esri.com', 'user2@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}".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']
            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

 

Comments

Monika Samorajska‌ are you looking to include additional fields other than the OBJECTID in the e-mail?

Hi  Jake Skinner 

Ideally I woul like to have more information, like two more fields, but I think this script is looking for the "OBJECTID" field and the information about "OBJECTID" is enough for me, as this is probably the only uniqeid which I have in the service. Are you able to help me with the code to achive that?

Wishes,

Monika

Ok Jake Skinner‌ iinvitation send

Anselme

GIS Developer

Dambé SANWOGOU‌ you will need to share the service with the Group.

Ok ok Jake Skinner i share it, many thanks,

Best regards,

Anselme

Dambé SANWOGOU‌ there will be a couple changes you need to make.  You will have to specify CreationDate for the dateField variable.  Also, there are some NULL values for this field, so I had to add an IF statement to check if the date was not NULL.  Try the below:

import requests, json, datetime, time, smtplib, math
from datetime import timedelta

# Disable warnings
requests.packages.urllib3.disable_warnings()

# Variables
username = '***********'        # AGOL Username
password = '***********'    # AGOL Password

URL = 'https://services3.arcgis.com/Wobi1dRiFkLPw0IP/arcgis/rest/services/TestPUDCUpdate/FeatureServer/0/qu...'       # Feature Service URL
uniqueID = 'OBJECTID'           # i.e. OBJECTID
dateField = 'CreationDate'      # Date field to query
hoursValue = 1                  # Number of hours to check when a feature was added

fromEmail = 'anselme.s@hitech-services.com' # Email sender
toEmail = 'anselme.s@hitech-services.com'   # Email receiver
smtpServer = 'mail.hitech-services.com'    # SMPT Server Name
portNumber = 587                 # SMTP Server port

# Create empty list for uniqueIDs
oidList = []

# 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))

OIDs = response['objectIds']
count = len(response['objectIds'])
iteration = int(response['objectIds'][-1])
minOID = int(response['objectIds'][0]) - 1
OID = response['objectIdFieldName']

# Query service and check if created_date time is within the last hour
if count < 1000:
    params = {'f': 'pjson', 'where': "1=1", 'outFields' : '{0}, {1}'.format(uniqueID, dateField), 'returnGeometry' : 'false', 'token' : token}
    r = requests.post(URL, data=params, verify=False)
    response = json.loads(r.content)
    for feat in response['features']:
        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:
            oidList.append(feat['attributes'][uniqueID])
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' : '{0}, {1}'.format(uniqueID, dateField), 'returnGeometry' : 'false', 'token' : token}
        r = requests.post(URL, data=params, verify=False)
        response = json.loads(r.content)
        for feat in response['features']:
            createDate = feat['attributes'][dateField]
            if createDate != None:
                createDate = int(str(createDate)[0:-3])
                t = datetime.datetime.now() - timedelta(hours=hoursValue)
                t = time.mktime(t.timetuple())
                if createDate > t:
                    oidList.append(feat['attributes'][uniqueID])
        x += 1000
        y += 1000

print(oidList)

# Email Info
FROM = fromEmail
TO = [toEmail]
SUBJECT = 'New Features Added'
TEXT = "Features with {0}s {1} were added.".format(uniqueID, oidList)

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()

Monika Samorajska‌ can you share the service to an ArcGIS Online group and invite my user account (jskinner_CountySandbox)?  Can you also let me know what information you want included in the notification?

Jake Skinner‌ I have invited you yo the group. So the Feature Service contain one feature class with the equipment location and thwo related tables. One table is related to the Feature Class and the other table is related the the first  The feature service is editable of-course. In the e-mail I would like to have the information depending on what has been changed, when the new feature was added to the feature class I would like to receive an e-mail which says that the "oOBJECTID" number 1 (for example) has been added, when the information in related table has been changed I would like to have the information that "objectid" nr 1 in related table has added.

Please let me know if you understand my request.

Monika

Monika Samorajska‌ looks like only the Web Application was shared to the group, so I didn't have access to the Feature Service.  However, you should be able to do this by specifying the URLs to the feature and related table in a list.  Ex:

URLs = ['http://services.arcgis.com/dlFJXQQtlabc4qUk/arcgis/rest/services/PropertyDamage/FeatureServer/0/quer...',
       'http://services.arcgis.com/dlFJXQQtlabc4qUk/arcgis/rest/services/PropertyDamage/FeatureServer/1/quer...']

You can then specify a layer name using an IF statement:

for URL in URLs:
    if URL.split("/")[-2] == 0:
       layerName = 'PropertyDamage'
    elif URL.split("/")[-2] == 1:
       layerName = 'RelatedTable'

Here is the entire code portion:

import requests, json, datetime, time, smtplib
from datetime import timedelta

# Disable warnings
requests.packages.urllib3.disable_warnings()

# Variables
username = 'agol'        # AGOL Username
password = 'gis12345'    # AGOL Password

URLs = ['http://services.arcgis.com/dlFJXQQtlabc4qUk/arcgis/rest/services/PropertyDamage/FeatureServer/0/quer...',
       'http://services.arcgis.com/dlFJXQQtlabc4qUk/arcgis/rest/services/PropertyDamage/FeatureServer/1/quer...']
uniqueID = 'OBJECTID'           # i.e. OBJECTID
dateField = 'created_date'      # Date field to query
hoursValue = 1                  # Number of hours to check when a feature was added

fromEmail = 'user1@esri.com' # Email sender
toEmail = 'manager1@esri.com'   # Email receiver
smtpServer = 'smtp.gis.com'    # SMPT Server Name
portNumber = 25                 # SMTP Server port


for URL in URLs:
    if URL.split("/")[-2] == 0:
       layerName = 'PropertyDamage'
    elif URL.split("/")[-2] == 1:
       layerName = 'RelatedTable'
    # Create empty list for uniqueIDs
    oidList = []

    # 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))

    OIDs = response['objectIds']
    count = len(response['objectIds'])
    iteration = int(response['objectIds'][-1])
    minOID = int(response['objectIds'][0]) - 1
    OID = response['objectIdFieldName']

    # Query service and check if created_date time is within the last hour
    if count < 1000:
        params = {'f': 'pjson', 'where': "1=1", 'outFields' : '{0}, {1}'.format(uniqueID, dateField), 'returnGeometry' : 'false', 'token' : token}
        r = requests.post(URL, data=params, verify=False)
        response = json.loads(r.content)
        for feat in response['features']:
            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:
                oidList.append(feat['attributes'][uniqueID])
    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' : '{0}, {1}'.format(uniqueID, dateField), 'returnGeometry' : 'false', 'token' : token}
            r = requests.post(URL, data=params, verify=False)
            response = json.loads(r.content)
            for feat in response['features']:
                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:
                    oidList.append(feat['attributes'][uniqueID])
            x += 1000
            y += 1000

    print(oidList)

    # Email Info
    FROM = fromEmail
    TO = [toEmail]
    SUBJECT = 'New Features Added'
    TEXT = "Features with {0}s {1} were added to {2}.".format(uniqueID, oidList, layerName)

    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()

Jake Skinner‌ yes, you are right. I have just shared Feature Service in the group, if you want to check it.

I have tried the script with your changes, but i have received this error message:

line 118
    if len(oidList) > 0:
    ^
IndentationError: unexpected indent

Do you know what can cause this issue?

Monika Samorajska‌ this is indicating your indentation for the code is not correct.  Can you post the code you executed?  Be sure to post the code in blocks so the indentation is preserved.

Hi Jake Skinner,

here is my code. I have tried to run it, but there is still some erroe message.

The error message is like below:

line 41
    whereClause = '1=1'
                      ^
IndentationError: unindent does not match any outer indentation level

Any thoughts?

import requests, json, datetime, time, smtplib
from datetime import timedelta

# Disable warnings
requests.packages.urllib3.disable_warnings()

# Variables
username = '**********'        # AGOL Username
password = '**********'    # AGOL Password

URL = ['https://arcgis.egis.fr/arcgis/rest/services/MMA_CSQ/CSQ_RAM_FRACAS_DesignSys_Equipment_Pt/FeatureSer...', 'https://arcgis.egis.fr/arcgis/rest/services/MMA_CSQ/CSQ_RAM_FRACAS_DesignSys_Equipment_Pt/FeatureSer...','https://arcgis.egis.fr/arcgis/rest/services/MMA_CSQ/CSQ_RAM_FRACAS_DesignSys_Equipment_Pt/FeatureSer...'] # Feature Service URL
uniqueID = 'objectid'           # i.e. OBJECTID
dateField = 'last_edited_date'      # Date field to query
hoursValue = 1                  # Number of hours to check when a feature was added

fromEmail = '**********' # Email sender
toEmail = '***********'   # Email receiver
smtpServer = 'smtp.gmail.com'    # SMPT Server Name
portNumber = 465                 # SMTP Server port

for URL in URLs:
     if URL.split("/")[-2] == 0:
        layerName = 'PropertyDamage'
     elif URL.split("/")[-2] == 1:
        layerName = 'RelatedTable'
     # Create empty list for uniqueIDs
     oidList = []

# Generate AGOL token
try:
          print('Generating Token')
          tokenURL = 'https://egisenv.maps.arcgis.com/home/groups.html/sharing/rest/generateToken'
          params = {'f': 'pjson', 'username': username, 'password': password, 'referer': 'https://egisenv.maps.arcgis.com/home/groups.html'}
          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))

    OIDs = response['objectIds']
    count = len(response['objectIds'])
    iteration = int(response['objectIds'][-1])
    minOID = int(response['objectIds'][0]) - 1
    OID = response['objectIdFieldName']

# Query service and check if last_edited_date time is within the last hour
if count < 1000:
     params = {'f': 'pjson', 'where': "1=1", 'outFields' : '{0}, {1}'.format(uniqueID, dateField), 'returnGeometry' : 'false', 'token' : token}
     r = requests.post(URL, data=params, verify=False)
     response = json.loads(r.content)
     for feat in response['features']:
          createDate = feat['attributes'][dateField]
          if createDate is not None:
               createDate = int(str(createDate)[0:-3])
               t = datetime.datetime.now() - timedelta(hours=hoursValue)
               t = time.mktime(t.timetuple())
               if createDate > t:
                    oidList.append(feat['attributes'][uniqueID])
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' : '{0}, {1}'.format(uniqueID, dateField), 'returnGeometry' : 'false', 'token' : token}
          r = requests.post(URL, data=params, verify=False)
          response = json.loads(r.content)
          for feat in response['features']:
               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:
                    oidList.append(feat['attributes'][uniqueID])
          x += 1000
          y += 1000

print(oidList)

# Email Info
server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
server.login("**********", "**********")

FROM = '*************'
TO = ['*************']
SUBJECT = 'New Features Added'
TEXT = "Features with {0}s {1} were added to {2}.".format(uniqueID, oidList, layerName)

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()

Monika Samorajska‌ since you are using ArcGIS Server services, you won't have to generate a token.  Try the following:

import requests, json, datetime, time, smtplib
from datetime import timedelta

# Disable warnings
requests.packages.urllib3.disable_warnings()

# Variables
username = '****'        # AGOL Username
password = '****'        # AGOL Password

URLs = ['https://arcgis.egis.fr/arcgis/rest/services/MMA_CSQ/CSQ_RAM_FRACAS_DesignSys_Equipment_Pt/FeatureSer...',
       'https://arcgis.egis.fr/arcgis/rest/services/MMA_CSQ/CSQ_RAM_FRACAS_DesignSys_Equipment_Pt/FeatureSer...',
       'https://arcgis.egis.fr/arcgis/rest/services/MMA_CSQ/CSQ_RAM_FRACAS_DesignSys_Equipment_Pt/FeatureSer...'] # Feature Service URL
uniqueID = 'objectid'           # i.e. OBJECTID
dateField = 'last_edited_date'      # Date field to query
hoursValue = 1                  # Number of hours to check when a feature was added

fromEmail = '**********' # Email sender
toEmail = '***********'   # Email receiver
smtpServer = 'smtp.gmail.com'    # SMPT Server Name
portNumber = 465                 # SMTP Server port

token = ''

for URL in URLs:
    if URL.split("/")[-2] == '0':
        layerName = 'CSQ_DesignSys_Pt'
    elif URL.split("/")[-2] == '3':
        layerName = 'CSQ_RAM_Deffect_Ta'
    elif URL.split("/")[-2] == '4':
        layerName = 'CSQ_RAM_ACTION_Ta'
    # Create empty list for uniqueIDs
    oidList = []

    # 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))

    OIDs = response['objectIds']
    if response['objectIds'] != None:
        count = len(response['objectIds'])
        iteration = int(response['objectIds'][-1])
        minOID = int(response['objectIds'][0]) - 1
        OID = response['objectIdFieldName']

        # Query service and check if last_edited_date time is within the last hour
        if count < 1000:
             params = {'f': 'pjson', 'where': "1=1", 'outFields' : '{0}, {1}'.format(uniqueID, dateField), 'returnGeometry' : 'false', 'token' : token}
             r = requests.post(URL, data=params, verify=False)
             response = json.loads(r.content)
             for feat in response['features']:
                  createDate = feat['attributes'][dateField]
                  if createDate is not None:
                       createDate = int(str(createDate)[0:-3])
                       t = datetime.datetime.now() - timedelta(hours=hoursValue)
                       t = time.mktime(t.timetuple())
                       if createDate > t:
                            oidList.append(feat['attributes'][uniqueID])
        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' : '{0}, {1}'.format(uniqueID, dateField), 'returnGeometry' : 'false', 'token' : token}
                  r = requests.post(URL, data=params, verify=False)
                  response = json.loads(r.content)
                  for feat in response['features']:
                       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:
                            oidList.append(feat['attributes'][uniqueID])
                  x += 1000
                  y += 1000

        print(oidList)

        # Email Info
        server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
        server.login("**********", "**********")

        FROM = '*************'
        TO = ['*************']
        SUBJECT = 'New Features Added'
        TEXT = "Features with {0}s {1} were added to {2}.".format(uniqueID, oidList, layerName)

        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()

Hi Jake Skinner,

I have just tried corrected script, and I've got the error message.

See below:


Error: 'NoneType' object has no attribute 'sort'

I have shared the FC in the group, which you have an access to. Any thoughts?

Monika Samorajska‌ that error is fine.  It is just indicating that there are no features in the related table.  One thing to add is the word pass under this Error statement.  Make sure your indentation is correct:

try:
  response['objectIds'].sort()
except Exception as e:
  print("Error: {0}".format(e))
  pass

Thank you Jake Skinner , I got to do this. I added a python code to make an authentication for email.

import requests, json, datetime, time, smtplib, math
from datetime import timedelta

# Disable warnings
requests.packages.urllib3.disable_warnings()

# Variables
username = 'xxxxxxxxxxx'        # AGOL Username
password = 'xxxxxxxxxxxxxx'    # AGOL Password

uniqueID = 'OBJECTID'           # i.e. OBJECTID
dateField = 'CreationDate'      # Date field to query
hoursValue = 1                  # Number of hours to check when a feature was added

fromEmail = 'anselme.s@hitech-services.com' # Email sender
toEmail = 'anselme.s@hitech-services.com'   # Email receiver
smtpServer = 'xxxxxxxxxxxxxxxx'    # SMPT Server Name
portNumber = xxx                 # SMTP Server port

# Create empty list for uniqueIDs
oidList = []

# Generate AGOL token
try:
        print('Generating Token')
        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'}
= 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))

OIDs = response['objectIds']
count = len(response['objectIds'])
iteration = int(response['objectIds'][-1])
minOID = int(response['objectIds'][0]) - 1
OID = response['objectIdFieldName']

# Query service and check if created_date time is within the last hour
if count < 1000:
    params = {'f''pjson''where'"1=1"'outFields' : '{0}{1}'.format(uniqueID, dateField), 'returnGeometry' : 'false''token' : token}
    r = requests.post(URL, data=params, verify=False)
    response = json.loads(r.content)
    for feat in response['features']:
        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:
            oidList.append(feat['attributes'][uniqueID])
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' : '{0}{1}'.format(uniqueID, dateField), 'returnGeometry' : 'false''token' : token}
        r = requests.post(URL, data=params, verify=False)
        response = json.loads(r.content)
        for feat in response['features']:
            createDate = feat['attributes'][dateField]
            if createDate != None:
                createDate = int(str(createDate)[0:-3])
                t = datetime.datetime.now() - timedelta(hours=hoursValue)
                t = time.mktime(t.timetuple())
                if createDate > t:
                    oidList.append(feat['attributes'][uniqueID])
        x += 1000
        y += 1000

print(oidList)

# Email Info
FROM = fromEmail
TO = [toEmail]
SUBJECT = 'New Features Added'
TEXT = "Features with {0}{1} were added.".format(uniqueID, oidList)

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='smtp host name'port='smtp port')
    smtpObj.login("your email","your email password")
    smtpObj.sendmail(FROM, TO, message)
    print("Successfully sent email")
    smtpObj.quit()

Jake Skinner‌ I managed to work the script. Thanks.

Is there a possibility to change that the script will be looking for the changes in the Feature Services and in related tables? So instead of if it will be and function?

Any thoughts?

Monika Samorajska‌ I'm not quite sure what you're asking.  Can you provide an example?

Hi Jake Skinner,

I would like to receive an e-mail with information including all hanges which have been made in the FS. Basically, I would like to set up the script to detecting the changes in the Feature Class, first related table and the second related table as well. For example, in FC there is location off traffic lights on the road, but someone surveyed this part of the road and add the new traffic light, and in the same time the same person added a new defect for the different traffic light. So I would like to receive the e-mail which says that the feature with Object ID number 5 has been added to the FC and the new defect with the object id 25 has been added to the related table.

Do you think is it possibile?

Monika

Jake Skinner - Thanks for this documentation.  I have everything working except when I attempt to send to multiple recipients, only the first recipient (myself) is the only one to actually receive the email.  However, when the email comes into my outlook inbox, the email is showing the other recipients should have received the email (but they didn't).

My script uses the format below in the toEmail:

toEmail = 'xxxx@xxxxx.com; xxxxx@xxxx.com'   # Email receiver(s)

Thanks in advance for any assistance you can provide.

David Light‌ I just updated the code.  Try this version and see if it works for you.

Anonymous User

How would I alter this to check every 15min rather than every hour?

Anthony Von Moos‌ you can change the hoursValue variable from 1 to 0.25:

hoursValue = 0.25
Anonymous User

Thank you.

Anonymous User

Another question I have is how would I modify the script to return everything in my where clause without time being a factor? I'm not worried about when it was added but only want to send a notification email if my where clause is true. Below is the where clause I'm looking to use.

assigned_to = "Full Name" AND date_resolved IS NULL

Try the following:

if count < 1000:
    where = "assigned_to = 'Full Name' AND date_resolved IS NULL"
    params = {'f': 'pjson', 'where': where, 'outFields' : '*', 'returnGeometry' : 'false', 'token' : token}
    r = requests.post(URL, data=params, verify=False)
    response = json.loads(r.content)
    for feat in response['features']:
        oidList.append(feat['attributes']['OBJECTID'])
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 = "assigned_to = 'Full Name' AND date_resolved IS NULL AND " + 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, data=params, verify=False)
        response = json.loads(r.content)
        for feat in response['features']:
            oidList.append(feat['attributes']['OBJECTID'])
        x += 1000
        y += 1000

Thanks for sharing this.

Is there some way that I can get this to send an email for each addition?

For example, if the script detect 5 records, I want 5 emails to be sent. One for each record.

 

Thanks again.

Helder Vieira‌ yes, you could add a for loop to do this.  Ex:

# If new features exist, send email
if len(oidList) > 0:
    for oid in oidList:
        # Email Info
        SUBJECT = 'New Feature Added'
        TEXT = "Feature with {0}s {1} were added.".format(uniqueID, oid)
        
        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()

Jake Skinner

Thanks for all the help you provide with this code. 

I have had this working for a few months now. My clients now want a date field to be included with the email. Is this possible? 

I tried adding a the date and I got: Date Sighted : ['1589552234000']

Thanks! 

I was able to change the date using the answer here: 

 

Format esriFieldTypeDate from map service 

 

It took a little work but here is the result 

Date Sighted : 2020-05-13 14:30:00 

Can this be set up to send to different recipients based on different field selections for a new feature? For example if fieldX = A send an email to recipientA@gmail.com, if fieldX = B send an email to recipientB@gmail.com, etc. 

Karlie Kendall‌, yes this is possible.  You can put the send email portion of the script in a function.  This will allow you to pass which toEmail you would like to send the e-mail to.  You will also update the Search Cursors to include all fields and create variable for the field which would dictate which e-mail to send to.  Ex:

See below:

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 = 'agol'        # AGOL Username
password = 'gis12345'    # AGOL Password

URL = 'http://services.arcgis.com/dlFJXQQtlabc4qUk/arcgis/rest/services/PropertyDamage/FeatureServer/0/quer...'       # Feature Service URL
uniqueID = 'OBJECTID'           # i.e. OBJECTID
dateField = 'created_date'      # Date field to query
hoursValue = 1                  # Number of hours to check when a feature was added

fromEmail = 'user1@esri.com' # Email sender
toEmail = ['manager1@esri.com', 'manager2@esri.com']   # Email receiver(s)
smtpServer = 'smtp.gis.com'    # SMPT Server Name
portNumber = 25                 # SMTP Server port

# Create empty list for uniqueIDs
oidList = []

def sendEmail(toEmail):
    # Email Info
    SUBJECT = 'New Features Added'
    TEXT = "Features with {0}s {1} were added.".format(uniqueID, oidList)

    # If new features exist, send email
    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))

OIDs = response['objectIds']
count = len(response['objectIds'])
iteration = int(response['objectIds'][-1])
minOID = int(response['objectIds'][0]) - 1
OID = response['objectIdFieldName']

# Query service and check if created_date 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']:
        xField = feat['attributes']['xField']
        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:
            oidList.append(feat['attributes'][uniqueID])
            if xField == 'this value':
              toEmail = 'this@email.com'
            elif xField == 'other value':
              toEmail = 'other@email.com'
            sendEmail(toEmail)
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, data=params, verify=False)
        response = json.loads(r.content)
        for feat in response['features']:
            xField = feat['attributes']['xField']
            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:
                oidList.append(feat['attributes'][uniqueID])
                if xField == 'this value':
                  toEmail = 'this@email.com'
                elif xField == 'other value':
                  toEmail = 'other@email.com'
                sendEmail(toEmail)
        x += 1000
        y += 1000

Note:  I have not tested the above.

I wanna understand the python script to send email but I can not understand where I post the code to run in my feature service...

Any help in this regard is much appreciated! 

Hammad Khalid‌ you will run the code on any machine that has python installed, for example, on a machine that has ArcGIS Pro or ArcGIS Desktop installed.  See the following document on how you can use Windows Task Scheduler to execute the python script.

I checked windows task function but code did not work!

Does the code work when you run it in a Python IDE (such as IDLE)?  Can you post the code your trying to execute?  This document will provide you the steps to properly post your code:

https://community.esri.com/docs/DOC-8691-posting-code-with-syntax-highlighting-on-geonet 

Jeffrey Steele‌ can you invite my AGOL account to a Group in AGOL that you have the service shared to?  My AGOL account is jskinner_CountySandbox.  I can take a look at the service and see how to modify the script.

Since this thread pre-dates the availability of webhooks in hosted feature layers I’ll offer this option for those with Data Interoperability extension, which has emailing capability (not discussed in the post):

https://community.esri.com/community/arcgis-data-interoperability/blog/2020/09/29/power-your-integrations-with-arcgis-online-feature-service-webhooks-and-data-interoperability

Jeffrey Steele try the following code below.  Here are the steps:

1.  Update the username, password, fromEmail, toEmail, smtpServer in the # Variables section:

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://services5.arcgis.com/JyUjaMA8RG5613cC/ArcGIS/rest/services/CitizenProblems_d15c40efcecc4c478...'       # Feature Service URL
uniqueID = 'OBJECTID'                      # i.e. OBJECTID
dateField = 'CreationDate'                 # Date field to query
hoursValue = 1                             # Number of hours to check when a feature was added
fromEmail = 'user1@esri.com' # Email sender
toEmail = ['manager1@esri.com', 'manager2@esri.com']   # Email receiver(s)
smtpServer = 'smtp.gis.com'    # SMPT Server Name
portNumber = 25                 # SMTP Server port


# Create empty list for uniqueIDs
oidList = []


# 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))


OIDs = response['objectIds']
count = len(response['objectIds'])
iteration = int(response['objectIds'][-1])
minOID = int(response['objectIds'][0]) - 1
OID = response['objectIdFieldName']


# Query service and check if created_date time is within the last hour
if count < 1000:
    params = {'f': 'pjson', 'where': "1=1", 'outFields' : '{0}, {1}'.format(uniqueID, dateField), 'returnGeometry' : 'false', 'token' : token}
    r = requests.post(URL, data=params, verify=False)
    response = json.loads(r.content)
    for feat in response['features']:
        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:
            oidList.append(feat['attributes'][uniqueID])
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' : '{0}, {1}'.format(uniqueID, dateField), 'returnGeometry' : 'false', 'token' : token}
        r = requests.post(URL, data=params, verify=False)
        response = json.loads(r.content)
        for feat in response['features']:
            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:
                oidList.append(feat['attributes'][uniqueID])
        x += 1000
        y += 1000


print(oidList)


# Email Info
SUBJECT = 'New Features Added'
TEXT = "Features with {0}s {1} were added.".format(uniqueID, oidList)


# If new features exist, send email
if len(oidList) > 0:
    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()

2.  Add a new point to the Citizens Problem feature service

3.  Execute the script

JakeSkinner

I get to here and it stops when I run the module

 

OIDs = response['OBJECTID']
count = len(response['objectIds'])
iteration = int(response['objectIds'][-1])
minOID = int(response['objectIds'][0]) - 1
OID = response['objectIdFieldName']

Hi to all, Please can anyone help me out for making a script when any feature is deleted from Server, will receive an alert on my email. Highly appreciate your help.

My suggestion @UmarNawaz would be to not delete features, but mark them as "removed" or something with a new column. Users would then update it rather than delete it and they would be hidden from view on the map with a definition query or filter. Then, you could slightly modify this original script to only look at the latest date modified on features with a value of "removed".

Hi,

 

I am able to get the email whenever feature is getting added to the Hosted FS thru a Survey123 form. I need the user as well as an Internal contact get email about the new feature. User's email is getting captured in the field "Email" of the hosted FS. After a new feature is added, additional fields are updated thru a CrowdSource manager app, by the internal user. At that point, can we send an update email to the user who puts a request and creates a new feature thru Survey123 app along with an attachment? Please let me know. TIA.

@JakeSkinner 

I've got the script working great for new updates using the "CreationDate" field.  But I'm trying to revise the script to use the "EditDate" field to capture only updates, i.e.:

if editDate is not None and editDate != createDate:

The json response pulls the correct Objectid of the most recently updated feature, but the fields in the email output are from the newest feature added.  Any help would be much appreciated!  Thanks!

code minus the credentials: 

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 = '' # AGOL Username
password = '' # AGOL Password

URL = 'https://services5.arcgis.com/lt8CbgYaNuFrQ7kB/arcgis/rest/services/CitizenProblems_b11a3a634eea4fa09192e6372a59daa5/FeatureServer/0/query' # Feature Service URL
uniqueID = 'OBJECTID' # i.e. OBJECTID
dateField1 = 'CreationDate' # Date field to query
dateField2 = 'EditDate' # Date field to query
hoursValue = 1 # Number of hours to check when a feature was added

fromEmail = '' # Email sender
smtpServer = '' # SMPT Server Name
portNumber = 25 # SMTP Server port

# Create empty list for uniqueIDs
oidList = []
updateFC = []

# 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))

OIDs = response['objectIds']
count = len(response['objectIds'])
iteration = int(response['objectIds'][-1])
minOID = int(response['objectIds'][0]) - 1
OID = response['objectIdFieldName']


# 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']:
pocemail2 = feat['attributes']['pocemail2']
category = feat['attributes']['category']
typeProb = feat['attributes']['probtype']
probDetails = feat['attributes']['details3']
createDate = feat['attributes'][dateField1]
editDate = feat['attributes'][dateField2]
status = feat['attributes']['status']
assignedto = feat['attributes']['assignedto']
personnel = feat['attributes']['Personnel']
if editDate is not None and editDate != createDate:
editDate = int(str(editDate)[0:-3])
t = datetime.datetime.now() - timedelta(hours=hoursValue)
t = time.mktime(t.timetuple())
if editDate > t:
oidList.append(feat['attributes'][uniqueID])
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' : '{0}, {1}'.format(uniqueID, dateField2), 'returnGeometry' : 'false', 'token' : token}
r = requests.post(URL, data=params, verify=False)
response = json.loads(r.content)
for feat in response['features']:
editDate = feat['attributes'][dateField2]
editDate = int(str(editDate)[0:-3])
t = datetime.datetime.now() - timedelta(hours=hoursValue)
t = time.mktime(t.timetuple())
if editDate > t:
oidList.append(feat['attributes'][uniqueID])
x += 1000
y += 1000

print(oidList)


s = editDate / 1000.0
s2 = createDate / 1000.0
# dFormat = "%d-%m-%Y %h:%m"
d = datetime.datetime.fromtimestamp(s).strftime("%m-%d-%Y %H:%M %p")
d2 = datetime.datetime.fromtimestamp(s2).strftime("%m-%d-%Y %H:%M %p")
print(d)
print(d2)

# Email Info
SUBJECT = 'Status Updated - Murphy Citizen Problem Reporter'
html = """\
<html>
<body>
<p font-family: Arial>A "{0}" problem has been updated!</p>

<p><b>Problem Overview:</b></p>
<p>Status Update: {1}</p>
<p>Assigned To: {2}</p>
<p>Please allow 72 hours for a City of Murphy employee to contact you regarding this problem. You can also check the progress of your problem here:</p>
<p><a href="https://murphytx.maps.arcgis.com/apps/CrowdsourceReporter/index.html?appid=6200a42374604a91b5a3956951518abe" target="_blank">Murphy Citizen Problem Reporter</a>
</p>
<p>Thank you,</p>
</body>
</html>
<td style="color:DarkGreen; font-family: Arial, sans-serif; font-size: 14px;">
<b>City of Murphy</b><br />
<a href="mailto:gis@murphytx.org">gis@murphytx.org</a><br />
</td>
""".format(typeProb, status, assignedto)

# If new features exist, send email
if len(oidList) > 0:
smtpObj = smtplib.SMTP(host=smtpServer, port=portNumber)

msg = MIMEText("alternative")
msg = MIMEText(html, "html")
msg['Subject'] = SUBJECT
msg['From'] = fromEmail
msg['To'] = pocemail2

smtpObj.sendmail(fromEmail, pocemail2, msg.as_string())
print("Successfully sent email")
smtpObj.quit()

results:

Generating Token
[]
03-30-2021 08:54 AM
03-30-2021 08:54 AM

Process finished with exit code 0

 

 

 

 

@Danielle_Journey 

You would need to send the e-mail as you iterate through the features.  One way to do this is by moving the code to send the e-mail into a function.  In the for loop, you can call the function and send the appropriate e-mail.  Try the following:

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://services.arcgis.com/dlFJXQQtlWFB4qUk/arcgis/rest/services/CitizenProblems/FeatureServer/0/query'       # Feature Service URL
uniqueID = 'OBJECTID' # i.e. OBJECTID
dateField1 = 'CreationDate'  # Date field to query
dateField2 = 'EditDate'  # Date field to query
hoursValue = 1  # Number of hours to check when a feature was added

fromEmail = '' # Email sender
smtpServer = ''    # SMPT Server Name
portNumber = 25    # SMTP Server port

# Create empty list for uniqueIDs
oidList = []
updateFC = []

# Function to send email
def sendEmail(typeProb, status, assignedto):
    SUBJECT = 'Status Updated - Murphy Citizen Problem Reporter'
    html = """\
    <html>
    <body>
     <p font-family: Arial>A "{0}" problem has been updated!</p>

     <p><b>Problem Overview:</b></p>
     <p>Status Update: {1}</p>
     <p>Assigned To: {2}</p>
     <p>Please allow 72 hours for a City of Murphy employee to contact you regarding this problem. You can also check the progress of your problem here:</p>
     <p><a href="https://murphytx.maps.arcgis.com/apps/CrowdsourceReporter/index.html?appid=6200a42374604a91b5a3956951518abe" target="_blank">Murphy Citizen Problem Reporter</a>
     </p>
     <p>Thank you,</p>
    </body>
    </html>
    <td style="color:DarkGreen; font-family: Arial, sans-serif; font-size: 14px;">
     <b>City of Murphy</b><br />
     <a href="mailto:gis@murphytx.org">gis@murphytx.org</a><br />
    </td>
    """.format(typeProb, status, assignedto)
    smtpObj = smtplib.SMTP(host=smtpServer, port=portNumber)

    msg = MIMEText("alternative")
    msg = MIMEText(html, "html")
    msg['Subject'] = SUBJECT
    msg['From'] = fromEmail
    msg['To'] = pocemail

    smtpObj.sendmail(fromEmail, pocemail, 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))

OIDs = response['objectIds']
count = len(response['objectIds'])
iteration = int(response['objectIds'][-1])
minOID = int(response['objectIds'][0]) - 1
OID = response['objectIdFieldName']


# 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']:
        pocemail = feat['attributes']['pocemail']
        category = feat['attributes']['category']
        typeProb = feat['attributes']['probtype']
        createDate = feat['attributes'][dateField1]
        editDate = feat['attributes'][dateField2]
        status = feat['attributes']['status']
        assignedto = feat['attributes']['assignedto']
        if editDate is not None and editDate != createDate:
            editDate = int(str(editDate)[0:-3])
            t = datetime.datetime.now() - timedelta(hours=hoursValue)
            t = time.mktime(t.timetuple())
            if editDate > t:
                sendEmail(typeProb, status, assignedto)


s = editDate / 1000.0
s2 = createDate / 1000.0
# dFormat = "%d-%m-%Y %h:%m"
d = datetime.datetime.fromtimestamp(s).strftime("%m-%d-%Y %H:%M %p")
d2 = datetime.datetime.fromtimestamp(s2).strftime("%m-%d-%Y %H:%M %p")
print(d)
print(d2)

@JakeSkinner  Thank you so much for your help!  That worked!!

 

 

Hi @JakeSkinner ,

I have two questions:

- How can I modify the original script to include additional fields other than the OBJECTID in the e-mail?

- How can I change the script to send one e-mail for one change only? If there are two or three changes in last hour, I would like to receive two/ three e-mails.

Thank you in advance.

Regards,

Monika

 

 

@MonikaSamorajskatake a look at the below code:

 

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://services.arcgis.com/dlFJXQQtlWFB4qUk/ArcGIS/rest/services/CitizenProblems/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()

 

 

Where I'm creating choosing which fields to include in the e-mail is the below section:

JakeSkinner_0-1617889235018.png

I'm selecting fields probtype, status, and assignedto.  The e-mail is sent through a function where these new field variables are included in the message:

JakeSkinner_1-1617889427464.png

 

@JakeSkinner 

Thanks again for this awesome resource. I am getting an error that you might be able to help me with. 

 

Someone put "didn't" in a field and the script did not like the '.  Any idea how to get around this? 

Error:

UnicodeEncodeError: 'ascii' codec can't encode character u'\u2019' in position 4: ordinal not in range(128)

@RickeyFighthow are you calling the field that contains didn't?  Are you trying to include this in the e-mail that is sent?  Feel free to post your code.

Version history
Last update:
‎08-02-2021 10:30 AM
Updated by:
Contributors