Workshop 27. Web Deployment

Web deployment is hosting your web application on a server that is accessible through the internet. So that anyone who has a link to your application's server can use it.

Requirements and steps

  1. The app itself that can work locally (provided here)
  2. A GitHub repository with the app itself.
  3. Additional configuration files:
    1. requirements.txt so the hosting server can understand which Python packages to install.
    2. Procfile containing web: gunicorn app:app. This file tells the server which command to run to start your app.
  4. An account on a hosting service. We will use Heroku - https://heroku.com

Details

Repository

Files

Before we actually start deployment process we need to perform to prepare all the necessary files:

  1. We need to create file requirements.txt. We can do that using command pip freeze executed in VIRTUAL ENV of application
  2. The application will not be executed by the debug flask server, instead it will be executed by gunicorn. So we need to provide file named Procfile with the following content web: gunicorn app:app

Example content of requirements.txt:

In [ ]:
certifi==2021.10.8
charset-normalizer==2.0.8
click==8.0.3
cycler==0.11.0
Flask==2.0.2
fonttools==4.28.4
idna==3.3
itsdangerous==2.0.1
Jinja2==3.0.3
kiwisolver==1.3.2
MarkupSafe==2.0.1
matplotlib==3.5.1
numpy==1.21.4
packaging==21.3
pandas==1.3.5
Pillow==8.4.0
pyparsing==3.0.6
python-dateutil==2.8.2
pytz==2021.3
requests==2.26.0
scipy==1.7.3
seaborn==0.11.2
six==1.16.0
urllib3==1.26.7
Werkzeug==2.0.2

GitHub

To deploy our application, let's first create git repository and push changes to the github.

Instructions

If you don't have git

  1. Make a GitHub account
  2. Create a new repository
  3. Initialize it with .gitignore with template "Python"
  4. Add your files through github UI

Alternatively, if you don't have a working repository, you may take files from here - https://github.com/dsba-z/dsba2021-flask-example

Remove app.run() from the python file

If you have git

  1. Create repository on github
  2. Clone it to some folder using command git clone https://github.com/USER/REPOSITORY or by using context menu (right mouse button).
  3. Add files to the folder
  4. Commit changes to the repository
  5. Click push and select remote branch (should be only one option in git gui)

Hosting

To deploy our application we need a working server that will execute our flask application. Examples:

  1. Amazon EC2
  2. Google Cloud
  3. Yandex Cloud

Usually it requires some resources.

Also we can find some free deployment options, e.g. Heroku. https://heroku.com

image.png

After registration process we can go to our dashboard and create new app by corresponding menu

We would need to provide some unique name to our application. Name will be part of our application url. In the next screen we need to select deployment method. Let us select GitHUB method.

image.png

After verification process we need to provide repository name

image.png

If you want heroku to automatically rebuild your app you may set automatic deploys.

image.png

If everything is fine, then heroku should build your app and give deployment url, e.g. https://example20201213.herokuapp.com/

Next steps

Now that you have a web application, you may add any features to your original app, update the GitHub page with them, and publish them to the web.

Right now your application can send raw data or build some charts. You may change it to your data instead of Titanic, you may add more pages, you may do anything.

But a webapp is not only a website.

We can also make a telegram bot that will process our commands and possibly return some information about the data

Telegram bot example

If you want to make a Telegram bot from your app you need the following:

  1. Successfully deployed Heroku app.
  2. Package python-telegram-bot.
  3. Token from the Telegram bot @botfather.
  4. Code modifications given at the end of this notebook.

It is possible to make a Telegram bot without creating a web application, but we will focus on the hosted version.

Token

Let's first go the telegram and create bot using botfather. It is Telegram utility for creating telegram bots,

It will return bot TOKEN and url for our bot. Token shoulnd't be published anywhere - it is secret key to our telegram bot.

Modifying code

Now we need to add telegram bot functionality to the application.

Place this code in app.py. The module telegram is available in the package python-telegram-bot.

In [ ]:
import telegram

TOKEN="TOKEN"
bot = telegram.Bot(token=TOKEN)
URL = HEROKU URL
In [ ]:
def get_response (text):
    return text

@app.route('/{}'.format(TOKEN), methods=['POST'])
def respond():
    # retrieve the message in JSON and then transform it to Telegram object
    update = telegram.Update.de_json(request.get_json(force=True), bot)
    # get the chat_id to be able to respond to the same user
    chat_id = update.message.chat.id
    # get the message id to be able to reply to this specific message
    msg_id = update.message.message_id
    # Telegram understands UTF-8, so encode text for unicode compatibility
    text = update.message.text.encode('utf-8').decode()
    print("got text message :", text)
    # here we call our super AI
    response = get_response(text)
    # now just send the message back
    # notice how we specify the chat and the msg we reply to
    bot.sendMessage(chat_id=chat_id, text=response, reply_to_message_id=msg_id)
    return 'ok'

This code should work each time telegram bot receives some message. But how do we know when the user sends a message? We can put this code in a cycle with 1 second sleep, but it is a bad solution.

For this purpose telegram has a webhook. After setting it telegram will send udpates automatically when the user sends a message to the bot.

In [ ]:
@app.route('/setwebhook', methods=['GET', 'POST'])
def set_webhook():
    # we use the bot object to link the bot to our app which live
    # in the link provided by URL
    s = bot.setWebhook('{URL}{HOOK}'.format(URL=URL, HOOK=TOKEN))
    # something to let us know things work
    if s:
        return "webhook setup ok"
    else:
        return "webhook setup failed"

Now everything should be ready.

In [ ]: