iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 16
1
自我挑戰組

LINE BOT 新手村30日攻略系列 第 16

Day16 口罩查詢系統 - 1

  • 分享至 

  • xImage
  •  

先跟各位說聲抱歉,昨天那篇實在太混了Q
我之後會將完整的 Rich Menus 內容都統整到昨天那篇裡
今天我們會開始我們的專案 - 口罩查詢系統

口罩查詢系統 Github 連結

這個專案的開發時間會比較長,我將內容大致分成幾個部分

  1. 取得藥局資料
    • 醫事機構代碼
    • 名稱
    • 電話
    • 地址
    • 座標
  2. 取得口罩資料
    • 名稱
    • 地址
    • 電話
    • 成人/兒童 口罩剩餘數量
    • 最後更新時間
  3. 製作 Flex 版型 & logger
  4. 回覆訊息 & Postback Action

在這次的專案當中,我會大量使用到 try-except 以及 function 加強程式碼的可讀性以及容錯率
程式碼大多是參考過去我開發的專案內容進行修改

事前規劃

我們的專案簡單來說是使用者傳送位置訊息,我們會回覆該地點半徑1公里內的藥局資訊
而我們的訊息會是以 Flex Message 傳送

為了要計算距離,我們還需要藥局的座標資料
所以我們需要先處理好各家藥局的座標資料
這部分有人有統整好的資料集

接下來是要收到藥局口罩的剩餘數量
這部分會使用政府提供的口罩資料

最後是回傳的內容,我們會要設計 Flex Message 的版型
其中需要有藥局的資料,最下方需要有一個按鈕,回傳該藥局的位置資料

資料的部分,我們會把資料以及副程式都分開放,架構如下

  • 主資料夾
    • main.py
    • config.yml
    • requirements.txt
    • Data
      • csv
        • maskdata.csv
        • positions.csv
      • json
        • positions.json
        • Bubble.json
        • Card.json
    • subsys
      • add_pos.py
      • get_distance.py
      • get_mask.py
      • logger.py
      • update.py

congfig.yml 是存放 Channel Access TokenChannel Secret 的檔案

規劃大致就到這邊,接下來就要開始實作囉

取得藥局座標資料

這邊參考了 Kiang 大大整理的資料,我們先將檔案下載下來
這裡提供兩種下載方式

  1. 另存連結
    前往連結後,會看到 view raw 的字樣,對它點右鍵後選擇 另存連結為...
    副檔名記得改為.csv
  2. wget
    wget https://github.com/kiang/pharmacies/blob/master/data.csv
    

如果你們也遇到亂碼問題,這裡提供兩個解決方式

  1. 如果你是使用 windows 系統,可以改以記事本開啟
    再來點擊 另存為... ,編碼方式改為 UTF-8
  2. 以 Excel 開啟,在上方的 資料 ,找到在 取得外部資料從文字檔
    選擇剛剛下載的檔案
    原始檔案格式 改成 UTF-8

    分隔符號勾選 逗點

    成功~

觀察一下資料之後會發現到裏面包含了很多種資料,但是我們只需要其中幾個

  • 醫事機構代碼
  • 電話
  • 地址
  • TGOS X
  • TGOS Y

所以接下來我們要對這些資料做處理,只留下我們需要的資料
另外,我們要將它改成 JSON 的格式,方便之後存取資料

修改藥局資料格式

我們先在資料夾放好剛剛轉成 UTF-8 格式的 csv 檔,取名為 data
最後要將結果輸出到 positions.json
我希望輸出的格式如下:

{
    "醫事機構代碼":{
        "name" : "藥局名稱"
        "phone" : "藥局電話"
        "address" : "藥局地址"
        "經度" : "藥局經度"
        "緯度" : "藥局緯度"
    },
    ...
}

程式碼如下

import csv,json
with open('data.csv','r',newline='') as f:
    data = csv.reader(f)
    output = open('positions.json','w')
    arr = {}
    for i in data:
        arr[i[0]] = {}
        arr[i[0]]['name']=i[1]
        arr[i[0]]['phone']=i[3]
        arr[i[0]]['address']=i[4]
        arr[i[0]]['lng']=i[12]
        arr[i[0]]['lat']=i[13]
    output.write(json.dumps(arr,indent=4,ensure_ascii=False))
    output.close()

