<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Okedele Favour</title>
    <description>The latest articles on DEV Community by Okedele Favour (@favouroked).</description>
    <link>https://dev.to/favouroked</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F100380%2F1d5e7dc2-a4db-4335-ad60-41e3219b9b8b.png</url>
      <title>DEV Community: Okedele Favour</title>
      <link>https://dev.to/favouroked</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/favouroked"/>
    <language>en</language>
    <item>
      <title>Sending app notifications over Whatsapp using Twilio</title>
      <dc:creator>Okedele Favour</dc:creator>
      <pubDate>Thu, 22 Oct 2020 00:46:17 +0000</pubDate>
      <link>https://dev.to/favouroked/sending-app-notifications-over-whatsapp-using-twilio-dm6</link>
      <guid>https://dev.to/favouroked/sending-app-notifications-over-whatsapp-using-twilio-dm6</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this article, we are going to build an application that sends appointment notifications to users over whatsapp via twilio. According to statistics, 90.4% of the young generation are active on social media so this approach of sending notifications will be more effective in setting remiders without being intrusive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User Journey&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An API request is sent to the application containing the appointment details (title, description, phone and time). Once the appointment time is reached, the user gets notified on whatsapp about the appointment. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_84F97AD51A16E7A0A60C1D1E89892F9FB45EDB6B9586E29DC715D4180B666E28_1582478259744_Screen%2BShot%2B2020-02-23%2Bat%2B6.10.33%2BPM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_84F97AD51A16E7A0A60C1D1E89892F9FB45EDB6B9586E29DC715D4180B666E28_1582478259744_Screen%2BShot%2B2020-02-23%2Bat%2B6.10.33%2BPM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tutorial Requirements&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To follow this tutorial, you are expected to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have sufficient understanding of Python and Flask&lt;/li&gt;
&lt;li&gt;Have Python 3 installed on your machine&lt;/li&gt;
&lt;li&gt;Have MongoDB installed on your machine&lt;/li&gt;
&lt;li&gt;Have a smartphone with an active phone number and Whatsapp installed&lt;/li&gt;
&lt;li&gt;Have a Twilio account. You can create a free account if you are new to Twilio.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Setting up the Twilio Whatsapp Sandbox&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First of all, you need to create a Twilio account if you don’t have one or sign in if you have one. You will need to activate Twilio Whatsapp sandbox since we are going to be working with Whatsapp. The sandbox allows you to immediately test with Whatsapp using a phone number without waiting for you Twilio number to be approved by Whatsapp. You can &lt;a href="https://www.twilio.com/whatsapp/request-access" rel="noopener noreferrer"&gt;request production access for your Twilio phone number here&lt;/a&gt;. &lt;br&gt;
To connect to the sandbox, log into your &lt;a href="https://www.twilio.com/console" rel="noopener noreferrer"&gt;Twilio Console&lt;/a&gt; and select &lt;a href="https://www.twilio.com/console/sms/dashboard" rel="noopener noreferrer"&gt;Programmable SMS&lt;/a&gt; on the side menu. After that, click on &lt;a href="https://www.twilio.com/console/sms/whatsapp/learn" rel="noopener noreferrer"&gt;Whatsapp&lt;/a&gt;. This will open the Whatsapp sandbox page which contains your sandbox phone number and a join code. Send the join code starting with the word “join” to your sandbox phone number to enable the Whatsapp sandbox for your phone. You will then receive a message confirming the activation of the phone number to use the sandbox. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We are going to be using the Flask framework to build the application and MongoDB as our preferred database. &lt;/p&gt;

