Ubuntu 14.04のPython2のデフォルトバージョンをPython 2.7.9にする
たまたまやる必要があったのでメモ
参考
https://www.ralphlepore.net/installing-python-279-on-ubuntu-1404-lts/
このとおりやればできる
アップデート前
[~] $python2 --version Python 2.7.6 [~] $
アップデート
mkdir ~/software cd ~/software wget https://www.python.org/ftp/python/2.7.9/Python-2.7.9.tgz # Now we are going to unpack the tar and install python tar -xvf Python-2.7.9.tgz cd Python-2.7.9 ./configure make sudo make install
反映させるために再起動 以下をする前に大事なものは保存しておく
# Restart server to ensure everything is up to date. sudo shutdown now -r
アップデート後
[~] $python2 --version Python 2.7.9 [~] $
vim8のインストール(Ubuntu 14.04)
vim8のインストール方法
OSのバージョン:Ubuntu 14.04 ※ Ubuntu 16.04でも以下と同じ方法でできるっぽい
現在のvimの確認
現在のvimのバージョンは以下で確認できる
方法1.vim --version
自分が実行したときの出力結果
[~] $vim --version VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Dec 18 2015 21:31:31) 適用済パッチ: 1-882 Modified by pkg-vim-maintainers@lists.alioth.debian.org Compiled by buildd@ 以下省略
方法2.dpkg -l vim
自分が実行したときの出力結果
[~] $dpkg -l vim 要望=(U)不明/(I)インストール/(R)削除/(P)完全削除/(H)保持 | 状態=(N)無/(I)インストール済/(C)設定/(U)展開/(F)設定失敗/(H)半インストール/(W)トリガ待ち/(T)トリガ保留 |/ エラー?=(空欄)無/(R)要再インストール (状態,エラーの大文字=異常) ||/ 名前 バージョン アーキテクチャ 説明 +++-===================-==============-==============-=========================================== ii vim 2:7.4.826-1+da amd64 Vi IMproved - enhanced vi editor [~] $
最新のvimのインストール
以下の3つのコマンドをさえすればOK 参考:http://tipsonubuntu.com/2016/09/13/vim-8-0-released-install-ubuntu-16-04/
sudo add-apt-repository ppa:jonathonf/vim sudo apt update sudo apt install vim
上のコマンドの意味
add-apt-repository
UbuntuではPPA(Personal Package Archives)というUbuntu非公式のソフトウェアを管理しているアーカイブをPPAというらしい 参考:https://allabout.co.jp/gm/gc/438675/
sudo add-apt-repository ppa:jonathonf/vim コマンドによって、PPAのvimのリポジトリをこのUbuntuに認識させる
sudo apt update
パッケージの一覧を更新 リポジトリ追加・削除時には必ず実行するべきコマンド
sudo apt install vim
これで最新のvimをインストールすることができる
vimのバージョン確認
[~] $vim --version VIM - Vi IMproved 8.1 (2018 May 17, compiled May 23 2018 13:04:51) 適用済パッチ: 1-20 Modified by pkg-vim-maintainers@lists.alioth.debian.org Compiled by pkg-vim-maintainers@lists.alioth.debian.org 以下省略
[~] $dpkg -l vim 要望=(U)不明/(I)インストール/(R)削除/(P)完全削除/(H)保持 | 状態=(N)無/(I)インストール済/(C)設定/(U)展開/(F)設定失敗/(H)半インストール/(W)トリガ待ち/(T)トリガ保留 |/ エラー?=(空欄)無/(R)要再インストール (状態,エラーの大文字=異常) ||/ 名前 バージョン アーキテクチャ 説明 +++-===================-==============-==============-=========================================== ii vim 2:8.1.0020-0yo amd64 Vi IMproved - enhanced vi editor [~] $
おわり
【GoogleHomeでメモ帳アプリを作る】6. GoogleHomeに呼びかけてSpreadSheetのメモを読み上げてもらう
概要
前回までで、heroku上にアプリを作成するところまでできた
今度は、それをGoogleHomeから呼び出すため(正確にはactions on googleから呼び出すため)に、Dialogflowの設定をする
資料全体の構成はここに記載: GoogleHomeに話しかけてメモを記録したりメモを読み上げてもらう
参考
参考 actions on googleの公式マニュアル(Dialogflowについて)
Dialogflowの公式マニュアル
その他の参考
- https://qiita.com/kenz_firespeed/items/0979ceb05e4e3299f313
- https://qiita.com/kotatu_km/items/c06f45c6692dceb9258d
- https://qiita.com/doki_k/items/11f8a23d71dce59409da
- https://qiita.com/ume1126/items/f60335179b1cee25fa2f
- https://qiita.com/miso_develop/items/28cbfcdfb61d455ce346
- https://kotodama.today/?p=45
- https://docs.google.com/presentation/d/1HjxtUtMhphdBTm_KOGDb1dq7Dy1R8sNCQj80zDdCJqg/mobilepresent?slide=id.g2aeb9765a5_1_584
Dialogflowの詳しい作り方は上や色々なサイトに書いてあるのでここでは自分のアプリのために必要な最低限のことだけを記載する
Dialogflowでアプリ作成
actions on googleとDialogflowを連携する
https://developers.google.com/actions/
ACTIONS CONSOLE ボタンを押す
https://console.actions.google.com/
Add/import project を選択
Dialogflowを選択
※Dialogflow以外もあるようだが、参考資料が多かったのでここではDialogflowを使う
Dialogflowの設定
Intents
公式のマニュアル - https://dialogflow.com/docs/intents
新規のIntentsを作成 - ここでは、「 getMymemo 」 という名前のIntentsを作成
User saysに「 メモを読んで 」という文言を追加する
一旦これでSAVE
Fulfillment
WebhookのURLにherokuのURLを追加する
これで
ENABLEDにする
IntentsとFulfillmentを連携
先程のIntentsに戻って、Fulfillmentの連携を有効にする
ここまででDialogflow側の最低限の設定は終わり
DialogflowからWebアプリへのリクエストとレスポンス制約
DialogflowからWebアプリを呼び出すためには以下のフォーマットに従う必要がある
公式のドキュメント - https://dialogflow.com/docs/fulfillment
Webhook
Setting up a webhook allows you to pass information from a matched intent into a web service and get a result from it.
Webhookの仕様(制約)
Request
Your web service receives a POST request from Dialogflow.
Response
The response from the service should have the following fields:
Name | Type | Description |
---|---|---|
speech | String | Response to the request. |
displayText | String Text displayed on the user device screen. | |
data | Object | Additional data required for performing the action on the client side. The data is sent to the client in the original form and is not processed by Dialogflow. |
contextOut | Array of context objects | Array of context objects set after intent completion. |
source | String | Data source. |
followupEvent | Object | Event name and optional parameters sent from the web service to Dialogflow. Read more |
Caution: The header must be “Content-type: application/json”.
つまり要約すると・・・ - リクエスト - リクエストはPOSTでJSON形式で送られる - レスポンス - アプリからのレスポンスはJSON形式で、ヘッダーは“Content-type: application/json” である必要がある - レスポンスにはspeech, displayTextなどの項目が必要らしい
heroku(Django)の修正
前述の仕様を満たすように、Djangoアプリの修正をする
https://github.com/ludwig125/googlehome/blob/master/memorandum/views.py
必要な項目を返すようにする
- HttpResponse になっていた部分を以下のように書き直す
- また、レスポンスの項目として「speech」にSpreadSheetの最新の項目を返すようにする
差分
git diff views.py
diff --git a/memorandum/views.py b/memorandum/views.py index a544014..65e37fc 100644 --- a/memorandum/views.py +++ b/memorandum/views.py @@ -1,4 +1,5 @@ -from django.http import HttpResponse +from django.http import JsonResponse import json import gspread import oauth2client.client @@ -32,4 +33,12 @@ def get_value(gc): def index(request): gc = get_client() last_value = get_value(gc) - return HttpResponse(last_value) + res = { + "speech": last_value[2], + "displayText": ””, + "data": "", + "contextOut": "", + "source": "" + } + return JsonResponse(res)
JsonResponseとは
参考Django公式 - https://docs.djangoproject.com/en/2.0/ref/request-response/#jsonresponse-objects
An HttpResponse subclass that helps to create a JSON-encoded response. It inherits most behavior from its superclass with a couple differences: Its default Content-Type header is set to application/json.
他の参考 - https://simpleisbetterthancomplex.com/tutorial/2016/07/27/how-to-return-json-encoded-response.html
Its default Content-Type header is set to application/json.
とあるので、これを使えばfullfilementの制約が満たせそう
ブラウザから呼び出して確認
これでDjangoサーバを立ててみる
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $python manage.py runserver Performing system checks... System check identified no issues (0 silenced). February 11, 2018 - 22:00:40 Django version 2.0.2, using settings 'googlehome.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C. [11/Feb/2018 22:00:44] "GET /memorandum/ HTTP/1.1" 200 109 [11/Feb/2018 22:01:06] "GET /memorandum/ HTTP/1.1" 200 79
http://127.0.0.1:8000/memorandum/
→ 「{"speech": "test3", "displayText": "", "contextOut", "", "source": ""}」
ただ、POSTリクエストで送って返ってくるかどうかがわからない
Advanced REST client でリクエストを送って確認
Advanced REST client - https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo?hl=ja
これを使うと、GET, POSTリクエストを選んだり、レスポンスのステータスをチェックしたり色々便利、これで確認する
これでPOSTリクエストを送ると、結果が返ってきた!!
これをDialogflowが要求する形式に直す
このあたりのサンプルを見ると、dataとcontextOutはコメントアウトしていても問題なさそうなので、コメントアウトする - https://qiita.com/ume1126/items/f60335179b1cee25fa2f - https://github.com/dialogflow/fulfillment-webhook-weather-python/blob/966959087a5c82a50b21ce43515c7133e092f13c/app.py#L108-L112
views.pyを以下のように書き換える
from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt 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] @csrf_exempt def index(request): gc = get_client() last_value = get_value(gc) res = { "speech": last_value[2], "displayText": last_value[2], #"data": {"kik": {}}, #"contextOut": [{"name":"weather", "lifespan":2, "parameters":{"city":"Rome"}}], "source": "spreadsheet memorandum" } return JsonResponse(res)
@csrf_exempt は以下を参考に付けた https://docs.djangoproject.com/ja/2.0/ref/csrf/
クロスサイトリクエストフォージェリ (CSRF) 対策
上の内容をherokuにデプロイして(git push heroku masterして) Advanced REST clientを使うと200が返ってきた
修正したアプリに対してリクエストしてみる
Dialogflowからリクエストを送る
dialogflowの画面で、「try it now」に「メモを読んで」と入れてみると、test3が返ってきた
SHOW JSONボタンを押すと以下が得られる
{ "id": "XXXXXX", "timestamp": "2018-02-12T12:43:43.804Z", "lang": "ja", "result": { "source": "agent", "resolvedQuery": "メモを読んで", "action": "", "actionIncomplete": false, "parameters": {}, "contexts": [], "metadata": { "intentId": "XXXXXX", "webhookUsed": "true", "webhookForSlotFillingUsed": "false", "webhookResponseTime": 1153, "intentName": "getMymemo" }, "fulfillment": { "speech": "test3", "source": "spreadsheet memorandum", "displayText": "test3", "messages": [ { "type": 0, "speech": "test3" } ] }, "score": 1 }, "status": { "code": 200, "errorType": "success", "webhookTimedOut": false }, "sessionId": "XXXXXX" }
※IDは適当にXXXXXXとかに置き換えている
actions on googleでテストする
テストまでできたらintegrations - integrationsの方法は一番上に書いた参考ページなどを見ればすぐわかる
ここまでで、Actions on Googleで以下のようにテストすると、スプレッドシートからかえってくることが確認できた
自分「テスト用アプリにつないで」と記入
↓
画面「はい、テスト用アプリのテストバージョンです。」 「こんにちは!」
↓
自分「メモを読んで」
↓
画面「test3」と表示された
GoogleHomeで実際に動作を確認する
これをGoogleHome相手にやっても返ってきた!!
自分「OK Google, テスト用アプリにつないで」とGoogleHomeに話す
↓
GoogleHome「はい、テスト用アプリのテストバージョンです。」 「こんにちは!」
↓
自分「メモを読んで」
↓
GoogleHome「test3」
最新のデータを読み上げさせるところまで成功した!
Dialogflowから送ったリクエストに応じて処理を変える
- Dialogflowから送ったリクエストのパラメータを見て、それに応じた件数だけSpreadsheetからデータを返すように修正する
Dialogflow参考
公式
- https://dialogflow.com/docs/fulfillment
- https://qiita.com/kenz_firespeed/items/0979ceb05e4e3299f313
他 - https://qiita.com/sakamoto_koji/items/8a9bb8a3f2063b4b5f34
Dialogflowからリクエストを送る
Intentsを修正
試しにDialogflowからリクエストを送ってみる
「1件読んで」 というサンプルリクエストを書いて、PARAMETER NAME に「number」を、「1」の部分にENTITYの@sys.numberを割り当てる
これによって、「1件読んで」をリクエストすれば、numberに1が、「3件読んで」とすればnumberに3が入る事になる
試しにTry it nowに「3件読んで」を入れて送ってみる
リクエストの中身を確認
今回は、herokuのview.py側で出力させるdisplayTextを以下のようにして、何が送られているか確認してみる
req = json.loads(request.body.decode('utf-8')) res = { "speech": last_value[2], "displayText": str(req), "source": "spreadsheet memorandum" }
SHOW JSONを押すと送った内容とレスポンスが見られる JSON
{ "id": "XXXXXX", "timestamp": "2018-02-23T14:27:16.974Z", "lang": "ja", "result": { "source": "agent", "resolvedQuery": "1件読んで", "action": "", "actionIncomplete": false, "parameters": { "number": 3 }, "contexts": [], "metadata": { "intentId": "XXXXXX", "webhookUsed": "true", "webhookForSlotFillingUsed": "false", "webhookResponseTime": 983, "intentName": "getMymemo" }, "fulfillment": { "speech": "test3", "source": "spreadsheet memorandum", "displayText": "{'id': 'XXXXXX', 'timestamp': '2018-02-23T14:27:16.974Z', 'lang': 'ja', 'result': {'source': 'agent', 'resolvedQuery': '1件読んで', 'speech': '', 'action': '', 'actionIncomplete': False, 'parameters': {'number': 1}, 'contexts': [], 'metadata': {'intentId': 'XXXXXX', 'webhookUsed': 'true', 'webhookForSlotFillingUsed': 'false', 'intentName': 'getMymemo'}, 'fulfillment': {'speech': '', 'messages': [{'type': 0, 'speech': ''}]}, 'score': 1.0}, 'status': {'code': 200, 'errorType': 'success', 'webhookTimedOut': False}, 'sessionId': 'XXXXXX'}", "messages": [ { "type": 0, "speech": "test3" } ] }, "score": 1 }, "status": { "code": 200, "errorType": "success", "webhookTimedOut": false }, "sessionId": "XXXXXX" }
リクエストの中身をそのまま返せていることが確認できた
リクエストの中身から必要な項目を取り出す
- 「○件読んで」の○部分を取り出したい
リクエストで来ているJSONの中身は「request」の中の「parameters」の中の「number」で取得できる
よって、以下のようにしてやれば、「○件読んで」の○が取得できるはず
@csrf_exempt def index(request): req = json.loads(request.body.decode('utf-8')) number = req['result']['parameters']['number'] gc = get_client() last_value = get_value(gc) res = { "speech": last_value[2], "displayText": number, "source": "spreadsheet memorandum" } return JsonResponse(res)
試しにDialogflowのSHOW JSONで得られたJSONをAdvanced REST client のクエリに入れて実行するとこうなる
{ speech: "test3", displayText: 3, source: "spreadsheet memorandum" }
「3」が取得できている
あとはこのnumberを使って、それだけの件数Spreadsheetのデータを読み込んで返すように修正すればいい
Entitiesを修正
現時点の課題
- 件数を直接指定する必要がある
- GoogleHome想定通りに命令を認識しれてくれ無いことがある
Intentsを上述のように追加したことで「3件読んで」「5件読んで」というように件数を数字を指定すればそれだけの件数返してもらえるようになった しかし、毎回「○件読んで」と件数を指定するのは面倒なときもある
また、GoogleHomeから呼び出すと、うまく「3件読んで」などと認識してもらえず、「三件読んで」や「三件呼んで」と認識されてしまうことがある - ※どう発声したと認識されているかはGoogleHomeアプリのアクティビティを見れば履歴がわかる
解決方法としてEntitiesに登録する
上の課題を解決するためにEntitiesを使う
Entities - https://dialogflow.com/docs/entities
Entities are powerful tools used for extracting parameter values from natural language inputs. Any important data you want to get from a user's request, will have a corresponding entity
Entitiesでは、上にあげたような、「3件読んで」「三件読んで」「三件呼んで」などの違った命令が来たときに、等しく「3」をリクエストパラメータとするように扱ってもらうことができる
DialogflowのEntitiesを開いて、「request_nums」として以下のように登録する
左側がリクエストとして送られる数字 | シノニム |
---|---|
1 | 1, メモを読んで, 1件読んで, 一件読んで, いっけんよんで | |
2 | 2, 2件読んで, 二件読んで, 二件呼んで, にけんよんで |
3 | 3, 3件読んで, 三件読んで, 三件呼んで, さんけんよんで |
3 | 3, いくつか読んで, いくつか呼んで, いくつか, いくつかよんで |
こんな感じに色々なバリエーションを設定しておく
アプリの修正
前述のnumber以外に、request_numsがあったときはこれを件数として扱ってそれだけデータを返すようにアプリを修正する
修正後 - https://github.com/ludwig125/googlehome/blob/master/memorandum/views.py
これで、件数を指定しても、「いくつか」などと言っても柔軟にデータを読み上げてもらえるようになった
【GoogleHomeでメモ帳アプリを作る】7. 使い勝手を良くする
概要
いままで作ったアプリの使い勝手を良くする
資料全体の構成はここに記載: GoogleHomeに話しかけてメモを記録したりメモを読み上げてもらう
やること
やることは以下
- Herokuをスリープさせないようにする
- アプリの名前を変える
- ショートカットを作って呼び出しを簡単にする
- Herokuのアプリの名前を変更する
1. Herokuをスリープさせないようにする
現時点の課題:herokuがスリープしてしまってタイムアウトする
GoogleHomeからactions on googleのfullfilmentから実行するためには、5秒以内に返さなくてはいけないらしい
参考公式 - https://developers.google.com/actions/sdk/deploy-fulfillment
Note: Your fulfillment must respond within 5 seconds or the Assistant ends your conversation due to a timeout.
https://dialogflow.com/docs/fulfillment#limits
Timeout for service response – 5 seconds.
- herokuは無料版だと30分以上操作がないときはスリープしてしまう
- スリープを解除して応答するには時間がかかり、5秒のタイムアウトを超えてしまうことがしばしばある
- これでは使い物にならないので、herokuをスリープさせないようにする必要がある
参考 herokuの無料枠で使える時間 - https://devcenter.heroku.com/articles/free-dyno-hours#usage
Accounts are given a base of 550 hours each month in which your Free dynos can run. In addition to these base hours, accounts which verify with a credit cardwill receive an additional 450 hours to the monthly Free dyno quota.
http://awesome-linus.tk/2017/12/14/heroku-pricing/
クレジットカード認証なしだと、月に550時間、クレジットカード認証ありだと月に1000時間(550+450)使える
スケジューラを設定して定期的に叩く
公式
以下を参考に設定
- https://himakan.net/websites/heroku_free_plan_doesnt_sleep
- https://blog.mktia.com/how-to-avoid-to-sleep-the-application-on-heroku/
こんなのもあった
- https://quickleft.com/blog/6-easy-ways-to-prevent-your-heroku-node-app-from-sleeping/
- https://medium.com/@pandachain/keep-free-heroku-app-awake-during-a-specific-period-using-google-app-script-in-2017-63fe37ee9e9f
[~/git/ludwig125-heroku/googlehome] $heroku addons:create scheduler:standard (git)-[master] ▸ heroku-cli: update available from 6.14.43-73d5876 to 6.15.26-5726b6f Creating scheduler:standard on ⬢ aqueous-peak-42683... free This add-on consumes dyno hours, which could impact your monthly bill. To learn more: http://devcenter.heroku.com/addons_with_dyno_hour_usage To manage scheduled jobs run: heroku addons:open scheduler Created scheduler-horizontal-89891 Use heroku addons:docs scheduler to view documentation [~/git/ludwig125-heroku/googlehome] $
UIで以下のコマンドを10分おきに実行するようにする
if [ $(date --date "9hours" +%H) -ge 06 -o $(date --date "9hours" +%H) -le 02 ]; then curl https://aqueous-peak-42683.herokuapp.com ; fi
コマンドの説明
- curlでアプリのURLを指定して実行する
- 流石に午前2時から6時の間は使うことも無いはずなので、それ以外の時間に叩くようにしたい
- herokuの時間はGMT標準時なので、日本時間より9時間前
- そこで、「if [ $(date --date "9hours" +%H) -ge 06 -o $(date --date "9hours" +%H) -le 02 ]」とする
指定の時刻に実行できているか確認
heroku logs の結果を見る
2018-03-01T14:54:57.067107+00:00 app[scheduler.6122]: </body> 2018-03-01T14:54:57.067108+00:00 app[scheduler.6122]: </html> 2018-03-01T15:05:47.980054+00:00 app[api]: Starting process with command `if [ $(date --date "9hours" +%H) -ge 06 -o $(date --date "9hours" +%H) -le 02 ]; then curl https://aqueous-peak-42683.herokuapp.com ; fi` by user scheduler@addons.heroku.com 2018-03-01T15:05:54.034812+00:00 heroku[scheduler.5767]: Starting process with command `if [ $(date --date "9hours" +%H) -ge 06 -o $(date --date "9hours" +%H) -le 02 ]; then curl https://aqueous-peak-42683.herokuapp.com ; fi` 2018-03-01T15:05:54.827763+00:00 heroku[scheduler.5767]: State changed from starting to up 2018-03-01T15:05:56.537126+00:00 heroku[router]: at=info method=GET path="/" host=aqueous-peak-42683.herokuapp.com request_id=86071458-7414-42f7-b246-e056260137f6 fwd="174.129.137.75" dyno=web.1 connect=1ms service=7ms status=404 bytes=2344 protocol=https 2018-03-01T15:05:56.536624+00:00 app[web.1]: Not Found: / 2018-03-01T15:05:56.537059+00:00 app[web.1]: 10.151.49.221 - - [02/Mar/2018:00:05:56 +0900] "GET / HTTP/1.1" 404 2161 "-" "curl/7.47.0" 2018-03-01T15:05:56.265126+00:00 app[scheduler.5767]: Dload Upload Total Spent Left Speed 2018-03-01T15:05:56.265039+00:00 app[scheduler.5767]: % Total % Received % Xferd Average Speed Time Time Time Current 2018-03-01T15:05:56.699372+00:00 heroku[scheduler.5767]: State changed from up to complete (略)
エラーは出しているものの、叩けているのでこれでOK
アプリが起動しているかスリープしているかはアプリのアイコンを見ればわかる - https://blog.heroku.com/app_sleeping_on_heroku
2. アプリの名前を変える
actions on googleから変更する アプリとして登録するために、アプリ用のアイコンの画像が必要だったりするので結構めんどい そして、アプリ用の画像の判定が厳しすぎて辛い・・・
アプリの名前は簡単に「メモ帳」とした
これで、以下の言い方でつながる様になった
呼び出し方1
「メモ帳につないで」
↓
「はい、メモ帳のテストバージョンです。こんにちは」
↓
「メモを読んで」
↓
直近のメモが読まれる
呼び出し方2
上の方法だと冗長なので、以下のような言い方で一度に実行することもできる
「メモ帳を使ってメモを読んで」
↓
「はい、メモ帳のテストバーンジョンです。こんにちは」
↓
直近のメモが読まれる
3. ショートカットを作って呼び出しを簡単にする
GoogleHomeでショートカットを作成することで、「メモ帳を使ってメモを読んで」をもっと簡略化した言い方で済むようにする
ショートカットの言葉 - 「メモを読んで」 - 「メモ読んで」
↓
実際に実行される命令 - 「メモ帳を使ってメモを読んで」
4. Herokuのアプリの名前を変更する
- ずっとherokuが自動で発行したアプリ名「aqueous-peak-42683 」を使っていたので、これをわかりやすい名前に変える
変え方参考 - https://devcenter.heroku.com/articles/renaming-apps
$heroku apps:rename 新しいアプリ名
合わせて、dialogflowのFulfillmentで呼び出すURLと、HerokuのスケジューラでcurlするURLを変える必要があるので注意
【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の最後のデータがブラウザに表示された
【GoogleHomeでメモ帳アプリを作る】4. IFTTTを使ってSpreadSheetにメモを入力
概要
前回までで色々と必要な知識を得て、準備が整ったのでこれから実際に必要な機能を作っていく
- Spreadsheetに記録する部分の作成
- Ok Google ~でSpreadSheetにメモを記録する
- これはIFTTTを使うと便利
- ここで記録したSpreadSheetの内容を次回以降のherokuアプリから呼び出す
資料全体の構成はここに記載: GoogleHomeに話しかけてメモを記録したりメモを読み上げてもらう
IFTTTとGoogleSpreadsheet
IFTTT
アプリの作成
https://ifttt.com/discover - New Appletを選択
if thisの部分
あとから編集した画面なので、初期作成時とは背景の色が違うが、記入内容は同じ
then thisの部分
IFTTT注意
CreatedAtが効かないという問題があるらしい
- https://do-gugan.com/~furuta/archives/2017/10/google-home_spreadsheet.html
- https://qiita.com/miso_develop/items/be562d8a823ad2639d94
IFTTTを試す
GoogleSpreadSheet
IFTTTによって作成されたシートを確認
IFTTTをつくったあとは スプレッドシートが以下のように作られる
GASを追加
上のGoogleHomeに喋って作成したテキストの左の2列に日付を自動で挿入されるようにしたい
「ツール」→「スクリプト エディタ」からGAS(Google Apps Script)を作成する
参考 https://so-zou.jp/web-app/tech/programming/javascript/grammar/object/date.htm
一応プログラムで取得しやすいことを考えてUnixTimeも出すようにしておいた
function addDate() { var sheet = SpreadsheetApp.getActiveSheet(); var lastRow = sheet.getLastRow(); var date = new Date(); var unixTimestamp = Math.round( date.getTime() / 1000 ); sheet.getRange(lastRow, 1).setValue(unixTimestamp); sheet.getRange(lastRow, 2).setValue(date); }
もし1行目に日付と時刻を自動で入れるようにしたければunixTimestampの部分は不要で、以下だけでいい
sheet.getRange(lastRow, 1).setValue(date);
プロジェクト名を決める
時計の形をしたアイコンをクリックして、
プロジェクトのトリガーとして
- 実行:addDate
- イベント:スプレッドシートから 値の変更
【2019/05/13追記】 トリガーの作成方法が変わっていた。以下で上と同じことになる
ためしにデータを追加してみる
「test」「test2」を記入すると、GASによって、隣に勝手に時刻が記載された
【GoogleHomeでメモ帳アプリを作る】3. 実際にHerokuでアプリを作ってみる
概要
herokuやDjangoの公式のチュートリアルをやったけど、自分で一から作るとなると色々わからなかったので理解しながら作る
資料全体の構成はここに記載: GoogleHomeに話しかけてメモを記録したりメモを読み上げてもらう
この資料はGoogleHome用のアプリとは直接関係ないのでほとんど飛ばしていいことしか書いていない
herokuのチュートリアル
チュートリアルと同じように自分でも作ってみる
必要なファイルや設定だけをチュートリアルから取って来ることにする
参考 各種説明 - https://devcenter.heroku.com/articles/deploying-python
作る手順
この順序にした理由は後述のProcfileとWebアプリを同じディレクトリ内に置く必要があるため
Djangoアプリの作成
まずDjangoアプリを作成する - 別にDjangoではなくてflaskとかでもいいとは思うが、チュートリアルがDjangoなので真似して作ってみる
Django参考 - https://docs.djangoproject.com/ja/2.0/intro/tutorial01/#creating-a-project
【Django】プロジェクト作成
プロジェクト名とパッケージ名は以下のコマンドだとどちらもgooglehomeになる
[~/git/ludwig125-heroku] $django-admin startproject googlehome [~/git/ludwig125-heroku] $tree (git)-[master] . ── googlehome ├── googlehome │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── manage.py 2 directories, 9 files [~/git/ludwig125-heroku] $
【Django】アプリ作成
試しに適当なアプリケーションを作ってみる
参考 - https://docs.djangoproject.com/ja/2.0/intro/tutorial01/#write-your-first-view
helloアプリの作成
作るアプリケーション: hello
- 作り方
python manage.py startapp hello
[~/git/ludwig125-heroku/googlehome] $python manage.py startapp hello [~/git/ludwig125-heroku/googlehome] $tree (git)-[master] . ├── db.sqlite3 ├── googlehome │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-36.pyc │ │ ├── settings.cpython-36.pyc │ │ ├── urls.cpython-36.pyc │ │ └── wsgi.cpython-36.pyc │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── hello │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py └── manage.py 4 directories, 17 files [~/git/ludwig125-heroku/googlehome] $
view.pyの作成
view.pyを編集
[~/git/ludwig125-heroku/googlehome] $cat hello/views.py (git)-[master]
from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the hello index.")
urls.pyの作成
https://docs.djangoproject.com/ja/2.0/intro/tutorial01/#write-your-first-view
ビューを呼ぶために、 URL を対応付けしてやる必要があります。そのためには URLconf が必要です polls ディレクトリに URLconf を作るには urls.py というファイルを作ります。
これを参考にurls.pyを作成する
[~/git/ludwig125-heroku/googlehome] $cat hello/urls.py (git)-[master]
from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), ]
ルートのurls.pyの修正
次のステップはルートのURLconfに polls.urls モジュールの記述を反映させることです。
ここも同様に修正する
[~/git/ludwig125-heroku/googlehome] $cat googlehome/urls.py
from django.contrib import admin from django.urls import include, path urlpatterns = [ path('hello/', include('hello.urls')), ←追加 path('admin/', admin.site.urls), ]
【Django】ローカルでWebアプリ起動
ローカルでWebサーバ立ち上げ
ここまでやったら以下でWebサーバを立てる
[~/git/ludwig125-heroku/googlehome] $python manage.py runserver Performing system checks... System check identified no issues (0 silenced). You have 14 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions. Run 'python manage.py migrate' to apply them. February 03, 2018 - 15:24:45 Django version 2.0, using settings 'googlehome.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C. [03/Feb/2018 15:24:59] "GET /hello HTTP/1.1" 301 0 [03/Feb/2018 15:24:59] "GET /hello/ HTTP/1.1" 200 40
以下をブラウザから見てみる
http://127.0.0.1:8000/hello/ ↓ 「Hello, world. You're at the hello index.」が表示された
ここまででDjangoとしてするべきことはやった 次はこれをherokuで開くために色々やることがある
herokuアプリの作成
【heroku】アプリのcreate
Djangoプロジェクトを作成したときの外側のgooglehome直下でherokuアプリを作成する
git 初期化
[~/git/ludwig125-heroku/googlehome] $git init (git)-[master] Initialized empty Git repository in /home/ludwig125/git/ludwig125-heroku/googlehome/.git/ [~/git/ludwig125-heroku/googlehome] $
heroku create
[~/git/ludwig125-heroku/googlehome] $heroku create (git)-[master] ▸ heroku-cli: update available from 6.14.43-73d5876 to 6.15.22-3f1c4bd Creating app... done, ⬢ aqueous-peak-42683 https://aqueous-peak-42683.herokuapp.com/ | https://git.heroku.com/aqueous-peak-42683.git [~/git/ludwig125-heroku/googlehome] $ (git)-[master]
何も指定しないと適当なアプリ名があてがわれる
【heroku】Pipfileとrequirements.txt
以下のファイルだけをとりあえず公式のチュートリアル用リポジトリから持ってきてスタートする https://github.com/heroku/python-getting-started
- requirements.txt
- Pipfile
なぜこの2ファイルかは以下の理由
https://devcenter.heroku.com/articles/getting-started-with-python#declare-app-dependencies
Heroku recognizes an app as a Python app by the existence of a Pipfile or requirements.txt file in the root directory.
herokuでは、Pipfileかrequirements.txtがherokuのルートディレクトリにあると、Pythonアプリだと認識してくれるらしい
[~/git/ludwig125-heroku/googlehome] $cat requirements.txt (git)-[master] certifi==2017.11.5 chardet==3.0.4 Django==2.0 flake8==3.5.0 idna==2.6 mccabe==0.6.1 pew==1.1.2 pipenv==9.0.1 pycodestyle==2.3.1 pyflakes==1.6.0 pytz==2017.3 requests==2.18.4 urllib3==1.22 virtualenv==15.1.0 virtualenv-clone==0.2.6 [~/git/ludwig125-heroku/googlehome] $
[~/git/ludwig125-heroku/googlehome] $cat Pipfile (git)-[master] [[source]] url = "https://pypi.python.org/simple" verify_ssl = true [packages] django = "*" gunicorn = "*" django-heroku = "*" requests = "*" [requires] python_version = "3.6" [~/git/ludwig125-heroku/googlehome] $
【heroku】pipenv実行
pipenv install を実行
Pipfileの内容でインストールする:
参考 - http://pipenv-ja.readthedocs.io/ja/translate-ja/basics.html - http://pipenv-ja.readthedocs.io/ja/translate-ja/#other-commands
[~/git/ludwig125-herokuu/googlehome] $pipenv install (git)-[master] Creating a virtualenv for this project… Using /home/ludwig125/.pyenv/versions/anaconda3-4.4.0/bin/python3.6m to create virtualenv… ⠋Running virtualenv with interpreter /home/ludwig125/.pyenv/versions/anaconda3-4.4.0/bin/python3.6m Using base prefix '/home/ludwig125/.pyenv/versions/anaconda3-4.4.0' New python executable in /home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov/bin/python3.6m Also creating executable in /home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov/bin/python /home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov/bin/python3.6m: error while loading shared libraries: libpython3.6m.so.1.0: cannot open shared object file: No such file or directory ERROR: The executable /home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov/bin/python3.6m is not functioning ERROR: It thinks sys.prefix is '/home/ludwig125/git/ludwig125-heroku' (should be '/home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov') ERROR: virtualenv is not compatible with this system or executable Virtualenv location: Warning: Your Pipfile requires python_version 3.6, but you are using None (/bin/python). $ pipenv check will surely fail. Creating a virtualenv for this project… ⠙Using base prefix '/home/ludwig125/.pyenv/versions/3.6.3' New python executable in /home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov/bin/python3.6 Also creating executable in /home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov/bin/python Installing setuptools, pip, wheel...done. Virtualenv location: /home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov Pipfile.lock not found, creating… Locking [dev-packages] dependencies… Locking [packages] dependencies… Updated Pipfile.lock (c8a67b)! Installing dependencies from Pipfile.lock (c8a67b)… � ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 12/12 — 00:00:14 To activate this project's virtualenv, run the following: $ pipenv shell [~/git/ludwig125-herokuu/googlehome] $
soファイルが読み込めずにエラーが出ているが動いてはいるみたい
pipenv shellを実行
http://pipenv-ja.readthedocs.io/ja/translate-ja/#other-commands
shell は仮想環境が有効化されたシェルを起動します
check はセキュリティの脆弱性をチェックし、現在の環境がPEP 508の要求仕様を満たしていることを表明します。
[~/git/ludwig125-herokuu/googlehome] $pipenv check (git)-[master] Checking PEP 508 requirements… Passed! Checking installed package safety… All good! [~/git/ludwig125-herokuu/googlehome] $
[~/git/ludwig125-herokuu/googlehome] $pipenv shell (git)-[master] Spawning environment shell (/usr/bin/zsh). Use 'exit' to leave. source /home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov/bin/activate [~/git/ludwig125-heroku] $source /home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov/bin/activate (googlehome-Z81pgPAi) [~/git/ludwig125-heroku] $
【heroku】Procfile
Profileとは - アプリのスタート時に動くファイルを定義するもの https://devcenter.heroku.com/articles/getting-started-with-python#define-a-procfile
Procfileについて
https://devcenter.heroku.com/articles/deploying-python#the-procfile
A Procfile is a text file in the root directory of your application that defines process types and explicitly declares what command should be executed to start your app.
Your Procfile will look something like this:
web: gunicorn gettingstarted.wsgi --log-file -
チュートリアルのProcfileも同じような感じだった https://github.com/heroku/python-getting-started/blob/master/Procfile
web: gunicorn gettingstarted.wsgi
Procfileの各項目の説明
web: gunicorn gettingstarted.wsgi
web
web, is important here. It declares that this process type will be attached to the HTTP routing stack of Heroku, and receive web traffic when deployed.
log-file
It means "log to stdout". The --log-file flag lets you set a path to a log file, and - means "stdout" (in this context).
参考
- https://stackoverflow.com/questions/26129020/what-does-log-file-mean-in-heroku-procfile
gunicorn gettingstarted.wsgi
- Gunicornはwsgiサーバのことらしい
- WSGI: Web Server Gateway Interface
- https://ja.wikipedia.org/wiki/Web_Server_Gateway_Interface
- プログラミング言語Pythonにおいて、WebサーバとWebアプリケーション(もしくはWebアプリケーションフレームワーク)を接続するための、標準化されたインタフェース定義である。
上のProcfileでは、チュートリアルのアプリケーション名が以下の通りgettingstarted なのでこう書いてあるが、自分のアプリケーションを作るのであれば直さなければならない - https://github.com/heroku/python-getting-started/tree/master/gettingstarted
自分のアプリケーション用にProcfileを作成する
今回はGoogleHome用のサーバを立てたいので、googlehomeという名前を指定する
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $cat Procfile (git)-[master] web: gunicorn googlehome.wsgi --log-file - (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $
注意点
- herokuのProcfileは、Webアプリと同じディレクトリにないと認識してくれないみたい
最初は自分は ~/git/ludwig125-heroku/ 以下にProcfileを置いて、その下にDjangoアプリを作成した
そのために、Procfileがあるのは~/git/ludwig125-heroku/googlehomeで、Djangoのアプリケーション本体があるのは一段下のディレクトリになってしまった
以下のように、Procfileとアプリケーションを同じディレクトリに置く必要がある
(ludwig125-heroku-G7FbBSov) [~/git/ludwig125-heroku/googlehome] $ls (git)-[master] Pipfile Procfile db.sqlite3 googlehome/ hello/ manage.py* requirements.txt staticfiles/ (ludwig125-heroku-G7FbBSov) [~/git/ludwig125-heroku/googlehome] $git status (git)-[master]
最初、Procfileを外側の「googlehome」ディレクトリより上に置いてherokuを立ち上げようとしたら全くうまく行かなかった
(ludwig125-heroku-G7FbBSov) [~/git/ludwig125-heroku] $heroku local web (git)-[master] ▸ heroku-cli: update available from 6.14.43-73d5876 to 6.15.22-3f1c4bd [OKAY] Loaded ENV .env File as KEY=VALUE Format 00:28:35 web.1 | [2018-02-04 00:28:35 +0900] [19199] [INFO] Starting gunicorn 19.7.1 00:28:35 web.1 | [2018-02-04 00:28:35 +0900] [19199] [INFO] Listening at: http://0.0.0.0:5000 (19199) 00:28:35 web.1 | [2018-02-04 00:28:35 +0900] [19199] [INFO] Using worker: sync 00:28:35 web.1 | [2018-02-04 00:28:35 +0900] [19202] [INFO] Booting worker with pid: 19202 00:28:35 web.1 | [2018-02-04 00:28:35 +0900] [19202] [ERROR] Exception in worker process 00:28:35 web.1 | Traceback (most recent call last): 00:28:35 web.1 | File "/home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov/lib/python3.6/site-packages/gunicorn/arbiter.py", line 578, in spawn_worker 00:28:35 web.1 | worker.init_process() 00:28:35 web.1 | File "/home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov/lib/python3.6/site-packages/gunicorn/workers/base.py", line 126, in init_process 00:28:35 web.1 | self.load_wsgi() 00:28:35 web.1 | File "/home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov/lib/python3.6/site-packages/gunicorn/workers/base.py", line 135, in load_wsgi 00:28:35 web.1 | self.wsgi = self.app.wsgi() 00:28:35 web.1 | File "/home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov/lib/python3.6/site-packages/gunicorn/app/base.py", line 67, in wsgi 00:28:35 web.1 | self.callable = self.load() 00:28:35 web.1 | File "/home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 65, in load 00:28:35 web.1 | return self.load_wsgiapp() 00:28:35 web.1 | File "/home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 52, in load_wsgiapp 00:28:35 web.1 | return util.import_app(self.app_uri) 00:28:35 web.1 | File "/home/ludwig125/.local/share/virtualenvs/ludwig125-heroku-G7FbBSov/lib/python3.6/site-packages/gunicorn/util.py", line 352, in import_app 00:28:35 web.1 | __import__(module) 00:28:35 web.1 | ModuleNotFoundError: No module named 'googlehome.wsgi' 00:28:35 web.1 | [2018-02-04 00:28:35 +0900] [19202] [INFO] Worker exiting (pid: 19202) 00:28:35 web.1 | [2018-02-04 00:28:35 +0900] [19199] [INFO] Shutting down: Master 00:28:35 web.1 | [2018-02-04 00:28:35 +0900] [19199] [INFO] Reason: Worker failed to boot. 00:28:35 web.1 Exited with exit code 3 (ludwig125-heroku-G7FbBSov) [~/git/ludwig125-heroku] $
→ ModuleNotFoundError: No module named 'googlehome.wsgi'
【heroku】heroku用にDjangoを少し直す
Djangoをherokuで動かすためには以下の設定が必要
https://devcenter-assets0.herokucdn.com/articles/django-memcache#configure-django-for-heroku
Django requires some special configuration in order to work on Heroku, mainly for the database to work and the static files to be served. Luckily, there is adjango-heroku package that takes care of all that. So on the bottom of the file django_queue/settings.py add the following lines:
# Configure Django App for Heroku. import django_heroku django_heroku.settings(locals())
ということで、hello/view.pyを以下のように修正する
from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the hello index.")
【heroku】herokuアプリをローカルで立ち上げる
heroku local web
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $heroku local web (git)-[master] ▸ heroku-cli: update available from 6.14.43-73d5876 to 6.15.22-3f1c4bd [OKAY] Loaded ENV .env File as KEY=VALUE Format 22:55:23 web.1 | [2018-02-04 22:55:23 +0900] [11851] [INFO] Starting gunicorn 19.7.1 22:55:23 web.1 | [2018-02-04 22:55:23 +0900] [11851] [INFO] Listening at: http://0.0.0.0:5000 (11851) 22:55:23 web.1 | [2018-02-04 22:55:23 +0900] [11851] [INFO] Using worker: sync 22:55:23 web.1 | [2018-02-04 22:55:23 +0900] [11854] [INFO] Booting worker with pid: 11854
ローカルのブラウザでは見られるようになった http://0.0.0.0:5000/hello/ Hello, world. You're at the hello index.
.envの追加
上のheroku local webを最初にやったときは以下のWARNが出た
(ludwig125-heroku-G7FbBSov) [~/git/ludwig125-heroku] $heroku local web (git)-[master] ▸ heroku-cli: update available from 6.14.43-73d5876 to 6.15.22-3f1c4bd [WARN] No ENV file found 23:27:09 web.1 | [2018-02-03 23:27:09 +0900] [14824] [INFO] Starting gunicorn 19.7.1 23:27:10 web.1 | [2018-02-03 23:27:09 +0900] [14824] [INFO] Listening at: http://0.0.0.0:5000 (14 824)
→ 「[WARN] No ENV file found」 そこで、公式のチュートリアルから.envをコピーした - https://github.com/heroku/python-getting-started/blob/master/.env
参考 - https://stackoverflow.com/questions/37690780/heroku-local-warn-no-env-file-found
(ludwig125-heroku-G7FbBSov) [~/git/ludwig125-heroku] $cat .env (git)-[master] TIMES=2 (ludwig125-heroku-G7FbBSov) [~/git/ludwig125-heroku] $
【heroku】herokuのリモートにデプロイ
この状態でherokuにデプロイしてみよう
(ludwig125-heroku-G7FbBSov) [~/git/ludwig125-heroku/googlehome] $git add . (git)-[master] (ludwig125-heroku-G7FbBSov) [~/git/ludwig125-heroku/googlehome] $git commit -m 'new heroku app' . (git)-[master] [master (root-commit) a80e0db] new heroku app 489 files changed, 55618 insertions(+) create mode 100644 .gitignore create mode 100644 Pipfile create mode 100644 Procfile create mode 100644 db.sqlite3 略 create mode 100644 staticfiles/staticfiles.json (ludwig125-heroku-G7FbBSov) [~/git/ludwig125-heroku/googlehome] $ (git)-[master]
(ludwig125-heroku-G7FbBSov) [~/git/ludwig125-heroku/googlehome] $git push heroku master (git)-[master] Counting objects: 280, done. Compressing objects: 100% (274/274), done. Writing objects: 100% (280/280), 871.41 KiB | 0 bytes/s, done. Total 280 (delta 44), reused 0 (delta 0) remote: Compressing source files... done. remote: Building source: remote: remote: -----> Python app detected remote: ! No 'Pipfile.lock' found! We recommend you commit this into your repository. remote: -----> Installing python-3.6.4 remote: -----> Installing pip remote: -----> Installing requirements with pip remote: Collecting certifi==2017.11.5 (from -r /tmp/build_bee926373efffef7cab46947c96c8691/requirements.txt (line 1)) (中略) remote: File "<frozen importlib._bootstrap_external>", line 678, in exec_module remote: File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed remote: File "/tmp/build_bee926373efffef7cab46947c96c8691/googlehome/settings.py", line 14, in <module> remote: import django_heroku remote: ModuleNotFoundError: No module named 'django_heroku' remote: remote: ! Error while running '$ python manage.py collectstatic --noinput'. remote: See traceback above for details. remote: remote: You may need to update application code to resolve this error. remote: Or, you can disable collectstatic for this application: remote: remote: $ heroku config:set DISABLE_COLLECTSTATIC=1 remote: remote: https://devcenter.heroku.com/articles/django-assets remote: ! Push rejected, failed to compile Python app. remote: remote: ! Push failed remote: Verifying deploy... remote: remote: ! Push rejected to aqueous-peak-42683. remote: To https://git.heroku.com/aqueous-peak-42683.git ! [remote rejected] master -> master (pre-receive hook declined) error: failed to push some refs to 'https://git.heroku.com/aqueous-peak-42683.git' (ludwig125-heroku-G7FbBSov) [~/git/ludwig125-heroku/googlehome] $
remote: $ heroku config:set DISABLE_COLLECTSTATIC=1 と出ている
collectstatic は不要なので以下をする
$ heroku config:set DISABLE_COLLECTSTATIC=1
参考 - https://devcenter.heroku.com/articles/django-assets#disabling-collectstatic - http://thinkami.hatenablog.com/entry/2017/01/13/053643
(ludwig125-heroku-G7FbBSov) [~/git/ludwig125-heroku/googlehome] $heroku config:set DISABLE_COLLECTSTATIC=1 (git)-[master] ▸ heroku-cli: update available from 6.14.43-73d5876 to 6.15.22-3f1c4bd Setting DISABLE_COLLECTSTATIC and restarting ⬢ aqueous-peak-42683... done, v3 DISABLE_COLLECTSTATIC: 1 (ludwig125-heroku-G7FbBSov) [~/git/ludwig125-heroku/googlehome] $git push heroku master (git)-[master] Counting objects: 280, done. Compressing objects: 100% (274/274), done. Writing objects: 100% (280/280), 871.41 KiB | 0 bytes/s, done. Total 280 (delta 44), reused 0 (delta 0) remote: Compressing source files... done. remote: Building source: remote: remote: -----> Python app detected (中略) remote: Released v5 remote: https://aqueous-peak-42683.herokuapp.com/ deployed to Heroku remote: remote: Verifying deploy... done. To https://git.heroku.com/aqueous-peak-42683.git * [new branch] master -> master (ludwig125-heroku-G7FbBSov) [~/git/ludwig125-heroku/googlehome] $
pushできた!
heroku open とすると以下のURLでブラウザが開く https://aqueous-peak-42683.herokuapp.com/
「Application error」
ログの確認
困ったときはheroku logsでログを確認する
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $heroku logs (git)-[master] ▸ heroku-cli: update available from 6.14.43-73d5876 to 6.15.22-3f1c4bd 2018-02-04T13:55:51.472389+00:00 app[web.1]: self.callable = self.load() 2018-02-04T13:55:51.472390+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 65, in load (中略) 2018-02-04T13:55:51.416412+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/django/conf/__init__.py", line 106, in __init__ 2018-02-04T13:55:51.416414+00:00 app[web.1]: mod = importlib.import_module(self.SETTINGS_MODULE) 2018-02-04T13:55:51.416416+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/importlib/__init__.py", line 126, in import_module 2018-02-04T13:55:51.416417+00:00 app[web.1]: return _bootstrap._gcd_import(name[level:], package, level) 2018-02-04T13:55:51.416419+00:00 app[web.1]: File "/app/googlehome/settings.py", line 14, in <module> 2018-02-04T13:55:51.416420+00:00 app[web.1]: import django_heroku 2018-02-04T13:55:51.416430+00:00 app[web.1]: ModuleNotFoundError: No module named 'django_heroku' 2018-02-04T13:55:51.416774+00:00 app[web.1]: [2018-02-04 13:55:51 +0000] [8] [INFO] Worker exiting (pid: 8) 2018-02-04T14:00:20.701204+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=aqueous-peak-42683.herokuapp.com request_id=58027f22-e4fc-43b9-9bbf-eb4014918628 fwd="60.76.227.16" dyno= connect= service= status=503 bytes= protocol=https 2018-02-04T14:00:21.453798+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/favicon.ico" host=aqueous-peak-42683.herokuapp.com request_id=97dbadbc-8864-4be0-852a-a3a2178ac7a2 fwd="60.76.227.16" dyno= connect= service= status=503 bytes= protocol=https (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $
import django_heroku ModuleNotFoundError: No module named 'django_heroku'
requirements.txtとローカルのパッケージを比較する
ローカルのパッケージ
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $pip freeze > pip_packages (git)-[master] (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $cat pip_packages (git)-[master] certifi==2018.1.18 chardet==3.0.4 dj-database-url==0.4.2 Django==2.0.2 django-heroku==0.2.0 gunicorn==19.7.1 idna==2.6 psycopg2==2.7.3.2 pytz==2017.3 requests==2.18.4 urllib3==1.22 whitenoise==3.3.1 (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $
一方requirements.txt
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $cat requirements.txt (git)-[master] certifi==2017.11.5 chardet==3.0.4 Django==2.0 flake8==3.5.0 gunicorn==19.7.1 idna==2.6 mccabe==0.6.1 pew==1.1.2 pipenv==9.0.1 pycodestyle==2.3.1 pyflakes==1.6.0 pytz==2017.3 requests==2.18.4 urllib3==1.22 virtualenv==15.1.0 virtualenv-clone==0.2.6 (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $
django-heroku==0.2.0がrequirements.txtに無かった!
このように、ローカルのパッケージと、リモートのパッケージが異なっていてうまく動かない場合があるので、 pip freezeとrequirements.txtを比較するのは大事
改めてgit push heroku master
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $git diff (git)-[master] diff --git a/requirements.txt b/requirements.txt index 468f0a3..50e7c0a 100644 @@ -1,6 +1,7 @@ certifi==2017.11.5 chardet==3.0.4 Django==2.0 +django-heroku==0.2.0 flake8==3.5.0 gunicorn==19.7.1 idna==2.6 (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $git commit -m 'add django-heroku==0.2.0' requirements.txt [master 7dffb4c] add django-heroku==0.2.0 1 file changed, 1 insertion(+) (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $git push heroku master (git)-[master] Counting objects: 5, done. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 316 bytes | 0 bytes/s, done. Total 3 (delta 2), reused 0 (delta 0) remote: Compressing source files... done. remote: Building source: remote: remote: -----> Python app detected remote: ! No 'Pipfile.lock' found! We recommend you commit this into your repository. remote: -----> Installing requirements with pip remote: Collecting django-heroku==0.2.0 (from -r /tmp/build_e5ffa088d26050c45e7abaa595a50b1f/requirements.txt (line 4)) remote: Downloading django_heroku-0.2.0-py2.py3-none-any.whl remote: Collecting dj-database-url (from django-heroku==0.2.0->-r /tmp/build_e5ffa088d26050c45e7abaa595a50b1f/requirements.txt (line 4)) (中略) remote: https://aqueous-peak-42683.herokuapp.com/ deployed to Heroku remote: remote: Verifying deploy... done. To https://git.heroku.com/aqueous-peak-42683.git 558d762..7dffb4c master -> master (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $
→ 「No 'Pipfile.lock' found! We recommend you commit this into your repository.」 Pipfile.lock がないというメッセージが出ているので追加しておく
Pipfile.lock とは
http://pipenv-ja.readthedocs.io/ja/translate-ja/basics.html#pipfile-lock-security-features
Pipfile.lock は pip に新しく入った偉大なセキュリティ上の改善を利用します。 デフォルトでは、 Pipfile.lock はダウンロードされたパッケージごとのsha256ハッシュから生成されます。 これにより pip は、ネットワークが驚異に晒されていたり、信頼できないPyPIエンドポイントから依存パッケージをインストールしたりしているときに、意図したとおりのパッケージをインストールしていることを保証できます。 開発環境から本番環境へプロジェクトを昇格させる方法でデプロイに取り組むことを強く推奨します。 pipenv lock を使って開発環境の依存関係をコンパイルし、そのコンパイルされた Pipfile.lock を全ての本番環境にデプロイして再現可能なビルドができます。
追加してからgit push
(googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $git push heroku master (git)-[master] Counting objects: 4, done. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 3.29 KiB | 0 bytes/s, done. Total 3 (delta 1), reused 0 (delta 0) remote: Compressing source files... done. remote: Building source: remote: remote: -----> Python app detected remote: ! The latest version of Python 3 is python-3.6.4 (you are using python-3.6.3, which is unsupported). remote: ! We recommend upgrading by specifying the latest version (python-3.6.4). remote: Learn More: https://devcenter.heroku.com/articles/python-runtimes remote: -----> Found python-3.6.4, removing remote: -----> Installing python-3.6.3 remote: -----> Installing pip remote: -----> Installing requirements with pip remote: Collecting certifi==2017.11.5 (from -r /tmp/build_8601996728746a7313b6f8219d936dbb/requirements.txt (line 1)) (中略) remote: Collecting psycopg2 (from django-heroku==0.2.0->-r /tmp/build_8601996728746a7313b6f8219d936dbb/requirements.txt (line 4)) remote: Downloading psycopg2-2.7.3.2-cp36-cp36m-manylinux1_x86_64.whl (2.7MB) remote: Installing collected packages: certifi, chardet, pytz, Django, whitenoise, dj-database-url, psycopg2, django-heroku, mccabe, pyflakes, pycodestyle, flake8, gunicorn, idna, virtualenv, virtualenv-clone, pew, urllib3, requests, pipenv remote: Running setup.py install for virtualenv-clone: started remote: Running setup.py install for virtualenv-clone: finished with status 'done' remote: Running setup.py install for pipenv: started remote: Running setup.py install for pipenv: finished with status 'done' remote: Successfully installed Django-2.0 certifi-2017.11.5 chardet-3.0.4 dj-database-url-0.4.2 django-heroku-0.2.0 flake8-3.5.0 gunicorn-19.7.1 idna-2.6 mccabe-0.6.1 pew-1.1.2 pipenv-9.0.1 psycopg2-2.7.3.2 pycodestyle-2.3.1 pyflakes-1.6.0 pytz-2017.3 requests-2.18.4 urllib3-1.22 virtualenv-15.1.0 virtualenv-clone-0.2.6 whitenoise-3.3.1 remote: remote: -----> Discovering process types remote: Procfile declares types -> web remote: remote: -----> Compressing... remote: Done: 60.7M remote: -----> Launching... remote: Released v7 remote: https://aqueous-peak-42683.herokuapp.com/ deployed to Heroku remote: remote: Verifying deploy... done. To https://git.heroku.com/aqueous-peak-42683.git 7dffb4c..a11938f master -> master (googlehome-Z81pgPAi) [~/git/ludwig125-heroku/googlehome] $
これでもう一度 https://aqueous-peak-42683.herokuapp.com/hello/
→ 「Hello, world. You're at the hello index.」
表示された!
git のリモートにoriginを登録
githubでコードを見られるようにする 参考 - https://qiita.com/koshihikari/items/dcf126fa9c0de2b6fa7e
自分の設定では
[~/git/ludwig125-heroku/googlehome] $git remote -v (git)-[master] heroku https://git.heroku.com/aqueous-peak-42683.git (fetch) heroku https://git.heroku.com/aqueous-peak-42683.git (push) [~/git/ludwig125-heroku/googlehome] $c
herokuだけがリモートリポジトリとなっている
githubにこれを登録するために、 https://github.com/ からNew repositoryを押して、「googlehome」リポジトリを作成
そのリポジトリからURLをコピペして、以下のようにgit remote add origin
[~/git/ludwig125-heroku/googlehome] $git remote add origin git@github.com:ludwig125/googlehome.git [~/git/ludwig125-heroku/googlehome] $git branch (git)-[master] * master [~/git/ludwig125-heroku/googlehome] $git push origin master (git)-[master] Counting objects: 335, done. Compressing objects: 100% (326/326), done. Writing objects: 100% (335/335), 882.21 KiB | 0 bytes/s, done. Total 335 (delta 77), reused 0 (delta 0) remote: Resolving deltas: 100% (77/77), done. To git@github.com:ludwig125/googlehome.git * [new branch] master -> master [~/git/ludwig125-heroku/googlehome] $
追加後はこうなる
[~/git/ludwig125-heroku/googlehome] $git remote -v (git)-[master] heroku https://git.heroku.com/aqueous-peak-42683.git (fetch) heroku https://git.heroku.com/aqueous-peak-42683.git (push) origin git@github.com:ludwig125/googlehome.git (fetch) origin git@github.com:ludwig125/googlehome.git (push) [~/git/ludwig125-heroku/googlehome] $