【GoogleHomeでメモ帳アプリを作る】5. HerokuアプリでSpreadSheetの中身を取得する
概要
以前helloアプリは作ったので、ここではSpreadSheetからデータを取得するアプリを作る
この前まででSpreadSheetは用意できているので、 ここでは、Spreadsheetからデータを取得するアプリケーションを「memorandum」という名前でDjangoに追加する
資料全体の構成はここに記載: GoogleHomeに話しかけてメモを記録したりメモを読み上げてもらう
memorandumアプリ
アプリ作成のおさらい
まずはhelloのおさらいを兼ねて、動くアプリを作成してみる
アプリの作り方は公式のチュートリアルの通り、 https://docs.djangoproject.com/ja/2.0/intro/tutorial01/#creating-the-polls-app
python manage.py startapp memorandum でいい
事前にpipenv shellを起動しておく
[~/git/ludwig125-heroku/googlehome] $pipenv shell (git)-[master] Loading .env environment variables… Spawning environment shell (/usr/bin/zsh). Use 'exit' to leave. source /home/ludwig125/.local/share/virtualenvs/googlehome-Z81pgPAi/bin/activate [~/git/ludwig125-heroku/googlehome] $source /home/ludwig125/.local/share/virtualenvs/googlehome-Z81pgPAi/bin/activate (git)-[master] (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $
アプリ作成
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $python manage.py startapp memorandum (git)-[master] (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $ls -l (git)-[master] 合計 84 -rw-rw-r-- 1 ludwig125 ludwig125 183 2月 4 02:07 Pipfile -rw-rw-r-- 1 ludwig125 ludwig125 7710 2月 4 22:54 Pipfile.lock -rw-rw-r-- 1 ludwig125 ludwig125 43 2月 4 03:00 Procfile -rw-r--r-- 1 ludwig125 ludwig125 38912 2月 4 00:50 db.sqlite3 drwxrwxr-x 3 ludwig125 ludwig125 4096 2月 4 23:39 googlehome/ drwxrwxr-x 4 ludwig125 ludwig125 4096 2月 4 02:38 hello/ -rwxrwxr-x 1 ludwig125 ludwig125 542 2月 3 23:57 manage.py* drwxrwxr-x 3 ludwig125 ludwig125 4096 2月 7 23:55 memorandum/ -rw-rw-r-- 1 ludwig125 ludwig125 269 2月 4 23:05 requirements.txt drwxrwxr-x 3 ludwig125 ludwig125 4096 2月 4 02:11 staticfiles/ (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $ (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $tree memorandum (git)-[master] memorandum ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py └── views.py 1 directory, 7 files (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $
views.py
おさらいで、とりあえず文字を返すアプリを作って動作を確認する
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $cat memorandum/views.py (git)-[master]
from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the memorandum index.")
urls.py
helloのときと同様に以下のファイルを追加
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $cat memorandum/urls.py (git)-[master] from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), ] (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $
ここも同様に、プロジェクトのurlsに上のurlsを認識させる
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $cat googlehome/urls.py (git)-[master] from django.contrib import admin from django.urls import include, path urlpatterns = [ path('hello/', include('hello.urls')), path('memorandum/', include('memorandum.urls')), ← ここを追加 path('admin/', admin.site.urls), ] (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $
ローカルでアプリ立ち上げ
ここまででアプリを立ち上げる
http://127.0.0.1:8000/memorandum/ ブラウザで以下が返ってきた
→ 「Hello, world. You're at the memorandum index.」
herokuに反映
ここまでをherokuにも反映する
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $git add memorandum/ (git)-[master] (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $git commit -m 'add initial memorandum' . (git)-[master] [master 9122dd7] add initial memorandum 9 files changed, 27 insertions(+) create mode 100644 memorandum/__init__.py create mode 100644 memorandum/admin.py create mode 100644 memorandum/apps.py create mode 100644 memorandum/migrations/__init__.py create mode 100644 memorandum/models.py create mode 100644 memorandum/tests.py create mode 100644 memorandum/urls.py create mode 100644 memorandum/views.py (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $ (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $git push heroku master (git)-[master] Counting objects: 15, done. Compressing objects: 100% (11/11), done. Writing objects: 100% (11/11), 1.36 KiB | 0 bytes/s, done. Total 11 (delta 5), reused 0 (delta 0) remote: Compressing source files... done.
リモートのherokuを確認
$heroku open
https://aqueous-peak-42683.herokuapp.com/memorandum/ → 「Hello, world. You're at the memorandum index.」
おさらい終わり
今作ったアプリをもとに、動作を確認しつつ修正していく
GoogleAPI秘密鍵の取得とSpreadSheetの共有設定
以下を参考に、GoogleAPIを取得し、SpreadSheetをAPIで呼べるようにする
参考 - https://qiita.com/koyopro/items/d8d56f69f863f07e9378 - https://qiita.com/AAkira/items/22719cbbd41b26dbd0d1 - http://www.yoheim.net/blog.php?q=20160205
GoogleAPIのクライアント認証用JSONを取得
cloud-resource-manager - https://console.developers.google.com/cloud-resource-manager ここで適当なプロジェクトを作成
ここでは、「getMymemo」というプロジェクトを作成する
上の参考を見ながら、作っていく
https://console.developers.google.com/cloud-resource-manager から 「APIとサービス」→ 「APIとサービスの有効化」からGoogle Drive APIを選択
→「認証情報」→「認証情報を作成」→「サービスアカウントキー」
SpreadSheetの共有設定
「memorandum」シートに上記で発行されたメールアドレスを登録
pythonでGoogleSpreadSheetにアクセスできるか確認
JSONファイルを直接使う
client認証用のjsonを以下に置く
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $cat google_client.json (git)-[master] { "type": "service_account", "project_id": "ludwig125-37f7c", "private_key_id": XXXXXX "private_key": XXXXXX "client_email": XXXXXX "client_id": "XXXXXX", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://accounts.google.com/o/oauth2/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "XXXXXX" } (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $
パッケージ追加
pythonでSpreadSheetにアクセスするために以下のパッケージを入れておく
pip install gspread pip install "oauth2client<2.0" pip install pycrypto
pythonでSpreadSheetのデータを取得する
pythonでSpreadsheetのデータが取れるか確認する
gspreadのリファレンスは以下 - http://gspread.readthedocs.io/en/latest/
サンプルスクリプト作成
このときのspreadsheetは以下のデータが存在している状態
テスト | ||
テスト に | ||
1517843323 | 2018/2/6 0:08 | テスト さん |
1517843115 | 2018/2/6 0:05 | test |
1517844297 | 2018/2/6 0:24 | test2 |
1518354065 | 2018/2/11 22:01 | test3 |
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome/sample] $cat get_memo.py
import json import gspread import oauth2client.client json_key = json.load(open('../google_client.json')) scope = ['https://spreadsheets.google.com/feeds'] credentials = oauth2client.client.SignedJwtAssertionCredentials(json_key['client_email'], json_key['private_key'].encode(), scope) gc = gspread.authorize(credentials) ss = gc.open("memorandum") sh = ss.worksheet("memo1") values = sh.get_all_values() print(values) # [['', '', 'テスト'], ['', '', 'テスト に'], ['1517843323', '2018/02/06 0:08:43', 'テスト さん'], ['1517843115', '2018/02/06 0:05:15', 'test'], ['1517844297', '2018/02/06 0:24:57', 'test2'], ['1518017810', '2018/02/08 0:36:50', 'test3']] print(len(values)) # 6 last = len(values) - 1 print(values[last]) # ['1517844303', '2018/02/06 0:25:03', 'test3']
実行結果
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome/sample] $python3 get_memo.py (git)-[master] [['', '', 'テスト'], ['', '', 'テスト に'], ['1517843323', '2018/02/06 0:08:43', 'テスト さん'], ['1517843115', '2018/02/06 0:05:15', 'test'], ['1517844297', '2018/02/06 0:24:57', 'test2'], ['1518017810', '2018/02/08 0:36:50', 'test3']] 6 ['1518017810', '2018/02/08 0:36:50', 'test3'] (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome/sample] $
きちんと取れている
秘密鍵をファイルではなく環境変数から取るようにする
上のソースコードの方法をherokuでやろうとすると、gitにGoogleAPIの秘密鍵を置かないといけない
これは嫌だ
http://hakobera.hatenablog.com/entry/20111122/1321975203 の辺を見ると、herokuには環境変数を渡すことができそうなので、この方法をやってみる
jsonファイルの内容を環境変数として以下のように設定しておく
export CLIENT_EMAIL="<自分のemail>.com" export PRIVATE_KEY="-----BEGIN PRIVATE KEY-----<改行コード含む長い鍵>\n-----END PRIVATE KEY-----\n"
秘密鍵の情報をファイルから取っていたこの部分を
$ cat get_memo.py json_key = json.load(open('../google_client.json')) scope = ['https://spreadsheets.google.com/feeds'] credentials = oauth2client.client.SignedJwtAssertionCredentials(json_key['client_email'], json_key['private_key'].encode(), scope)
↓
$cat get_memo_with_env.py scope = ['https://spreadsheets.google.com/feeds'] client_email = os.environ['CLIENT_EMAIL'] private_key = os.environ['PRIVATE_KEY'].replace('\\n', '\n').encode('utf-8') credentials = oauth2client.client.SignedJwtAssertionCredentials(client_email, private_key, scope)
こんな感じに変えて、取得できることを確認する
※ private_keyは改行コードを含んでおり、これをenvironで取得すると「\」が「\\」になってしまったので、置換して「\n」にした 参考 - https://docs.python.jp/3/library/os.html - https://webkaru.net/linux/export-command/ - https://docs.python.jp/3/howto/unicode.html#converting-to-bytes
実行してスプレッドシートから取得できることを確認
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome/sample] $python3 get_memo_with_env.py (git)-[master] [['', '', 'テスト'], ['', '', 'テスト に'], ['1517843323', '2018/02/06 0:08:43', 'テスト さん'], ['1517843115', '2018/02/06 0:05:15', 'test'], ['1517844297', '2018/02/06 0:24:57', 'test2'], ['1518017810', '2018/02/08 0:36:50', 'test3']] 6 ['1518017810', '2018/02/08 0:36:50', 'test3'] (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome/sample] $
ローカルのherokuで実行する
以上のことをもとに、view.pyを修正する
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $cat memorandum/views.py
from django.http import HttpResponse import json import gspread import oauth2client.client import os def get_client(): """ return google spreadsheet client see, http://gspread.readthedocs.io/en/latest/#gspread.authorize """ scope = ['https://spreadsheets.google.com/feeds'] client_email = os.environ['CLIENT_EMAIL'] private_key = os.environ['PRIVATE_KEY'].replace('\\n', '\n').encode('utf-8') credentials = oauth2client.client.SignedJwtAssertionCredentials(client_email, private_key, scope) gc = gspread.authorize(credentials) return gc def get_value(gc): """ get google spreadsheet values return last value """ ss = gc.open("memorandum") sh = ss.worksheet("memo1") values = sh.get_all_values() last = len(values) - 1 return values[last] def index(request): gc = get_client() last_value = get_value(gc) return HttpResponse(last_value)
ローカルで実行
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $heroku local web (git)-[master] ▸ heroku-cli: update available from 6.14.43-73d5876 to 6.15.24-e5de04c [OKAY] Loaded ENV .env File as KEY=VALUE Format 23:40:49 web.1 | [2018-02-09 23:40:49 +0900] [18423] [INFO] Starting gunicorn 19.7.1 23:40:49 web.1 | [2018-02-09 23:40:49 +0900] [18423] [INFO] Listening at: http://0.0.0.0:5000 (18423) 23:40:49 web.1 | [2018-02-09 23:40:49 +0900] [18423] [INFO] Using worker: sync 23:40:49 web.1 | [2018-02-09 23:40:49 +0900] [18426] [INFO] Booting worker with pid: 18426
http://localhost:5000/memorandum/
→ 「'1518017810', '2018/02/08 0:36:50', 'test3'」
取得できた
リモートのherokuで取れるようにする
requirements.txt編集
リモートで使うために必要なパッケージをrequirements.txtに入れておく - ローカルで使ったときにインストールした以下のパッケージを追加する - 何のバージョンを入れたのかは、pip freezeで確認
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $pip freeze | egrep "gspread|oauth2client|pycrypto" (git)-[master] gspread==0.6.2 oauth2client==1.5.2 pycrypto==2.6.1 (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $
requirements.txt - 既存のパッケージ以外に以下を追加する
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $cat requirements.txt (git)-[master] (略) gspread==0.6.2 (略) oauth2client<2.0 (略) pycrypto==2.6.1 (略) (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $
herokuに環境変数を追加
以下を見るとherokuのリモートに設定値を渡すことができるらしいので、 - https://devcenter.heroku.com/articles/config-vars#example - https://qiita.com/colorrabbit/items/18db3c97734f32ebdfde - http://hakobera.hatenablog.com/entry/20111122/1321975203
上の修正までをコミットしたらherokuに上げる
$ git push heroku master
ここで、heroku 環境変数を設定する
$ heroku config:set CLIENT_EMAIL="XXXX.com" $ heroku config:set PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nXXXXXX-----END PRIVATE KEY-----\n"
herokuの環境変数の設定方法について
- 上のようにheroku config:set をするか、
- ダッシュボードから行う方法がある
参考 - https://devcenter.heroku.com/articles/config-vars#setting-up-config-vars-for-a-deployed-application
設定できたかどうかは以下のコマンドで確認できる
$heroku config
リモートのherokuで確認
https://aqueous-peak-42683.herokuapp.com/memorandum/
→ 「1518017810 2018/02/08 0:36:50 test3」 Spreadsheetの最後のデータがブラウザに表示された