&lt;p&gt;Creating the Application Directory and Virtual Environment&lt;br&gt;
Run the following commands in your terminal to create the project folder and virtual environment for this project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create project directory named &lt;code&gt;whatsapp_appointments&lt;/code&gt;
mkdir whatsapp_appointments&lt;/li&gt;
&lt;li&gt;Enter into the project directory
cd whatsapp_appointments&lt;/li&gt;
&lt;li&gt;Create a virtual environment for the project
python3 -m venv venv&lt;/li&gt;
&lt;li&gt;Activate the virtual environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For macOS and Linux users:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For windows users:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;venv\Scripts\activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The virtual environment helps create an isolated environment separate from the global python environment to run the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structuring the Project&lt;/strong&gt;&lt;br&gt;
In the application directory, run the commands below to set up the folder structure:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir controllers jobs services utils 
touch app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The above commands create 4 folders and a file called &lt;code&gt;app.py&lt;/code&gt;  in the application directory.&lt;br&gt;
Your application directory should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_84F97AD51A16E7A0A60C1D1E89892F9FB45EDB6B9586E29DC715D4180B666E28_1582463434802_Screen%2BShot%2B2020-02-23%2Bat%2B2.10.14%2BPM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_84F97AD51A16E7A0A60C1D1E89892F9FB45EDB6B9586E29DC715D4180B666E28_1582463434802_Screen%2BShot%2B2020-02-23%2Bat%2B2.10.14%2BPM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installing Project Dependencies&lt;/strong&gt;&lt;br&gt;
Finally, let’s install all the dependencies used in this project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flask: This library will be used for running our web server.&lt;/li&gt;
&lt;li&gt;Pymongo: This library will be used to interface with the MongoDB database on your computer.&lt;/li&gt;
&lt;li&gt;Twilio Python Helper Library: This library will be used to send Whatsapp messages to users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run the command below to install these dependencies:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install Flask pymongo twilio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Building appointment reminders&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this section, we are going to write the logic for appointment reminders. We are going to use the &lt;code&gt;mutiprocessing&lt;/code&gt; module in python to build the reminders logic. The main reason we are using processes over threads in this section is because in python, there is no safe way to terminate threads and we will need to edit and cancel appointments 🤷🏿‍♂️.&lt;br&gt;
First of all, create 2 files called &lt;code&gt;__init__.py&lt;/code&gt; and &lt;code&gt;appointments.py&lt;/code&gt; in the &lt;code&gt;jobs/&lt;/code&gt; directory. Also, create a file called &lt;code&gt;whatsapp.py&lt;/code&gt; in the &lt;code&gt;utils/&lt;/code&gt; directory. &lt;br&gt;
Copy the code below into the &lt;code&gt;whatsapp.py&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
from twilio.rest import Client

ACCOUNT_SID = os.getenv('ACCOUNT_SID')
AUTH_TOKEN = os.getenv('AUTH_TOKEN')
TWILIO_NUMBER = os.getenv('TWILIO_NUMBER')
client = Client(ACCOUNT_SID, AUTH_TOKEN)


def format_message(appointment):
    message = "You have an appointment\n"
    message += f"Title: {appointment['title']}\n"
    message += f"Description: {appointment['description']}\n"
    message += f"Time: {appointment['time']}"
    return message


def send_message(appointment):
    message = client.messages.create(
        from_=f'whatsapp:{TWILIO_NUMBER}',
        body=format_message(appointment),
        to=f"whatsapp:{appointment['phone']}"
    )
    return message.sid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the above code, we have the logic for sending whatsapp messages. Note that the &lt;code&gt;ACCOUNT_SID&lt;/code&gt;, &lt;code&gt;AUTH_TOKEN&lt;/code&gt;, &lt;code&gt;TWILIO_NUMBER&lt;/code&gt; are set as environment variables and we use &lt;code&gt;os.getenv&lt;/code&gt; to access them. To send whatsapp messages, you must prefix the phone numbers with &lt;code&gt;whatsapp:&lt;/code&gt; else it will be sent as sms. The &lt;code&gt;format_message&lt;/code&gt; function accepts an appointment object and represents it in the following format:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You have an appointment
Title: &amp;lt;Appointment Title&amp;gt;
Description: &amp;lt;Appointment Description&amp;gt;
Time: &amp;lt;Appointment Time&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, we are going to write the reminder’s logic. Open the file &lt;code&gt;jobs/appointments.py&lt;/code&gt; and copy the following code inside:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import time
from datetime import datetime

from utils.whatsapp import send_message


def start_appointment_job(appointment):
    print(f"=====&amp;gt; Scheduled Appointment {appointment['_id']} for :&amp;gt; {appointment['time']}")
    if datetime.now() &amp;gt; appointment['time']:
        return
    diff = appointment['time'] - datetime.now()
    time.sleep(diff.seconds)
    send_message(appointment)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;start_appointment_job&lt;/code&gt; function first checks if the appointment is in the past and if so, it returns. We use the &lt;code&gt;time.sleep&lt;/code&gt; function to tell the application to pause execution for the number of seconds it takes for the appointment’s time to be reached. Once it finishes sleeping, it calls the &lt;code&gt;send_message&lt;/code&gt; function written earlier to notify the user of the appointment via whatsapp.&lt;br&gt;
