<jupyter_start><jupyter_text>Administration: Deploy automatic notifications> * ✏️ Needs Configuration* 🗄️ Administration* 🔔 NotificationsTo optimize your notebook's utility, consider configuring it to alert someone of a notable event. For example, a publisher could be notified that their web map contains a broken layer URL, or an admin could be notified when certain users haven't logged on in over a month. Notifications may arrive as an email or text message - or they could take the form of a tweet or a Slack post. There are many ways to use native Python libraries to send notifications through external services. This notebook will walk you through the technical details of how to configure these external services for your hosted notebooks and provide you the sample code needed to control these services.  SecurityBefore writing code that connects to any external service, it is vital to keep security in mind. Notebooks are commonly shared and code is copied and pasted, sometimes leading to passwords and private keys landing in the wrong hands.**Passwords and private keys should NEVER be stored in plaintext in a notebook**. There are many paradigms to securely store passwords and private keys. We will be storing all 'secrets' in a [private CSV item](https://www.esri.com/arcgis-blog/products/arcgis-online/sharing-collaboration/managing-security-and-findability-of-items-with-the-arcgis-sharing-model/) that is only accessible by you and your GIS administrator. > __Note__: that although this is more secure than storing your passwords as a plain string in a notebook, this is __NOT__ a totally secure solution—Portal items aren't encrypted in the underlying server where they are stored. Consider storing your secrets in an encrypted files in `/arcgis/home/`.Run the below cells to import necessary libraries, connect to our Portal, and create a new private blank CSV item.<jupyter_code># First, import some libraries we'll need, and connect to our GIS
import csv
import os
import smtplib
import requests
import logging
log = logging.getLogger()

from arcgis.gis import GIS

gis = GIS("home")
SECRET_CSV_ITEM_ID = '180641b2dfc549a5aa214dfbf68ec146'
def get_secrets(gis=gis,
                secret_csv_item_id = SECRET_CSV_ITEM_ID):
    """Returns the secrets.csv file as a dict of secret_key : secret_value"""
    item = gis.content.get(secret_csv_item_id)
    with open(item.download(), 'r') as local_item_file:
        reader = csv.DictReader(local_item_file)
        return { rows['secret_key'] : rows['secret_value'] for rows in reader}
secrets = get_secrets()<jupyter_output><empty_output><jupyter_text>Email NotificationsEmails might be the gold standard for notifications. Emails are simple and sortable, and everyone can receive them. Emails can only be sent by an external service, so Python must know how to connect to that service. The most common way to do this is to connect to an SMTP Server. SMTP ServerThe builtin [`smtplib`](https://docs.python.org/3/library/smtplib.htmlmodule-smtplib) library makes it easy to connect an external email SMTP server, whether that's a server you manage or a commercial offering (e.g. Gmail).The below function provides an outline for sending emails via an external SMTP server. Make sure to update the string values of `smtp_server_url`, `smt_server_port`, `sender`, and `username` to reflect your own SMTP server. Add your SMTP server password to your secrets.csv item with the key `'smtp_email_password'`, so you can access it in a secure way.<jupyter_code>def send_email_smtp(recipients, message,
                    subject="Message from your Notebook"):
    """Sends the `message` string to all of the emails in the 
    `recipients` list using the configured SMTP email server. 
    """
    try:
        # Set up server and credential variables
        smtp_server_url = "smtp.gmail.com"
        smtp_server_port = 587
        sender = "<EMAIL>"
        username = "arcgispyapibot"
        password = secrets["smtp_email_password"]

        # Instantiate our server, configure the necessary security
        server = smtplib.SMTP(smtp_server_url, smtp_server_port)
        server.ehlo()
        server.starttls() # Needed if TLS is required w/ SMTP server
        server.login(username, password)
    except Exception as e:
        log.warning("Error setting up SMTP server, couldn't send " +
                    f"message to {recipients}")
        raise e

    # For each recipient, construct the message and attempt to send
    did_succeed = True
    for recipient in recipients:
        try:
            message_body = '\r\n'.join(['To: {}'.format(recipient),
                                        'From: {}'.format(sender),
                                        'Subject: {}'.format(subject),
                                        '',
                                        '{}'.format(message)])
            message_body = message_body.encode("utf-8")
            server.sendmail(sender, [recipient], message_body)
            print(f"SMTP server returned success for sending email "\
                  f"to {recipient}")
        except Exception as e:
            log.warning(f"Failed sending message to {recipient}")
            log.warning(e)
            did_succeed = False
    
    # Cleanup and return
    server.quit()
    return did_succeed<jupyter_output><empty_output><jupyter_text>Now that you have updated the above function with your SMTP server information, run the below cell to send a test email. The function should return `True`, print out no warnings, and send an email to your test recipient! You can use this function anywhere in your code to send out an email.<jupyter_code>send_email_smtp(recipients = ['<EMAIL>'],
                message = "Hello World!")<jupyter_output>SMTP server returned success for sending email to <EMAIL><jupyter_text>Text Messages via EmailMany major cellular providers offer a mechanism to send text messages (SMS or MMS) via email. You just need to know what the phone number is, and what cellular provider that phone number is under. For example, if phone number _555-123-4567_ has AT&T as their cellular provider, the below code cell can be run to send that number a text message.<jupyter_code>send_email_smtp(recipients = ['<EMAIL>'],
                message = "Hello World!")<jupyter_output><empty_output><jupyter_text>Check your area's cellular providers to see if this functionality is supported. Standard messaging and data rates would apply. The below table outlines some of the common cellular providers' email formats—it is not a complete or authoritative list.Cellular Provider | SMS email | MMS email |---|---|---|AT&T | <EMAIL> | <EMAIL> |T-Mobile | <EMAIL> | <EMAIL> |Verizon |<EMAIL> | <EMAIL> |Sprint | <EMAIL> | <EMAIL> |U.S. Cellular | <EMAIL> | <EMAIL> |Virgin Mobile | <EMAIL> | <EMAIL> |Boost Mobile | <EMAIL> | <EMAIL> | Cricket | <EMAIL> | <EMAIL> |Google Fi (Project Fi) | <EMAIL> | <EMAIL> |Tracfone | - | <EMAIL> |Metro PCS | <EMAIL> | <EMAIL> |Republic Wireless | <EMAIL> | - |Ting | <EMAIL> | - |Consumer Cellular | <EMAIL> | - |C-Spire | <EMAIL> | - |Page Plus | <EMAIL> | - | Source: https://20somethingfinance.com/how-to-send-text-messages-sms-via-email-for-free/  Zapier via Tweet A Zapier "Zap" has been set up which will post a tweet when the `zapier_tweet_webhook_url` endpoint is hit with the correct `POST` HTTP request.<jupyter_code>def tweet_via_zapier(message):
    """Sends the `message` string as a tweet via zapier."""
    # Do the POST request that sends the tweet
    secrets = get_secrets()
    response = requests.post(secrets['zapier_tweet_webhook_url'],
                             data={"message" : message})
    
    # Check that the response looks OK; warn the user if it doesn't
    if response.ok:
        return True
    else:
        log.warning("Zapier tweet POST returned a bad response:")
        log.warning(response.text)
        return False<jupyter_output><empty_output><jupyter_text>Now, call this function and verify that your Twitter account posted the message! You can use this function anywhere in your code to send out a tweet.<jupyter_code>tweet_via_zapier("Hello World!")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08051157125456761, 'fraction_numerical': 0.00438489646772229, 'mean_word_length': 3.53646408839779, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 56, 'https://': 3, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '299900', 'license': 'Apache-2.0', 'n_tokens_mistral': 2347, 'n_tokens_neox': 2227, 'n_words': 937, 'path': 'talks/DevSummit2019/automation-webgis/Deploy_Notifications.ipynb', 'repo_name': 'test-v1/arcgis-python-api', 'size': '8644'}