取得口罩資料

口罩資料我們會使用政府提供的口罩資料集

這裡我們要製作兩個檔案
第一個是口罩資料集
另一個是將口罩資料以及藥局座標彙整起來的檔案

口罩資料是會不定時更新的資料集,為了確保獲取的資料是當前最新資料
我們會寫一個 python 去做更新
而更新也不是每次都要做,我設定每 30 分鐘一個區間,如果此次更新時間比上次多 30 分鐘以上,那就必須要更新資料了

update.py

import datetime,csv,json,requests,os
from logger import logger
from add_pos import add_pos
data_folder = str(os.getcwd()) + '/data/'

def update():
    position_out = open(data_folder + 'csv/positions.csv','r',newline='')
    nowtime = datetime.datetime.now()
    pasttime = datetime.datetime.strptime(list(csv.reader(position_out))[0][6],'%Y/%m/%d %H:%M:%S')
    minutes = datetime.timedelta(minutes=30)
    if(nowtime-pasttime >= minutes):
        # download maskdata.csv
        logger.info('========== downloading ==========')
        url = 'https://data.nhi.gov.tw/resource/mask/maskdata.csv'
        request_data = requests.get(url).content.decode('utf-8')
        # update maskdata.csv
        try:
            with open(data_folder + 'csv/maskdata.csv','w') as maskdata:
                logger.info('open maskdata.csv => Success')
                maskdata.write(request_data)
                logger.info('========== Download Success ==========')
        except:
            logger.warning('open maskdata.csv => Failed')
            logger.warning('========== Download Failed ==========')
            return False
        # update positions.csv
        res = add_pos()
        if(res == True):
            logger.info('create positions.csv => Success')
            return True
        else:
            logger.warning('create positions.csv => Failed')
            return False

時間處理的部分使用了 datetime 這個套件處理
logger 的部分目前還不會細講,功能是為了要方便 debug
它會產出 log.log 檔案,將我們所紀錄的內容都寫在裡面

特別注意到,我們在找資料位置會為了要在任何環境下都能順利操作,所以會以 os.getcwd() 再加上相對路徑的方式處理

另外,也可以發現到在開啟檔案我都會另外寫 log 紀錄
日後如果是檔案出問題就可以第一時間找到囉~

接下來要將下載的資料以及前面產出的 positions.json 資料結合成一份 csv

add_pos.py

import csv,json,os
from logger import logger
data_folder = str(os.getcwd()) + '/data/'

def add_pos():
    # read maskdata
    try:
        maskdata = open(data_folder + 'csv/maskdata.csv','r',newline='')
        logger.info('open maskdata.csv => Success')
    except:
        logger.warning('open maskdata.csv => Failed')
        return False
    # read dict table
    try:
        pos_data = open(data_folder + 'json/positions.json','r',newline='')
        logger.info('open positions.json => Success')
    except:
        logger.warning('open positions.json => Failed')
        return False
    # update positions
    try:
        out = open(data_folder + 'csv/positions.csv','w')
        logger.info('open positions.csv => Success')
    except:
        logger.warning('open positions.csv => Failed')
        return False
    sheets = csv.reader(maskdata)
    pos = json.load(pos_data)
    writer = csv.writer(out)
    for sheet in sheets:
        arr = sheet
        try:
            lat = pos[sheet[0]]['lat']
            lng = pos[sheet[0]]['lng']
        except KeyError:
            logger.warning('[add_pos] KeyError in line ' + str(sheet[0]))
            continue
        arr.append(lat)
        arr.append(lng)
        writer.writerow(arr)
    return True

到目前為止,關於資料處理的部分大致就完成了
在下一篇我們會繼續將後續的部分完成

後記

這次的實作內容如果完整自行實做起來可能會需要花到一整天的時間
在其中其實會遇到很多中文轉碼的問題
還有在 JSON, CSV 檔案會出現很多疑惑
我也翻了很多資料ww
希望大家能玩得開心拉~

參考資料

HackMD 口罩供需資料平台
Kiang pharmacy資料
健康保險資料開放服務
CSDN python3 讀取csv問題
Koios1143 Chat Bot
口罩查詢系統 Github


上一篇
Day15 Rich Menus
下一篇
Day17 口罩查詢系統 - 2
系列文
LINE BOT 新手村30日攻略30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言