Next, we are going to write the service functions for creating, getting, updating and deleting appointments. Create a file called &lt;code&gt;appointments.py&lt;/code&gt; in the &lt;code&gt;services/&lt;/code&gt; folder and copy the following inside:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from bson.objectid import ObjectId
from pymongo import MongoClient

client = MongoClient('mongodb://localhost/')

appointments = client['twilio']['appointments']


def create_appointment(data):
    appointments.insert_one(data)


def get_appointment(appointment_id):
    result = appointments.find_one({'_id': ObjectId(appointment_id)})
    return dict(result) if result else None


def get_appointments(conditions):
    if '_id' in conditions:
        conditions['_id'] = ObjectId(conditions['_id'])
    results = appointments.find(conditions)
    data = []
    for result in results:
        data.append(dict(result))
    return data


def update_appointment(appointment_id, data):
    appointment = appointments.find_one_and_update({'_id': ObjectId(appointment_id)}, {'$set': data},
                                                   return_document=True)
    return appointment


def delete_appointment(appointment_id):
    appointments.find_one_and_delete({'_id': ObjectId(appointment_id)})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the above code, we write functions to perform CRUD operations on the &lt;code&gt;appointments&lt;/code&gt; collections in mongodb. We use the &lt;code&gt;pymongo&lt;/code&gt; library to connect to our local mongodb instance. Note that the &lt;code&gt;_id&lt;/code&gt; field cannot be sent as a string else no data will be returned, we have to cast it to an &lt;code&gt;ObjectId&lt;/code&gt; instance which mongodb uses. &lt;/p&gt;

&lt;p&gt;Finally, we are going to write the logic for creating processes for an appointment and modifying them based on the change in appointments. Open &lt;code&gt;jobs/__init__.py&lt;/code&gt; and copy the following code inside:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from multiprocessing import Process
from datetime import datetime

from .appointments import start_appointment_job

from services.appointments import get_appointments

WORKERS = {}


def terminate_worker(worker):
    try:
        worker.terminate()
        worker.join()
        worker.close()
    except Exception as err:
        print('====&amp;gt; Error occurred terminating process', err)


def schedule_appointment(appointment):
    appointment_id = str(appointment['_id'])
    worker = Process(target=start_appointment_job, args=(appointment,))
    worker.start()
    WORKERS[appointment_id] = worker


def update_scheduled_appointment(appointment_id, updated_appt):
    worker = WORKERS[appointment_id]
    terminate_worker(worker)
    new_worker = Process(target=start_appointment_job, args=(updated_appt,))
    new_worker.start()
    WORKERS[appointment_id] = new_worker


def delete_scheduled_appointment(appointment_id):
    worker = WORKERS[appointment_id]
    terminate_worker(worker)
    del WORKERS[appointment_id]

def init_workers():
    print('=====&amp;gt; Initializing workers')
    appts = get_appointments({})
    for appt in appts:
        if datetime.now() &amp;gt; appt['time']:
            continue
        schedule_appointment(appt)

def close_workers():
    for appointment_id, worker in WORKERS.items():
        terminate_worker(worker)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the above code, when we want to schedule an appointment, we create a &lt;code&gt;Process&lt;/code&gt; targeting the &lt;code&gt;start_appointment_job&lt;/code&gt; function with the &lt;code&gt;appointment&lt;/code&gt; object as argument. Note that we have a dictionary objection named &lt;code&gt;WORKERS&lt;/code&gt; where we store the process object with the &lt;code&gt;appointment_id&lt;/code&gt; as key, this is what we use to get the &lt;code&gt;Process&lt;/code&gt; object in case of an update. &lt;br&gt;
