COVID-19 tracking via SMS with Twilio and Python

March 25, 2020
Written by
Sam Agnew
Twilion

Copy of Language template - PYTHON2.png

Around the world, the Coronavirus Pandemic is affecting people and their communities. It might impact different areas in varying ways, but the virus causing the COVID-19 disease has reached a level of global pervasiveness. The situation is constantly evolving so it can be hard to keep track of.

If you are in the United States, there is a COVID Tracking Project which provides up-to-date statistics for the nation as a whole and for each individual State on the number of tests administered, positive cases, and the death toll. The project also provides this data in the form of an API.
You can text the phone number +1 (864) 77-STATS which is +1 (864) 777-8287 with a particular US State abbreviation to get the current statistics for that State, or any other message to get the nation-wide data.

Let's walk through how to build a text-message-based COVID-19 tracker in Python using this API and Twilio Programmable Messaging.

Setting up your environment

Make sure to have your Python environment set up before we get started. Getting everything working correctly, especially with respect to virtual environments is important for isolating your dependencies if you have multiple projects running on the same machine.

You can also run through this guide to make sure you’re good to go before moving on.

Installing dependencies

Now that your environment is set up, you’re going to need to install some third party libraries. We’re going to use:

Navigate to the directory where you want this code to live and run the following command in your terminal with your virtual environment activated to install these dependencies:

pip install twilio==6.37.0 flask==1.1.1

We don't need to explicitly install Requests because that is included with the Twilio Python library.

Retrieving data from the COVID Tracking API

Let’s start by writing code to interact with the COVID Tracking API, which is just a series of URLs that return JSON data. All of these API endpoints are available in CSV format as well. There are different API endpoints for US States and Counties, but let's start with nation-wide data for now.

Open a Python shell and enter the following code to make a request to this API endpoint and print out the results:

import requests
resp = requests.get('https://covidtracking.com/api/us').json()
print(resp)

This should show you the JSON data retrieved from the API. With this, we can write a function that will return an informative string that uses all of this data. Create a file called app.py and add the following code to it:

def get_stats_message():
    resp = requests.get('https://covidtracking.com/api/us').json()
    data = resp[0]
    return ('In the US so far, {total} tests have been given with ' +
           '{positive} confirmed positive cases.\n\n{hospitalized}' +
           ' have been hospitalized so far with {death} deaths.') \
           .format(**data)

This function can be called later when you receive a text to your Twilio number, and then respond with this informative string as another text message. To test this out, open your Python shell and run from app import get_stats_message and then execute the function.

We can expand this code a bit by adding the option for getting State-specific data. Let's say if a user texts a valid abbreviated US State, we will give them the data for that State, and otherwise we will respond with the nation-wide data. Change the code in app.py to the following:

import requests

states = ["AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DC", "DE", "FL", "GA",
          "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD",
          "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ",
          "NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC",
          "SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY"]


def get_stats_message(message):
    if message in states:
        resp = requests.get('https://covidtracking.com/api/states').json()

        # Get the data for the given State.
        for state_data in resp:
            if message == state_data['state']:
                data = state_data
        return ('In {state} so far, {total} tests have been given with ' +
                '{positive} confirmed positive cases.\n\n{hospitalized}' +
                ' have been hospitalized so far with {death} deaths.') \
                .format(**data)

    # Return nation-wide data if no valid State was given.
    resp = requests.get('https://covidtracking.com/api/us').json()
    data = resp[0]
    return ('In the US so far, {total} tests have been given with ' +
           '{positive} confirmed positive cases.\n\n{hospitalized}' +
           ' have been hospitalized so far with {death} deaths.') \
           .format(**data)

Run it again by importing the function in your Python shell and calling it with a string representing a US State. With this, we're ready to move on to using this code in a Twilio application.

Setting up your Twilio account

Before being able to respond to messages, you’ll need a Twilio phone number. You can buy a phone number here.

Your Flask app will need to be visible from the Internet in order for Twilio to send requests to it. We will use ngrok for this, which you’ll need to install if you don’t have it. In your terminal run the following command:

ngrok http 5000

Ngrok forwarding url

This provides us with a publicly accessible URL to the Flask app. Configure your phone number as seen in this image so that when a text message is received, Twilio will send a POST request to the /sms route on the app we are going to build, which will sit behind your Ngrok URL:

Configuring your phone number

It's time to actually build the app.

Building the Flask app

Now that you have a Twilio number and are able to grab the data you need from the COVID Tracking API, you want to allow users to text a phone number to view this data.

Let’s create our Flask app. Open app.py and add onto the code we wrote earlier with code for the Flask app:

import requests
from flask import Flask, request
from twilio.twiml.messaging_response import MessagingResponse, Message


app = Flask(__name__)
states = ["AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DC", "DE", "FL", "GA",
          "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD",
          "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ",
          "NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC",
          "SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY"]


# Take the text message sent by the user and return a string with the results.
def get_stats_message(message):
    if message in states:
        resp = requests.get('https://covidtracking.com/api/states').json()

        # Get the data for the given State.
        for state_data in resp:
            # Figure out which State we're looking at.
            if message == state_data['state']:
                data = state_data
        return ('In {state} so far, {total} tests have been given with ' +
                '{positive} confirmed positive cases.\n\n{hospitalized}' +
                ' have been hospitalized so far with {death} deaths.') \
                .format(**data)

    # Return nation-wide data if no valid State was given.
    resp = requests.get('https://covidtracking.com/api/us').json()
    data = resp[0]
    return ('In the US so far, {total} tests have been given with ' +
           '{positive} confirmed positive cases.\n\n{hospitalized}' +
           ' have been hospitalized so far with {death} deaths.') \
           .format(**data)


@app.route('/sms', methods=['POST'])
def inbound_sms():
    # Grab the text from the received message.
    message_body = request.form['Body']

    # Generate a TwiML Response object with the message we want to send.
    twiml_resp = MessagingResponse()
    twiml_resp.message(get_stats_message(message_body))
    return str(twiml_resp)

We only need one route on this app: /sms to handle incoming text messages.

Run your code with the following terminal command:

flask run

Text your Twilio number to see the results.

The number in action

Staying up to date and maintaining social distancing

During this pandemic, it's important to take the proper precautions to protect yourself and your community. If you're able to, try to stay home as much as you can, or at least away from crowds to continue social distancing and "flatten the curve". Wash your hands regularly for 20 seconds and try not to touch your face. Reach out to your friends and family to see how they are doing. And remember that while it's important to stay up to date with the situation as we're doing with this project, don't forget to also take care of yourself and unplug a little bit if possible.

If you're looking for something fun to do in the meantime, try playing TwilioQuest, our documentation video game. It has a really awesome Python mission! You can also work on other fun Python projects like generating Nintendo music through phone calls.

Feel free to reach out if you have any questions or comments or just want to show off the cool stuff you’ve built.