医療通訳支援Webアプリ(The record system)にPlotlyから最近出たDashを使ってインタラクティブなグラフを追加した
以下のエントリーの続編です。
healthcareit-interpreter.hatenablog.com
herokuにデプロイしたページは以下になります。
https://englishtranslationrecord.herokuapp.com/
コードはGitHubにあげてあります。
①Dashのページのコード
②医療通訳支援Webアプリ(以下、Webアプリ)のコード
作成手順(概要)
1)Dashのページを作ってherokuにデプロイ
2)WebアプリにDashのページを埋め込む
という2段階になります。今回も前回に引き続き以下のページをベースに作業を進めました。
Making a Flask app using a PostgreSQL database and deploying to Heroku
それからDash用に以下も参照:
Dash User Guide and Documentation - Dash by Plotly
herokuに置いたDB(PostgreSQL)をDashのページとWebアプリとで共有する
参考にしたページ:
$ git init $ git add . $ git commit -m "initial commit" $ heroku create etr-dashapp $ git push heroku master
とした後に、データベースの手続きをします。ちなみにetr-dashappというのが今回新規作成するDashのページになります。今回はすでに展開してあったWebアプリ用のDBを使い回したいので、
$ heroku addons:attach englishtranslationrecord::DATABASE --app etr-dashapp $ heroku pg:promote postgresql-hexagonal-12345 --app etr-dashapp
とやってあげます。englishtranslationrecordが既存のWebアプリの名称ということになります。
postgresql-hexagonal-12345はダミーの名称です。herokuのページ行くと確認できる、すでに展開してあるDBの名称になります。
すでに作成済みのDBを使い回すので以下の処理(herokuにDBを新規作成)は不要です。
$ heroku run python >>> from app import db >>> db.create_all() >>> exit()
Procfileの中身
さて上記で、
$ git push heroku master
とやった時点でうまくいけば
https://etr-dashapp.herokuapp.com/
にDashのページが表示されるのですが、Procfileの書き方を間違えていたせいでHeroku deployment error H10 (App crashed)というエラーが出てしまいました。
↑ここにあるように
$ heroku restart
とか色々と試してみたのですがダメでした。頭を抱えた末にたどり着いたページに助けられました:
このページでは、Procfileの中身は以下のようになっていました。
web gunicorn run:server
うん? runというのはrun.pyのことか。serverというのはrun.py中に記載されたDashのインスタンスのことか。なら自分はdashapp.pyという名称にしていたので
web gunicorn dashapp:server
にしてみるか。動いた!jimmybowに感謝です。 でもよく見たら公式ページにもちゃんと書いてありました。
Procfile
web: gunicorn app:server
(Note that app refers to the filename app.py. server refers to the variable server inside that file).
Webアプリのデプロイのときは純flask製だったのでserverインスタンスではなくappインスタンスだった上、ファイル名称はapp.pyだったので
web: gunicorn app:app
だったとしてもよく分かっていなかったということが露呈した形になります。
app:appはapp.py:appインスタンスということだとようやく分かりました。
WebアプリにDashのページを埋め込む
さて話を本題に戻しましょう。Dashのページはflask本体のようにrender_templateメソッドを(現在のところ)使えないらしいので、
(参考:Dash itself won’t provide compatibility with Jinja templates.)
Webアプリ(flaskapp)がrender_templateするindex.html (ここではhome.html)などにiframeを組み込んで、herokuにデプロイしたDashのページのURLをsrcに指定してやる方法が一番簡単そうです。
ということで以下の修正をWebアプリ側に施しました。
<html> <head> </head> <body> <iframe frameborder='0' noresize='noresize' style='position: absolute; background: transparent; width: 100%; height:100%;' src="{{ iframe }}" frameborder="0" id="dash"></iframe> <script type="text/javascript"> window.onload = setInterval(function(){ document.getElementById("dash").src = document.getElementById("dash").src }, 60*1000); </script> </body> </html>
@app.route('/') def home(): iframe = 'https://etr-dashapp.herokuapp.com/' return render_template('home.html', iframe=iframe)
htmlファイルにはJavaScriptで60秒ごとにiframe部分限定のリロードをかけるように仕込んであります。 Dashのページはリロードする度にDBから最新データを取得するように関数化してあります。
def serve_layout(): getdata() return html.Div([ dcc.Dropdown( id='select-person', options=[{'label': i, 'value': i} for i in ffc.columns], multi=True, value=ffc.columns ), dcc.Graph( id='graph-with-range', animate=False ), dcc.Graph( id='my-table' ), dcc.RangeSlider( id='month-range', marks={str(i): i for i in ffc.index}, min=ffc.index.min(), max=ffc.index.max(), value=[ffc.index.min(), ffc.index.max()], step=None ) ]) app.layout = serve_layout
しかしDashのコードというのはPythonなのかJavaScriptなのか分からなくなりますね。(実際、HTMLとJavaScriptだけでインタラクティブなページを作ろうとするとこれほど短いコードでは済みませんが。)