To update an appointment, we get the process object via the &lt;code&gt;appointment_id&lt;/code&gt; and terminate it. We then create a new &lt;code&gt;Process&lt;/code&gt; object with the updated details and update the reference in the &lt;code&gt;WORKERS&lt;/code&gt; dictionary. &lt;br&gt;
To delete an appointment, we get the &lt;code&gt;Process&lt;/code&gt; object and terminate it. We then delete the key from the &lt;code&gt;WORKERS&lt;/code&gt; dictionary so that it doesn’t have a reference there again. &lt;br&gt;
Note that, to terminate a &lt;code&gt;Process&lt;/code&gt; object, we first call the &lt;code&gt;terminate&lt;/code&gt; function on it when sends a &lt;code&gt;SIGTERM&lt;/code&gt; signal to the process. The &lt;code&gt;join&lt;/code&gt; function waits for the &lt;code&gt;Process&lt;/code&gt; object to terminate and the &lt;code&gt;close&lt;/code&gt; function tells the process to release the resources that it holds. &lt;br&gt;
Lets say our application restarts, we want to be able to recreate our Process objects and that’s where the &lt;code&gt;init_workers&lt;/code&gt; function comes into play. It gets all the appointments and calls the &lt;code&gt;schedule_appointment&lt;/code&gt; function on appointments that are still pending.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building the API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this section, we are going to be building the api routes which we are going use to create, update, get and delete appointments. First of all, we have to create utility functions for handling both our requests and our api responses. Create 2 files called &lt;code&gt;request.py&lt;/code&gt; and &lt;code&gt;response.py&lt;/code&gt; in the &lt;code&gt;utils/&lt;/code&gt; folder. Copy the code below into the &lt;code&gt;request.py&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from datetime import datetime


def validate_body(data, required_fields):
    for field in required_fields:
        if field not in data:
            return False, field
    return True, None


def parse_appointment(body):
    try:
        if body.get('time'):
            time_obj = datetime.strptime(body['time'], '%Y-%m-%d %H:%M')
            body['time'] = time_obj
        return True, None
    except Exception as err:
        return False, str(err)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;From the above code, we can see that the &lt;code&gt;validate_body&lt;/code&gt; function takes in a data dictionary and a list of required fields. It checks the required fields against the data dictionary and returns a status of False if a field is absent. The &lt;code&gt;parse_appointment&lt;/code&gt; function is used to convert the &lt;code&gt;time&lt;/code&gt; field in an appointment object into a datetime object by the use of &lt;code&gt;datetime.strptime&lt;/code&gt; function which accepts the time string and the time format.  The function returns false if it’s not able to convert the string into a datetime object. &lt;br&gt;
Next, copy the code below into the &lt;code&gt;response.py&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import time

from flask import jsonify


def stringify_objectid(data):
    str_data = json.dumps(data, default=str)
    return json.loads(str_data)


def response(status, message, data, status_code=200):
    """
    :param status : Boolean Status of the request
    :param status_code: Status Code of response
    :param message : String message to be sent out as description of the message
    :param data : dictionary representing extra data
    """
    if data:
        data = stringify_objectid(data)
    res = {'status': status, 'message': message, 'data': data, 'timestamp': timestamp()}
    return jsonify(res), status_code


def error_response(message, status='error', code='R0', status_code=400):
    res = {'message': message, 'status': status, 'code': code}
    return jsonify(res), status_code


def timestamp():
    """
    Helper Function to generate the current time
    """
    return time.time()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the above code, we have helper functions to return api responses and error messages. The &lt;code&gt;stringify_objectid&lt;/code&gt; function is used to convert the mongodb ObjectId object into a string.&lt;/p&gt;

&lt;p&gt;Next, we are going to write our controller functions. Create a file called &lt;code&gt;appointments.py&lt;/code&gt; in the &lt;code&gt;controllers/&lt;/code&gt; folder and copy the following inside:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from flask import Blueprint, request

from services import appointments as apt_service
from utils.request import validate_body, parse_appointment
from utils.response import response, error_response
from utils.whatsapp import send_sms

import jobs

appointments = Blueprint('appointments', __name__)


@appointments.route('/', methods=['POST'])
def create():
    body = request.get_json()
    status, missing_field = validate_body(body, ['title', 'phone', 'description', 'time'])
    if not status:
        return error_response(f'{missing_field} is required')
    status, error = parse_appointment(body)
    if not status:
        return error_response(error)
    try:
        apt_service.create_appointment(body)
        jobs.schedule_appointment(body)
        return response(True, 'Appointment created successfully', body)
    except Exception as err:
        print('=====&amp;gt; Error', err)
        return error_response(str(err))


@appointments.route('/')
def view():
    conditions = dict(request.args)
    try:
        data = apt_service.get_appointments(conditions)
        return response(True, 'Appointments', data)
    except Exception as err:
        print('=====&amp;gt; Error', err)
        return error_response(str(err))


