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.
[Tokyo Metropolitan Government's new coronavirus infection control site] 1 data will be acquired, organized, plotted, and posted to Slack.
It is necessary to prepare the Token of Slackbot in advance, but in this article, we will omit the method of issuing the Token.
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
.
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
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.
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
It is the processing of post.post_message (txt)
.
↑ 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`.
This is the process of post.upload_figure ("", "")
.
↑ 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.
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
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