LINE Things×kintoneハンズオン（kintone編）

## kintoneとは
![image1.png](https://qiita-image-store.s3.amazonaws.com/0/153051/8984262a-4448-b66a-9426-fbcda9b0d2d5.png)

------

### kintoneの機能
![image2.png](https://qiita-image-store.s3.amazonaws.com/0/153051/1ac3a807-ce26-474a-bd05-41516db62dcd.png)

------

#### アプリ
![image3.png](https://qiita-image-store.s3.amazonaws.com/0/153051/5c168bfc-50fb-7327-0b80-971ddd16735f.png)

------

#### ワークフロー (プロセス管理)
![image4.png](https://qiita-image-store.s3.amazonaws.com/0/153051/63f276a3-3f26-4521-5b34-1cb0836b5e7b.png)

------

#### コミュニケーション
![image6.png](https://qiita-image-store.s3.amazonaws.com/0/153051/106a65fa-df9a-ab9d-5955-4b6651237178.png)

------

### kintoneカスタマイズ

kintoneはカスタマイズも可能となっており、独自にいじることが可能です。

------

#### kintoneが提供しているAPI

![image.png](https://qiita-image-store.s3.amazonaws.com/0/153051/a4f5d006-4433-4d14-782f-acea205ee65b.png)

基本的に、kintone上でカスタマイズをするときは **kintone JavaScript API** を利用します。
レコードのデータをいろいろと利用したいときは **kintone REST API** を利用します。

------

## ハンズオン

ということで、
LINE Things dev board から送られたデータをkintoneへ貯めるハンズオンを始めます！！

------

### 0.必要なもの

* kintone環境
    * まだ用意されていないの方は[こちら](https://kintone.cybozu.co.jp/jp/trial/) からkintoneのお試しを申し込みください
* エディタ (お好みのもので大丈夫です)
    * **VisualStudioCode**, Atom, Sublime 等々

------

### 1.kintoneへログイン

kintoneへ申し込んだときにメールで送られるURLへアクセスします。

URL: `https://{subdomain}.cybozu.com`
ログイン名： `登録メールアドレス`
パスワード: `メールに初期パスワードが記載`

------

### 1-2.アプリ作成

![linethings-kintone_01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153051/ce712427-2591-c588-c513-4d29b1c099c6.gif)

* ログイン後、右側にある「アプリ」の＋ボタンからアプリを新規作成
* 「はじめから作成」を選択
* アプリ名をわかりやすいものへ変更
* 「数値」フィールドをD&D
* フィールドの設定からフィールドコードを `num` に変更
* 「フォームを保存」を選択

------

### 2.認証情報を取得

![linethings-kintone_02.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153051/ebe0e60f-c653-d262-c78a-56124f591458.gif)

* 「設定」から「APIトークン」を選択
* 「生成」を選択してアクセス権の **レコード追加** にチェック
* 生成されたトークンをコピー(メモ)
* 「保存」を選択して「アプリを公開/アプリの更新」を選択

------

### 3.アプリIDを取得

<img src="https://docs.google.com/drawings/d/e/2PACX-1vRo7KupPOK2buCfV6y6ppm9ZppEQoUglfbF19gf0-_Y92YAyraS98oVcRYDSoxwhdUZ80-jdjPpBCLT/pub?w=683&amp;h=334">

URL部分 `https://{subdomain}.cybozu.com/k/100` の **最後の数字部分** がアプリIDをとなります。(この例の場合は100がアプリID)

これでデータベースの用意は完了です。あとは作成したこのアプリに対してherokuからデータを投げます。

------

### 4-0.herokuのコードの書き換え

```python:main.py
from flask import Flask, request, abort, render_template
import os
import json
import base64
import urllib.parse
import requests

from linebot import (
    LineBotApi, WebhookParser
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)

# get Token and Secret from token
CHANNEL_ACCESS_TOKEN = os.environ.get('CHANNEL_ACCESS_TOKEN')
CHANNEL_SECRET = os.environ.get('CHANNEL_SECRET')

line_bot_api = LineBotApi(CHANNEL_ACCESS_TOKEN)
parser = WebhookParser(CHANNEL_SECRET)

app = Flask(__name__)

# check server working
@app.route('/')
def do_get():
    return "Hello, from flask!"

@app.route("/callback", methods=['POST'])
def callback():
    signature = request.headers['X-Line-Signature']

    # get request body as text
    body = request.get_data(as_text=True)
    print("Request body: " + body)

    try:
        # Parse JSON without SDK for LINE Things event
        events = json.loads(body)
        for event in events["events"]:
            if "things" in event:
                handle_things_event(event)
            else:
                event = parser.parse(body, signature)[0]
                handle_message(event)
    except InvalidSignatureError:
        print("Invalid signature. Please check your channel access token/channel secret.")
        abort(400)

    return 'OK'

def handle_things_event(event):
    if event["things"]["type"] != "scenarioResult":
        return
    if event["things"]["result"]["resultCode"] != "success":
        app.logger.warn("Error result: %s", event)
        return

    # send message of payload
    try:
        payload = base64.b64decode(event["things"]["result"]["bleNotificationPayload"])
        tempelature = int.from_bytes(payload, 'big') / 100.0
        line_bot_api.reply_message(event["replyToken"], TextSendMessage(text="値を受け取ったよ %s" % (tempelature)))
    except KeyError:
        return

    print("Got data: " + str(tempelature))

def handle_message(event):
    if event.type == "message" and event.message.type == "text":
        line_bot_api.reply_message(event.reply_token, TextSendMessage(text=event.message.text))

if __name__ == "__main__":
    app.debug = True
    app.run()

```

------

### 4.herokuのコード修正

`linethings_kintone_handson/main.py` を修正します。

```python:main.py
# get Token and Secret from token
# CHANNEL_ACCESS_TOKEN = os.environ.get('CHANNEL_ACCESS_TOKEN')
# CHANNEL_SECRET = os.environ.get('CHANNEL_SECRET')

# line_bot_api = LineBotApi(CHANNEL_ACCESS_TOKEN)
# parser = WebhookParser(CHANNEL_SECRET)

# app = Flask(__name__)

# ↓ Added code
# kintone Settings
URL = "https://{subdomain}.cybozu.com/k/v1/record.json" # change your URL
APPID = 100 # change your App ID
API_TOKEN = "XXXXXXX" # change your Token
```

kintoneのURL、アプリID、トークンを記載します。

```python:main.py
# def handle_things_event(event):
#     if event["things"]["type"] != "scenarioResult":
#         return
#     if event["things"]["result"]["resultCode"] != "success":
#         app.logger.warn("Error result: %s", event)
#         return

#     # send message of payload
#     try:
#         payload = base64.b64decode(event["things"]["result"]["bleNotificationPayload"])
#         temperature = int.from_bytes(payload, 'big') / 100.0

#         # ↓ Delete 
          line_bot_api.reply_message(event["replyToken"], TextSendMessage(text="値を受け取ったよ %s" % (temperature)))

#         # ↓ Added Post Data Code
          post_kintone(URL, APPID, API_TOKEN, temperature)

#     except KeyError:
#         return

#     print("Got data: " + str(temperature))

# ↓ Added Post Data func
def post_kintone(url, app, api_token, val):
    params = {
        "app": app,
        "record": {
            "num": {
            "value": val
            }
        }
    }
    headers = {"X-Cybozu-API-Token": api_token, "Content-Type" : "application/json"}
    requests.post(url, json=params, headers=headers)
```

LINEにメッセージを送る処理

`line_bot_api.reply_message(event["replyToken"], TextSendMessage(text="値を受け取ったよ %s" % (temperature)))` 

を削除して、kintoneへデータを登録する処理を追加します。

------

### 5.コードのデプロイ

編集した main.py を heroku にデプロイします。
以下のコマンドを１行ずつ実行してください。

```bash
$ git add main.py
$ git commit -m "add post kintone"
$ git push heroku master
```

こんな感じにデータが入れば成功です！

<img width="1440" alt="スクリーンショット 2019-07-27 2.41.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153051/43c0d8cb-949a-c9f6-f927-c21c97616f26.png">

------

### 6.グラフ作成

kintoneには標準機能でグラフが作れるので、GUIでぽちぽち設定します。
![linethings-kintone_03.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153051/08ba2441-5450-8cd5-d1ae-33dd32c89c29.gif)

ただ・・・単位が分単位しかないので正直つらいです・・・
<img width="1440" alt="スクリーンショット 2019-07-27 10.29.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153051/762a77b5-06e2-3ddb-2d1b-8854f1898081.png">

なので作成日時ではなく、もう一つフィールドを用意してそのフィールドを並べる **裏技** 的な方法をお教えします

------

### 7.フィールド追加

新たに `文字列（1行）` フィールド を配置して、フィールド名を `日時` に、フィールドコードを `time` に変更してアプリを更新します。
![linethings-kintone_04.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153051/53d17fc4-0d7c-f0ec-68ba-255ca24b2349.gif)

これでもう一つデータを格納するカラムが追加されます。

------

### 8.コードの修正

現在日時を取得してその値を先ほど作成した `日時` に入れるように `main.py` を修正します。

現在日時を取得する `datetime` をインポートします。

```python:main.py
# from flask import Flask, request, abort, render_template
# import os
# import json
# import base64
# import urllib.parse
# import requests

# ↓ add
from datetime import datetime, timedelta, timezone
```

JSTで現在日時を取得して `time` に格納する処理に変更します。

```python:main.py
def post_kintone(url, app, api_token, val):
    JST = timezone(timedelta(hours=+9), 'JST')
    now = datetime.now(JST).strftime("%Y/%m/%d %H:%M:%S")
    params = {
        "app": app,
        "record": {
            "num": {
                "value": val
            },
            "time": {
                "value": now
            }
        }
    }
    headers = {"X-Cybozu-API-Token": api_token, "Content-Type" : "application/json"}
    requests.post(url, json=params, headers=headers)
```

再度、heroku へデプロイします。

```bash
$ git add main.py
$ git commit -m "add post nowTime"
$ git push heroku master
```

こんな感じに現在日時も登録されればOKです！

<img width="1440" alt="スクリーンショット 2019-07-27 11.02.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153051/73e9aace-0e44-65ac-cad4-457bc826b806.png">

------

### 9.グラフの再設定

グラフの軸を `日時` に、集計方法を `合計` に変更してグラフを作成します。
![linethings-kintone_05.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153051/e9f11886-6c92-4912-090b-ebfc3953f6ae.gif)

------

## 画面カスタマイズ

kintoneの標準機能のグラフでもそこそこ作れますが、もっと作り込むことも可能です。
(HTML、JS、CSSによるカスタマイズ)
<img width="1440" alt="スクリーンショット 2019-07-27 14.19.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153051/6a9fad4e-7a7f-6a05-7f46-59b849b12b81.png">

------

## おわりに

kintoneに貯めることで、その後データも扱いやすく、
エンジニア以外も利用できるようになります！

ただ、kintoneへのリクエスト数にはお気をつけくださいm(_ _)m
※ 1日1アプリ１００００リクエストが上限です。つまり毎秒単位でデータを送ると警告メールがきます。
`60 * 60 * 24 = 86400!`