@appointments.route('/&amp;lt;appointment_id&amp;gt;')
def view_one(appointment_id):
    try:
        data = apt_service.get_appointment(appointment_id)
        return response(True, 'Appointment', data)
    except Exception as err:
        print('=====&amp;gt; Error', err)
        return error_response(str(err))

@appointments.route('/&amp;lt;appointment_id&amp;gt;', methods=['PUT'])
def update(appointment_id):
    body = request.get_json()
    try:
        parse_appointment(body)
        appointment = apt_service.update_appointment(appointment_id, body)
        jobs.update_scheduled_appointment(appointment_id, appointment)
        return response(True, 'Updated Appointment', appointment)
    except Exception as err:
        print('=====&amp;gt; Error', err)
        return error_response(str(err))


@appointments.route('/&amp;lt;appointment_id&amp;gt;', methods=['DELETE'])
def delete(appointment_id):
    try:
        apt_service.delete_appointment(appointment_id)
        jobs.delete_scheduled_appointment(appointment_id)
        return response(True, 'Appointment deleted successfully', None)
    except Exception as err:
        print('====&amp;gt; Error', err)
        return error_response(str(err))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the above code, we created a new flask blueprint and functions to create appointments, get appointments, update appointments and delete appointments. As you can see from the above code, we call the schedule appointment function to start the scheduling process when an appointment is created. The update function calls the &lt;code&gt;update_scheduled_appointment&lt;/code&gt; function to reshedule the appointment and the delete function calls the &lt;code&gt;delete_scheduled_appointment&lt;/code&gt; function to delete the scheduling process. &lt;/p&gt;

&lt;p&gt;Finally, open the &lt;code&gt;app.py&lt;/code&gt; file and copy the following inside:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import atexit

from flask import Flask

from controllers.appointments import appointments
from jobs import close_workers, init_workers


def create_app():
    init_workers()
    atexit.register(close_workers)
    return Flask(__name__)


app = create_app()

app.register_blueprint(appointments, url_prefix='/api/appointments')

if __name__ == '__main__':
    app.run()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the above code, the use the &lt;code&gt;register_blueprint&lt;/code&gt; function to make sure that urls starting  with &lt;code&gt;/api/appointments&lt;/code&gt; are routed through the appointments blueprint and our controller functions are executed for matching urls. We use the &lt;code&gt;atexit&lt;/code&gt; module to perform a task when the application is closing. In the &lt;code&gt;create_app&lt;/code&gt; function, we are telling the system to first schedule pending appointments and then close all running processes when the application is closing. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing the Application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To run the application, open your terminal and execute &lt;code&gt;python app.py&lt;/code&gt; in your application’s directory. You should see something like the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_84F97AD51A16E7A0A60C1D1E89892F9FB45EDB6B9586E29DC715D4180B666E28_1582477244943_Screen%2BShot%2B2020-02-23%2Bat%2B6.00.35%2BPM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_84F97AD51A16E7A0A60C1D1E89892F9FB45EDB6B9586E29DC715D4180B666E28_1582477244943_Screen%2BShot%2B2020-02-23%2Bat%2B6.00.35%2BPM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you can send a POST request to &lt;code&gt;http://127.0.0.1:5000/api/appointments&lt;/code&gt; to create an appointment and you will get a whatsapp message when the appointment time has been reached. Below is a sample request body:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
        "title": "Data Structures &amp;amp; Algorithms",
        "phone": "+2349094739283",
        "description": "Watch Youtube videos on Data Structures and Algorithms",
        "time":"2020-02-23 18:10"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You should receive a whatsapp message when the appointment time is reached as shown in the image below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_84F97AD51A16E7A0A60C1D1E89892F9FB45EDB6B9586E29DC715D4180B666E28_1582477884158_Screen%2BShot%2B2020-02-23%2Bat%2B6.10.33%2BPM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpaper-attachments.dropbox.com%2Fs_84F97AD51A16E7A0A60C1D1E89892F9FB45EDB6B9586E29DC715D4180B666E28_1582477884158_Screen%2BShot%2B2020-02-23%2Bat%2B6.10.33%2BPM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;We have now come to the end of the tutorial. We have successfully built an application that sends appointment notification over Whatsapp using Twilio, Python and Flask. &lt;br&gt;
You will find the source code for this tutorial &lt;a href="https://github.com/Favouroked/twilio_appointments" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>twilio</category>
      <category>python</category>
      <category>whatsapp</category>
    </item>
  </channel>
</rss>
