iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 17
1
Modern Web

從LINE BOT到資料視覺化:賴田捕手系列 第 17

第 17 天:Heroku Postgres:連接 LINE 聊天機器人

  • 分享至 

  • twitterImage
  •  

第 17 天:Heroku Postgres:連接 LINE 聊天機器人

今天天氣已經沒像昨天那樣冷了,不過陽光依然被雲層遮著,老實說並不那麼適合外出散步。但依然有一件美好的事發生。我在路上碰到了一家人,你可以看的出來他們剛從教堂做完禮拜,爸爸、媽媽、小男孩、更有一個不僅幽默風趣,還可以幫你整理資料的 LINE 聊天機器人。

~節錄自《賴田捕手》第十六章

  經過昨天的努力,我們已經學會透過 psycopg2 下達基本的 SQL 指令給 Heroku Postgres,藉此創建表格、存入(INSERT)資料、讀取(SELECT)資料、更新(UPDATE)資料、以及刪除(DELETE)資料。昨天與 psycopg2 相關的程式碼都是在 Jupyter Notebook 上執行的,讓我不禁覺得,這樣子操作資料真的是有點麻煩。有沒有辦法請 LINE 聊天機器人幫我們做到這些事呢?當然可以!
  今天我們就來試試看,藉由向 LINE 聊天機器人發送訊息,讓他來幫助我們將一筆一筆的資料,新增進資料庫的表格當中。

將訊息轉換為符合資料庫表單的資料

  狀況是這樣的:我會請草泥馬每天將訓練的菜單、訓練的開始時間跟結束時間回報給我,讓我方便記錄下來。因此,訊息的內容可能會長這樣:

9/25 草泥馬訓練紀錄:
藍道 肌力訓練 09:00-10:37
吉姆 嚼食訓練 11:01-12:30

  還記得我們昨天創建的表格alpaca_training嗎?這個表格當中每一個欄位分別是這樣的:

[('record_no', 'integer'), ('alpaca_name', 'character varying'), ('training', 'character varying'), ('duration', 'interval'), ('date', 'date')]

  我希望 LINE 聊天機器人能幫我將訊息做處理,變成適合放入我們昨天在資料庫裡創建的表格alpaca_training的格式,又因為第一欄'record_no'我們是將欄位的特徵定位為SERIAL,所以在我們輸入資料的時候,'record_no'會自動產生編號並幫我們編排下去。我們需要準備的資料,就只剩下'alpaca_name''training''duration'、和'date',像下面這樣:

[('藍道', '肌力訓練', datetime.timedelta(seconds=5820), datetime.date(2019, 9, 25)),
 ('吉姆', '嚼食訓練', datetime.timedelta(seconds=5340), datetime.date(2019, 9, 25))]

  之後,再藉由 psycopg2 將準備好的資料放入表格當中。

  都明白目標了嗎?那麼就開始寫程式碼囉。首先,將 LINE 的訊息轉換成適合輸入資料庫的格式:

# 引入處理日期時間需要用到的套件
import datetime

def prepare_record(text):
    text_list = text.split('\n')
    
    month = text_list[0].split(' ')[0].split('/')[0]
    day = text_list[0].split(' ')[0].split('/')[1]
    d = datetime.date(datetime.date.today().year, int(month), int(day))
   
    record_list = []
    
    time_format = '%H:%M'
    
    for i in text_list[1:]:
        temp_list = i.split(' ')
        
        temp_name = temp_list[0]
        temp_training = temp_list[1]
        
        temp_start = datetime.datetime.strptime(temp_list[2].split('-')[0], time_format)
        temp_end = datetime.datetime.strptime(temp_list[2].split('-')[1], time_format)
        temp_duration = temp_end - temp_start
        
        record = (temp_name, temp_training, temp_duration, d)
        record_list.append(record)
        
    return record_list

  假設我們傳送給LINE 聊天機器人的訊息,會以text這個參數進入到prepare_record()函數裡。上面那段程式碼,首先就是在做字串的拆解,因此我們前面學過關於字串的操作和一些特殊的功能,現在就可以派上用場了!接下來,為了處理時間相關的物件,我利用到了datetime這個套件,我們就來詳細看一看吧。

In [1]: temp_str = f"""今天:{datetime.date.today()}
        今年:{datetime.date.today().year}
        今月:{datetime.date.today().month}
        今日:{datetime.date.today().day}
        """
        print(temp_str)

  既然套件的名字是叫做datetime,當然可以處理日期也能處理時間。而且最強大的其中一個功能是,只要你說清楚格式,它能幫你將字串物件一個不差的轉換成可以用在日期時間的資料:

In [2]: time_format = '%a %b %d %H:%M:%S %Y'
      temp_str = 'Wed Sep 25 20:05:00 2019'
      datetime.datetime.strptime(temp_str, time_format)
Out[2]: datetime.datetime(2019, 9, 25, 20, 5)

  只要先把格式講清楚,strptime就能輕鬆幫你換成日期時間物件。再看一次:

In [3]: time_format = '%Y-%m-%d %H:%M:%S'
        temp_str = '2019-09-25 20:05:00'
        datetime.datetime.strptime(temp_str, time_format)
Out[3]: datetime.datetime(2019, 9, 25, 20, 5)

  日期時間物件有什麼好處呢?有了日期時間物件,我們就能請 Python 幫我們算時間。請問 9 月 9 號到今天總共過了幾天?

In [4]: datetime.datetime.today()-datetime.datetime(2019, 9, 9)
Out[4]: datetime.timedelta(days=16, seconds=73042, microseconds=745102)

  太仔細了!

  經過字串的處理跟日期時間的計算,我們應該是將傳給 LINE 聊天機器人的訊息轉換成符合alpaca_training欄位需求的格式了。來看看成果吧:

In [5]: LINE_message = """9/25 草泥馬訓練紀錄:
        藍道 肌力訓練 09:00-10:37
        吉姆 嚼食訓練 11:01-12:30"""

        prepare_record(LINE_message)
Out[5]: [('藍道', '肌力訓練', datetime.timedelta(seconds=5820), datetime.date(2019, 9, 25)),
 ('吉姆', '嚼食訓練', datetime.timedelta(seconds=5340), datetime.date(2019, 9, 25))]

  太好了,正是我們期望的資料!

將資料放入 Heroku Postgres

  相信大家看過昨天 psycopg2 跟 SQL 的說明之後,應該都能寫出符合自己需求的方法。但有一件事需要注意,我們昨天都是在 Jupyter Notebook 上面操作 psycopg2,而今天我們將要把這些程式碼放到 Heroku 上並交由 Heroku 執行,因此用 psycopg2 連線到 Heroku Postgres 的程式碼寫法要更改一下:

In [6]: DATABASE_URL = os.popen('heroku config:get DATABASE_URL -a 你-APP-的名字').read()[:-1]
        conn = psycopg2.connect(DATABASE_URL, sslmode='require')

  前幾天我們一直都是這樣來拿到屬於我們的DATABASE_URL,但放到 Heroku 上之後,在 Heroku 上只要輸入:

DATABASE_URL = os.environ['DATABASE_URL']
conn = psycopg2.connect(DATABASE_URL, sslmode='require')

  就可以了。
  那麼,將準備好的資料,放入 Heroku Postgres 的程式碼,看起來應該是這樣:

import os
import psycopg2

def line_insert_record(record_list):
    DATABASE_URL = os.environ['DATABASE_URL']

    conn = psycopg2.connect(DATABASE_URL, sslmode='require')
    cursor = conn.cursor()

    table_columns = '(alpaca_name, training, duration, date)'
    postgres_insert_query = f"""INSERT INTO alpaca_training {table_columns} VALUES (%s,%s,%s,%s)"""

    cursor.executemany(postgres_insert_query, record_list)
    conn.commit()

    message = f"恭喜您! {cursor.rowcount} 筆資料成功匯入 alpaca_training 表單!"
    print(message)

    cursor.close()
    conn.close()
    
    return message

  我將準備好的資料,以record_list的參數名稱放入line_insert_record()這個函數裡。若成功將record_list的資料放入 Heroku Postgres 當中的alpaca_training之後,回傳訊息恭喜我。
  來實際的試一試吧:

https://ithelp.ithome.com.tw/upload/images/20190925/20120178cAdb0MtAXz.png
圖一、有禮貌又會幫我們整理資料的 LINE 聊天機器人

  恭喜您!

  另外有個小建議,大家在把這段程式碼放入 LINE 聊天機器人的時候,可以試著讓 LINE 聊天機器人做判斷,用一個關鍵字去啟動prepare_record(),比如說像下面這樣:

if '草泥馬訓練紀錄' in event.message.text:
        
    try:
        record_list = prepare_record(event.message.text)
        reply = line_insert_record(record_list)

        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text=reply)
        )
            
    except:
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text='失敗了')
        )

  先用一個if '草泥馬訓練紀錄' in event.message.text:讓 LINE 聊天機器人知道我們這一次發送訊息到底是想要請他幫忙作紀錄,還是純聊天,而不是每一次發送訊息給他的時候,都要他去試著把訊息整理成資料。不然我們原本風趣幽默的 LINE 聊天機器人就會消失了。

