[PYTHON] Create a bot that posts the number of people positive for the new coronavirus in Tokyo to Slack

The Tokyo Metropolitan Government publishes the number of people positive for the new coronavirus (COVID-19) in Tokyo every day. I wanted to automatically post this data to Slack, so I created the source code.

What was made

[Tokyo Metropolitan Government's new coronavirus infection control site] 1 data will be acquired, organized, plotted, and posted to Slack. c19bot.PNG

environment

It is necessary to prepare the Token of Slackbot in advance, but in this article, we will omit the method of issuing the Token.

How to post

Overview

I implemented the bot to post content to Slack by running python3 run.py.

The contents of run.py are like this.

import plot
import post

def main():
    df = plot.fetch_csv() #Data acquisition
    plot.plot_hist(df) #Graph drawing
    txt = plot.info_str(df) #Latest positive count
    post.post_message(txt) #Post the latest number of positives
    post.upload_figure("", "") #Post graph
 
if __name__ == "__main__":
    main()

I have prepared post.py and plot.py in the same directory as run.py.

Data acquisition

This is the process of plot.fetch_csv (). [Public data of Tokyo] 2 has been acquired.

import requests
import pandas as pd

URL = "https://stopcovid19.metro.tokyo.lg.jp/"
def fetch_csv():
    r = requests.get(URL+"data/130001_tokyo_covid19_patients.csv")
    df = pd.read_csv(io.BytesIO(r.content),sep=",")
    return df

Graph drawing (pandas, matplotlib)

This is the process of plot.plot_hist (df). Draw a histogram with pandas using the acquired data.

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

PNG = "hist"

def plot_hist(df):
    #Extract only date columns with Timestamp
    df = pd.to_datetime(df.loc[:, "Published_date"])
    # bins(Total number of days)Calculate
    num_bins = calc_num_days(df)
    #Split up and down
    fig, axes = plt.subplots(nrows=2, ncols=1, sharex=True)
    df.hist(ax=axes[0], bins=num_bins)
    axes[0].set_title("By day", fontproperties=FP)
    df.hist(ax=axes[1], bins=num_bins, cumulative=True)
    plt.title("Accumulation", fontproperties=FP)
    plt.suptitle("Tokyo COVID-19 Number of positives(Data source"+URL+")", fontproperties=FP)
    #Set x-axis step size to 1 week
    plt.gca().get_xaxis().set_major_formatter(mdates.DateFormatter("%y/%m/%d"))
    plt.gca().get_xaxis().set_major_locator(mdates.DayLocator(interval=7))
    plt.xticks(rotation=90) #Rotate x-axis label 90 degrees
    plt.savefig(PNG, bbox_inches="tight")

calc_num_days (df) is used to calculate the histogram bins. In the acquired data (csv), the first line excluding the header line is the patient data on the day when the number of positives was confirmed for the first time, and the last line is the latest patient data.

def calc_num_days(df):
    s = df.iloc[0] #1st row, 1st column
    e = df.iloc[-1] #First column of the last row
    delta_days = e - s
    return delta_days.days + 1

Since Japanese font is used for the title of the graph, fontproperties is specified likeplt.title ("cumulative", fontproperties = FP).

FONT_TTF = "/home/[User name]/.local/lib/python3.5/site-packages/matplotlib/mpl-data/fonts/ttf/ipaexg.ttf"
FP = FontProperties(fname=FONT_TTF)

Depending on the environment, you can use Japanese fonts without specifying font properties.

By the way, I originally wanted to draw with a bar graph instead of a histogram, but it didn't work. The point that didn't work was that the horizontal axis was treated as a label instead of the time axis, and the day when the number of positives was 0 could not be drawn well. I thought it would be better to make a label for the day when the number of positive people was 0, but I thought it would be easier to draw it as a histogram, so I drew it with a histogram. I would be grateful if you could tell me if there is a smarter way.

Latest positive count

It is a process with plot.info_str (df). I am creating the text I want to post to Slack from the data.


def info_str(df):
    #Extract only date columns
    df = df.loc[:, "Published_date"]
    today = df.iloc[-1] #First column of the last row
    # index:date, colums:Number of people
    df = df.value_counts().sort_index()
    num_y = int(df.iloc[-2]) # yesterday
    num_t = int(df.iloc[-1]) # today
    txt = today+"Positive person at the time:"+str(num_t)+"Man\n" \
           +"(Compared to yesterday:"+str(num_t - num_y)+"Man,Total"+str(df.sum())+"Man)\n" \
           + URL
    return txt

Post the latest number of positives (Slackbot)

It is the processing of post.post_message (txt). c19bot_u.PNG

↑ I will post this on Slack.

Use Python's slackbot module and the Slack API.


import json
import requests

SLACK_API = "https://slack.com/api/"
TITLE = "COVID-19 Number of positive people in Tokyo"

def post_message(txt, title=TITLE):
    API_METHOD = "chat.postMessage"
    response = requests.post(SLACK_API + API_METHOD, data={
        "token": SLACK_TOKEN,
        "channel": SLACK_CHANNEL,
        "username": USER_NAME,
        "as_user": True,
        "attachments": json.dumps([
            {
                "fallback": title,
                "color":  "#00FFFF",
                "title": title,
                "text": txt,
            }
        ])
    }).json()

Describe the token of Slackbot in SLACK_TOKEN. Describe the channel name (" # hogehuga ") in SLACK_CHANNEL. Describe the bot user name in ʻUSER_NAME`.

Graph post

This is the process of post.upload_figure ("", "") . c19bot_b.PNG

↑ I will post this image to Slack.

PNG = "hist"
PNG_FILE = PNG+".png "

def upload_figure(txt, title=TITLE):
    files = {"file": open(PNG_FILE, "rb")}
    param = {
        "token": SLACK_TOKEN,
        "channels": CHANNEL_ID,
        "filename": PNG_FILE,
        "initial_comment": title+"\n"+txt,
        "title": title
        }
    requests.post(url=SLACK_API+"files.upload", params=param, files=files)

CHANNEL_ID is the same as the string at the end of the channel URL. It is not a channel name.

To post automatically every day (crontab)

If the terminal (server) is always running, you can execute commands regularly by using crontab. For example, if you want to post automatically at 15:05 every day, open the editor with crontab -e and write the following.

05 15 * * * cd [Directory with source code]; python3 run.py >run.log

Summary

We acquired the number of positive people for the new coronavirus in Tokyo and created a bot to post to Slack.

As for future issues, I think it would be even better if we could automatically notify when [Tokyo data] 2 was updated. If you come up with a good method, I would like to try it.

Recommended Posts

Create a bot that posts the number of people positive for the new coronavirus in Tokyo to Slack
Create a BOT that displays the number of infected people in the new corona
Posted the number of new corona positives in Tokyo to Slack (deployed on Heroku)
I created a Discord bot on Docker that reports the number of corona infected people in Tokyo at a specified time.
Find a guideline for the number of processes / threads to set in the application server
[Python] Programming to find the number of a in a character string that repeats a specified number of times.
Convert PDF of the situation of people infected in Tokyo with the new coronavirus infection of the Tokyo Metropolitan Health and Welfare Bureau to CSV
I made a slack bot that notifies me of the temperature
Create a function to get the contents of the database in Go
The story of creating a bot that displays active members in a specific channel of slack with python
I tried fitting the exponential function and logistics function to the number of COVID-19 positive patients in Tokyo
Create a bot to retweet coronavirus information
Did the number of store closures increase due to the impact of the new coronavirus?
[Python] A program that calculates the number of socks to be paired
The story of creating a store search BOT (AI LINE BOT) for Go To EAT in Chiba Prefecture (1)
A story about creating a program that will increase the number of Instagram followers from 0 to 700 in a week
[Python] Create a script that uses FeedParser and LINE Notify to notify LINE of the latest information on the new coronavirus of the Ministry of Health, Labor and Welfare.
How to mention a user group in slack notification, how to check the id of the user group
Set the number of elements in a NumPy one-dimensional array to a power of 2 (0 padded)
The story of creating Botonyan that returns the contents of Google Docs in response to a specific keyword on Slack
How to count the number of elements in Django and output to a template
The story of creating a store search BOT (AI LINE BOT) for Go To EAT in Chiba Prefecture (2) [Overview]
Find the number of days in a month
Scraping the number of downloads and positive registrations of the new coronavirus contact confirmation app
I tried to predict the number of people infected with coronavirus in Japan by the method of the latest paper in China
I tried to predict the number of people infected with coronavirus in consideration of the effect of refraining from going out
Factfulness of the new coronavirus seen in Splunk
A python script that gets the number of jobs for a specified condition from indeed.com
[Python] A program that finds the shortest number of steps in a game that crosses clouds
A story of trial and error trying to create a dynamic user group in Slack
The theory that the key to controlling infection with the new coronavirus is hyperdispersion of susceptibility
I tried to summarize the new coronavirus infected people in Ichikawa City, Chiba Prefecture
Let's put out a ranking of the number of effective reproductions of the new coronavirus by prefecture
Add a function to tell the weather of today to slack bot (made by python)
Create a dataset of images to use for learning
[Python] A program that counts the number of valleys
How to get the number of digits in Python
Create a slack bot
Use hash to lighten collision detection of about 1000 balls in Python (related to the new coronavirus)
Create a bot that only returns the result of morphological analysis with MeCab on Discord
I tried to tabulate the number of deaths per capita of COVID-19 (new coronavirus) by country
How to create an instance of a particular class from dict using __new__ () in python
[Completed] bot that posts the start notification of Nico Nico Live Broadcasting to Discord on discord.py
I tried to create a Python script to get the value of a cell in Microsoft Excel
I tried to find the trend of the number of ships in Tokyo Bay from satellite images.
[Concept] bot that posts the start notification of Nico Nico Live Broadcasting to Discord on discord.py
[Python] A program to find the number of apples and oranges that can be harvested
Get the number of specific elements in a python list
Create a Twitter BOT with the GoogleAppEngine SDK for Python
Let's visualize the number of people infected with coronavirus with matplotlib
Get the number of occurrences for each element in the list
Create a new list by combining duplicate elements in the list
I tried to create a bot for PES event notification
I want to create a Dockerfile for the time being.
A story that verified whether the number of coronas is really increasing rapidly among young people
Put the process to sleep for a certain period of time (seconds) or more in Python
Hypothesis of why the new coronavirus is not so common in urban areas such as Tokyo
A story that is a little addicted to the authority of the directory specified by expdp (for beginners)
[Google Photo & Slack Photo Bot] A story about making a bot that acquires photos in Google Photos and sends them to Slack.
Raspberry Pi? What's that? Level of hardware Inexperienced college students created a system to grasp if there are people in the office
How to determine the existence of a selenium element in Python