https://ithelp.ithome.com.tw/upload/images/20190925/20120178iqyBdcfcMH.png
圖二、不僅幽默風趣,還會幫我們整理資料的 LINE 聊天機器人

  今天的實作出來了一個 LINE 儲存資料小幫手,利用在 LINE 上面發送訊息,就能夠幫我們在資料庫的表單中存入一筆一筆的資料。相關的程式碼我有放在 Github 上,大家若有興趣可以過去參考看看。一樣的,今天的內容若有不清楚的地方,歡迎直接在下面留言,我會盡可能地回覆大家的。謝謝大家!

參考資料

➀ Psycopg2 使用教學
➁ PostgreSQL 使用教學

註:對於此系列文有興趣的讀者,歡迎參考由此系列文擴編成書的 LINE Bot by Python,以及最新的系列文《賴田捕手:追加篇》
第 31 天 初始化 LINE BOT on Heroku
第 32 天 快速回覆 QuickReply 介紹
第 33 天 妥善運用 Heroku APP 暫存空間
第 34 天 妥善運用 LINE Notify 免費推播
第 35 天 製造 Deploy to Heroku 按鈕


上一篇
第 16 天:Heroku Postgres:CRUD
下一篇
第 18 天:Heroku Postgres:連接 LINE 聊天機器人(二)
系列文
從LINE BOT到資料視覺化:賴田捕手30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
1
asd95022
iT邦新手 5 級 ‧ 2020-08-17 19:24:51

不好意思我想請問一下,在jupyter中建立的資料庫,在HEROKU裡面已經實現了嗎?
我傳訊息給LINEBOT一直顯示失敗了,所以想問問我是不是需要從我的資料夾重新建立一次資料庫在座使用?

問題已解決了,不好意思。
在JUPYTER中已經建立好資料庫了,然後再HEROKU使用的時候需要再requirements中加入psycopg2==2.8.3

0
IAN
iT邦新手 5 級 ‧ 2020-12-27 22:46:10

老師你好,不好意思想問下,
1.我創了兩個APP,但是佈署推B的程式碼,會變成佈署推到A的APP去,
有打指令 heroku git:clone -a app名稱;佈署可以到指定的APP佈署嗎?

2.A的APP資料中有B的APP相關資料,有辦法清除嗎?
人工刪除後,heroku git:remote ,操作佈署程序又同步了。

再麻煩了,謝謝

您好

因為 heroku 是用 git 做版本控制,我猜比較簡單的做法可能是:

  1. 刪掉資料夾當中的 .git 檔案夾
D:\你的資料夾>tree
Folder PATH listing
Volume serial number is 9C33-6XXD
D:.
└───.git
    runtime.txt
    requirements.txt
    Procfile
    你的-Python-檔案.py

利用 git 做版本控制的資料夾當中,應該會存在一個 .git 的隱藏資料夾,裡面包含了所有版本控制相關的訊息,包括每一個提交的版本、遠端的資源庫位置等等。把 .git 整個資料夾刪掉,就不會再受到版本控制的影響。

  1. git init
    但是我們還是需要用 git 來做版本控制,因此在命令提示字元中重新輸入git init
D:\你的資料夾>git init
Initialized empty Git repository in D:/你的資料夾/.git/

把版本控制的訊息砍掉重練的意思。

  1. heroku git:remote -a 你指定的-APP-的名字
    最後,在命令提示字元輸入heroku git:remote -a 你指定的-APP-的名字
D:\你的資料夾>heroku git:remote -a 你指定的-APP-的名字
set git remote heroku to https://git.heroku.com/你指定的-APP-的名字.git

應該會看到上面這樣的訊息,代表這個資料夾的版本控制將會推送到https://git.heroku.com/你指定的-APP-的名字.git。那這樣應該就不會有問題了。

IAN iT邦新手 5 級 ‧ 2020-12-29 22:26:36 檢舉

感謝!!
刪除資料初始化後就好了 謝謝

0
Chris
iT邦新手 4 級 ‧ 2021-04-21 02:50:20

樓主您好!
不好意思 我想請問一下 關於程式碼的部分都是按照上面的格式打 但是打完卻顯示

time data '' does not match format '%H:%M'

想了很久 依舊不懂問題出在哪
如果樓主有空再麻煩請樓主幫我解惑一下
謝謝樓主

Chris iT邦新手 4 級 ‧ 2021-04-25 17:24:58 檢舉

以解決了 謝謝

我要留言

立即登入留言