医療通訳で汗だくになる医療情報技師のブログ

田舎の病院でも外国人患者急増中

医療通訳支援Webアプリ(The record system)にPlotlyから最近出たDashを使ってインタラクティブなグラフを追加した

以下のエントリーの続編です。

healthcareit-interpreter.hatenablog.com

herokuにデプロイしたページは以下になります。

https://englishtranslationrecord.herokuapp.com/f:id:HealthcareIT_interpreter:20170804005557p:plain

コードはGitHubにあげてあります。
①Dashのページのコード

github.com

医療通訳支援Webアプリ(以下、Webアプリ)のコード

github.com

作成手順(概要)

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アプリとで共有する

参考にしたページ:

devcenter.heroku.com

$ 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)というエラーが出てしまいました。

stackoverflow.com

↑ここにあるように

$ heroku restart

とか色々と試してみたのですがダメでした。頭を抱えた末にたどり着いたページに助けられました:

github.com

このページでは、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.)

community.plot.ly

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だけでインタラクティブなページを作ろうとするとこれほど短いコードでは済みませんが。)

matplotlibで複数のグラフを作ったが、上司に報告するときExcelに貼り付けるってどうよ

患者統計(医事統計)の数字がおかしいので調べてくれ

という上司の命令に従い、渋々これまでの月別帳票(月別に別個のフォルダに格納されている)をPython標準ライブラリのglobとosを使って爆速で一括取得し、openpyxl で特定のセルだけ横串にしてmatplotlibで数値の月別変遷を見える化、したものの、

出来上がった複数のpngファイルをExcelに貼り付けて報告

という最後の落ちで笑えない状態になりました。せっかくjupyter notebookで作業してるんだから最後までスマートに行きたいものです。Excelpngファイルを貼り付けって、結局手作業で画像の位置が微妙にずれたりして見た目にも美しくありません。

jupyter notebookからグラフを貼り付けたHTMLファイルを生成し、それで報告する

ここに同じような発想をしている人がいました。

qiita.com

スタンドアローンのHTMLファイルのソースコードを開いて、markdownで必要なテキストやデータテーブルを記載するというものです。これは素晴らしい! このスタンドアローンHTMLファイルをテンプレートにしてjupyter notebookからmatplotlibで生成したグラフをbase64形式にして貼り付ければ

Excel方眼紙を完全に駆逐

できます。pngファイルの残骸も発生することがありません。

ということでjupyter notebookに実装しました。

gistc542f40ad8221297a5c2436dbcceb410

matplotlibのグラフを任意の数だけを貼り付けたHTMLファイルがカレントディレクトリに生成されます。

f:id:HealthcareIT_interpreter:20170723171351p:plain

このファイルをエディタで開いてmarkdown記載領域に必要な説明文を追加すれば以下のようになります。う、美しい!

f:id:HealthcareIT_interpreter:20170723171358p:plain

はいサイナラExcel

OpenCVをWindows Server 2012 R2に入れてPythonで使う、だけの話なのに死ぬ思いした話

Windowsの場合、
結論から言うと、必要なものは、
・Anaconda3(前提)
・opencv3 (Pythonライブラリ)
OpenCV 3.2.0 (本体)
の3つだけです。cv2.pydのコピぺとか環境変数の追加とか一切必要ありません。

1. opencv3のインストー

$conda install --channel https://conda.anaconda.org/menpo opencv3

2. OpenCV 3.2.0 のダウンロード

github.com

3. opencv-3.2.0-vc14.exeを実行し、得られたファイルを任意の場所に格納する

以上。

参考ページ:

qiita.com ただし、カスケードファイルが
C:\Users[username]\Anaconda3\envs[envname]\Library\etc
に入っていると書いてありますが、私が見たところ空でした。(うん?この記事はもしかしてPythonのライブラリであるopencv3と本体のOpenCV(3.1)を混同していないか?)
ですので(カスケードファイルを取得するために(?))OpenCV本体が必要になります。
opencv-3.2.0-vc14.exe実行後取得できるファイルの格納先はCドライブ直下にしました。以下のようにカスケードファイルの絶対パスをコード中に書いてやれば使いたい放題です。

import cv2, sys, re

# 入力ファイルを指定 
if len(sys.argv) <= 1:
    print("no input file")
    quit()
image_file = sys.argv[1] 

# 出力ファイル名
output_file = re.sub(r'\.jpg|jpeg|png$', '-mosaic.jpg', image_file)
mosaic_rate = 30 

# カスケードファイルのパスを指定
cascade_file = "C:¥opencv¥sources¥data¥haarcascades¥haarcascade_frontalface_alt.xml"

# 画像の読み込み 
image = cv2.imread(image_file)
image_gs = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # グレイスケール変換

# 顔認識を実行 
cascade = cv2.CascadeClassifier(cascade_file)
face_list = cascade.detectMultiScale(image_gs,
    scaleFactor=1.1,
    minNeighbors=1,
    minSize=(100,100))

if len(face_list) == 0:
    print("no face")
    quit()

# 認識した部分にモザイクをかける 
print(face_list)
color = (0, 0, 255)
for (x,y,w,h) in face_list:
    # 顔を切り抜く 
    face_img = image[y:y+h, x:x+w]
    # 切り抜いた画像を指定倍率で縮小 
    face_img = cv2.resize(face_img, (w//mosaic_rate, h//mosaic_rate))
    # 縮小した画像を元のサイズに戻す 
    face_img = cv2.resize(face_img, (w, h), 
        interpolation=cv2.INTER_AREA)
    # 元の画像に貼り付ける 
    image[y:y+h, x:x+w] = face_img
# 描画結果をファイルに書き込む
cv2.imwrite(output_file, image)

今回もこの本に書いてあるコードを拝借してます。

以上、超シンプルなやり方以外にも色々とインストール方法はたくさんあるようで、今回はそんな多岐にわたるマニュアル(ブログ)に翻弄されて時間と労力を消費してしまいました。

実はmac だと

$ brew tap homebrew/science
$ brew install opencv
$ pip install opencv-python

の連続コンボだけですぐに使えます。非常にシンプルです。ところがWindowsだと色々と面倒な処置が必要なようです。特に私が今回最初に参照したページにはシステム環境編集のPathをいじれとか書いてある。

www.buildinsider.net

googleで「opencv install windows」で検索すると一番先頭に出てくるページですから参照しようとしたのも無理はありません。素直に従ってPathの内容をここに記載してある通り書き換えて、他のPathが全部消えてしまったとしても無理はありません。(←死ぬ思いしたのここ)

qiita.com

ここもひどいもんです。曰く、「opencv/build/python/2.7/x64/以下にあるcv2.pydファイルをクリップボードにコピーします。このファイルをWinPython(ここはAnacondaに読み替え)のモジュール管理フォルダに格納します。」

↓そもそも公式ページからして良くわかりません。どこからダウンロードすればいいのか。

opencv.org

Pythonのライブラリも名称がはっきりしません。opencv3なのかcv2なのか。使うときは

import cv2

ですが、conda listで見るとopencv3と表記されています。

先述の本にはWindowsでのインストールの方法が記載されていなかったんです。そのせいで大変な思いをしました。なんとかして欲しかったものです。

ベイジアン・フィルターを使って患者氏名情報だけで日本人か外国人かの判別をする

外国人患者数の集計を行う必要

があるのですが、電子カルテの患者プロファイルに国籍情報を入力していないため、手がかりになる情報が患者氏名だけ、という状況です。DWHから患者マスタを抽出し、Excelで氏名ソートをかけ、アルファベットとカタカナで表記されたものは外国人患者とする、という手作業が一番手軽な方法ですが、日本人でも常用漢字でない場合にカタカナで入力されていたり、結婚などで外国人名と日本人名が混合していたり、漢字名でも外国人(東アジア系)である場合等は全て抜け落ちてしまうので最後は目視確認ということになります。 こういう状況こそ機械学習の出番であることはすぐに分かったのですが、さてではどのような手法を取ればいいのか長らく不明でした。機械学習の教科書を見ていると、学習器にかけるためにはデータをベクトルにしないといけない、と前提条件みたく説明されていたので一時期はテキストをベクトル化するにはどうすればいいか本気で悩んでおりました。前回の医療情報学会(福井開催)でもMicrosoftパナソニックの人が機械学習をテーマにパネルディスカッションをしていたので思い切ってマイクの前に立って質問をぶつけて見ましたが具体的な回答は得られませんでした。 そうした中で手元にあった本にヒントを見つけました。

スパムメールを判別するために広く利用されているベイジアン・フィルター(ナイーブ・ベイズ分類器)であればテキスト情報をそのまま学習器にかけることができる!(正確には実装の中で単語出現回数とカテゴリ出現回数のベクトル化がなされます。)これならお手軽です。この本に紹介されていたベイジアン・フィルターの実装をそのまま拝借させていただきました。

import math, sys
from janome.tokenizer import Tokenizer # 形態素解析用

class BayesianFilter:
    """ ベイジアンフィルタ """
    def __init__(self):
        self.words = set() # 出現した単語を全て記録
        self.word_dict = {} # カテゴリごとの単語出現回数を記録
        self.category_dict = {} # カテゴリの出現回数を記録

    # 形態素解析を行う
    def split(self, text):
        result = []
        t = Tokenizer()
        malist = t.tokenize(text)
        for w in malist:
            sf = w.surface   # 区切られた単語そのまま 
            bf = w.base_form # 単語の基本形
            if bf == '' or bf == "*": bf = sf
            result.append(bf)
        return result

    # 単語とカテゴリを数える処理 
    def inc_word(self, word, category):
        # 単語をカテゴリに追加
        if not category in self.word_dict:
            self.word_dict[category] = {}
        if not word in self.word_dict[category]:
            self.word_dict[category][word] = 0
        self.word_dict[category][word] += 1
        self.words.add(word)
    def inc_category(self, category):
        # カテゴリを加算する
        if not category in self.category_dict:
            self.category_dict[category] = 0
        self.category_dict[category] += 1

    # テキストを学習する 
    def fit(self, text, category):
        """ テキストの学習 """
        word_list = self.split(text)
        for word in word_list:
            self.inc_word(word, category)
        self.inc_category(category)

    # カテゴリにおける単語リストのスコアを計算する 
    def score(self, words, category):
        score = math.log(self.category_prob(category))
        for word in words:
            score += math.log(self.word_prob(word, category))
        return score

    # テキストのカテゴリ分けを行う
    def predict(self, text):
        best_category = None
        max_score = -sys.maxsize 
        words = self.split(text)
        score_list = []
        for category in self.category_dict.keys():
            score = self.score(words, category)
            score_list.append((category, score))
            if score > max_score:
                max_score = score
                best_category = category
        return best_category, score_list

    # カテゴリ内の単語出現数を得る
    def get_word_count(self, word, category):
        if word in self.word_dict[category]:
            return self.word_dict[category][word]
        else:
            return 0

    # カテゴリ/総カテゴリを計算
    def category_prob(self, category):
        sum_categories = sum(self.category_dict.values())
        category_v = self.category_dict[category]
        return category_v / sum_categories
        
    # カテゴリ内の単語の出現率を計算 
    def word_prob(self, word, category):
        n = self.get_word_count(word, category) + 1
        d = sum(self.word_dict[category].values()) + len(self.words)
        return n / d

以上をbayes.pyとし、

from bayes import BayesienFilter

で呼び出します。bayes.pyはカレントディレクトリに置くことをお忘れなく。以下jupyter notebookをそのまま貼り付けます。

gist989cadcd1b7c8c3873b8ee67d4ec505d

まるで外国人患者をスパムのように扱う

という倫理観のかけらもない行い(笑)ですが楽をするためにはいたしかたありません。カテゴリは
- 外国人
- カタカナ外国人
- 漢字外国人
- 日本人
の4つとしました。カテゴリは幾つでも追加できます。初めは外国人と日本人の2択にしましたが、こうすると外国人カテゴリにアルファベット名外国人とカタカナ名外国人、漢字名外国人の3タイプが分類されることとなり、機械が混乱するのではないかと考え細分化することとしました。結果は見ての通りです。学習量が圧倒的に足りていないため精度はひどいものです。実際には患者マスタを使って学習させる計画です。患者マスタをアルファベット名、カタカナ名、漢字東アジア系、日本人系の4グループに分割し学習させます。学習量が多くなると結構な時間がかかりますがjsonファイルに学習結果を格納し、次回からはjsonファイル読み込みだけですぐに"強くてニューゲーム"が可能ですので助かります。
ところで

漢字東アジア系を目視確認以外でどうやって抽出するか

という問題がここで浮上します。WebスクレイピングWikipedia

漢姓 - Wikipedia

朝鮮人の姓の一覧 - Wikipedia

から漢姓(中国人姓)と朝鮮姓データを抜いてくることにしました。

gist58f7a2414250bfd924ff1fe69d4f96a0

朝鮮姓には一部日本人姓と共通のもの(例:岡田(カンジョン))がありましたのでそれらは除外とさせていただきました。桂、東、西、星、林もあります、こいつらどうするかな・・・。まあ、目視確認の手間は100%取り除くことはできない、ということでしょう。

Microsoft Translator APIを組み入れた翻訳Webアプリケーションを作成しました。

GitHubに公開しました。

github.com

以下GitHubからの転記です。

手順1:Microsoft Azureでサブスクリプションを購入(無料)

Microsoftアカウントを取得後、Azureのページに行けば何やかんや言われた通りにする。

手順2:サブスクリプションの中にTranslator API(200万文字/月まで無料)を導入

サブスクリプションのページから追加したいライブラリを選択して追加する。検索欄に"Microsoft Translator API"を入れるとCognitive Serviceの分類中からMicrosoft Translator APIを選択する画面に飛ぶので追加する。 なお、手順1〜手順2の途中でクレジットカード情報を入れる必要がある。無料だけど入れる必要あり。ここは思い切って入力しましょう。でないと先に進めません。

手順3:mstranslatorをインストー

PythonからTranslator APIを動かせるライブラリが無いか物色。 https://pypi.python.org/pypi/mstranslator これがPython3からでも動かせる良いライブラリの予感。

$ pip install mstranslator

で落としましょう。

動作確認作業:mstranslatorがちゃんと動くか検証

>>>from mstranslator import Translator
>>>translator = Translator("YOUR_ACCESS_KEY")
>>>original = "今朝の朝食はとんかつでした。"
>>>translator.translate(original, lang_from='ja', lang_to='en')
>>>'Breakfast this morning was the tonkatsu.'

うん、ちゃんと動いているようです。ちなみにYOUR_ACCESS_KEYにはTransralotr APIaccess keyが入ります。

手順4:実装(当レポジトリの内容)

入力フォームに日本語か英語を入力し、翻訳ボタンを押すと翻訳文が表示され、と同時にDBに翻訳前文と翻訳文がセットで保存されるという仕組みのWebアプリケーションを作成しました。詳細はコードを確認してください。

完成

Webブラウザ上でもちゃんと動くことが確認されました。後は翻訳文の文字サイズの拡大などインターフェースの改良が必要です。 f:id:HealthcareIT_interpreter:20170711213149p:plain f:id:HealthcareIT_interpreter:20170711213329p:plain f:id:HealthcareIT_interpreter:20170711213410p:plain
それからiOSアプリ,Androidアプリとして提供されているMicrosoft Translatorと当APIの翻訳精度の比較をして見ましたが、残念ながらAPIのほうが精度が劣ります。この精度の違いについては日本マイクロソフトの営業担当の人に直接問い合わせております。

Symfoware ODBC接続からChartJSで電子カルテデータをWebにリアルタイム表示するまで 〜院内データ見える化システムの開発〜

基本コンセプト

  1. Fujitsu Symfoware ODBCドライバをPythonで動かしてDWHの情報を取得
  2. 取得したデータをpandasで加工
  3. flask, jinja2でWebページを作成(ChartJSでグラフ描写、Ajaxを使ったリアルタイム更新)
  4. WebサーバとしてIISを使用してWebページ表示
  5. 以上全てを電子カルテネットワーク内でやる

環境

IISマネージャ 設定手順

1. wfastcgi.pyの移設

C:¥Python34¥Scripts¥wfastcgi.pyを所定のFlaskAppフォルダ内に移設する。
例)C:¥inetpub¥wwwroot¥myWebsite¥wfastcgi.py

2. モジュールマップの追加(追加するWebのツリー上で)

要求パス:*
モジュール: FastCgiModule
実行可能ファイル(オプション):“C:¥Program Files¥Anaconda3¥python.exe”|C:¥inetpub¥wwwroot¥myWebsite¥wfastcgi.py
名前: FlaskHandler(任意)
要求の制限…
 マップ
 □(チェックを外す)要求のマップ先が次の場合のみハンドラーを呼び出す
 動詞
  デフォルト
 アクセス
  デフォルト
→OK
(でFastCGIアプリケーションが最上位サーバのツリー上に作成される)

3. FastCGIの設定(最上位サーバのツリー上で)

1で作成されたFastCGIアプリケーションを編集する。(右クリック→編集)
環境変数をクリック
…をクリック
以下2点を追加する
- PYTHONPATH
→C:¥inetpub¥wwwroot¥myWebsite¥
- WSGI_HANDLER
→main.app
※Flaskアプリの名称がmain.pyであればmain.appとなる

4. Application Poolの設定

追加するWebサイトを担当するApplication Poolの設定を開き、IDをAdministratorに変更する

5. C:¥Program Filesのアクセス権限にIIS_IUSRを加える(もしかしたらこれは必要ないかも)

IISスタート!・・・エラーが出ました。。

1.の Fujitus Symfoware ODBCドライバをPythonで動かして・・・のところでいきなりコケました。Symfoware ODBCドライバが32bitであるのに対してPythonが64bitだったためアーキテクチャ不整合のエラーが出てしましました。32bit版Pythonを導入すべきでしたがもう手遅れ、というかここからやり直しをする気が起きませんでした。

じゃあ、どうするか。

仕方がないのでSymfoware ODBC接続をExcelで行うこととしました。Excelの外部接続機能を利用します。そしてPythonのpywin32というライブラリを使ってwin32com.clientというWindowsのモジュールにExcelの外部接続を更新するボタンを毎回押させてデータを最新状態に保ちます。

flaskでは上手くいった、しかしIISでは・・・

win32com.clientはflask単独では動きました。しかしIISに乗せた瞬間に動かなくなりました。

dcomcnfg.exeの設定をいじってみる

win32com.clientの操作権限がIISのDefaultAppPoolに付与されていないためIISからwin32com.clientを動かせない、ということらしいので、DCOM ConfigのMicrosoft Excel Applicationのプロパティからユーザ(DefaultAppPool)を追加してみました。 (IIS AppPool¥[Your Application Pool Identity]を加える。[Your Application Pool Identity]にはそのWebサイトを担当するApplication Pool Identityが入る。(例)DefaultAppPool)

stackoverflow.com

やっぱりダメだった

上記の対応でもIISからwin32com.clientは動かせませんでした。

諦めてflaskとwin32com.clientを切り離す

IIS内に置くflaskのプログラムからwin32com.clientの部分を削除することとしました。こうすることでExcelの更新は出来なくなりますがIISからでもWebページを正常に表示させることができるようになります。win32com.clientの部分はそれ単独でrefresh.pyとしてWindowsのタスクスケジューラで1分ごとにExcelの更新ボタン(RefresAll)を押すように仕込みます。タスクスケジューラの設定方法については後述します。

タスクスケジューラの設定方法

時間間隔について

新規にタスクを設定する場合、タスク実行の最小間隔は1日ということになります。まずはそれで最後まで設定してタスクを生成します。その上で生成されたタスクを右クリックし、トリガーの編集をします。そこで繰り返し間隔を1分間、継続時間を無期限に変更します。1分間が最小単位ということになります。なおタスクの開始設定は毎日ではなく、1日にします。(そうしないと日毎に同一タスクが重複して生成されてしまいます。)

Pythonファイルの設定方法

タスクスケジューラのプログラムの欄にpython.exeファイルを指定し、引数の欄にタスクスケジューラで実行させたいpythonファイルを指定します。
例) プログラム:C:¥Program Files¥Anaconda3¥python.exe
引数:C:¥mytask¥refresh.py
開始の欄には特に何も入れる必要はありません。

おっと、これではタスクが実行されるごとにpython.exeの黒画面が開いてしまいます。これでは他の作業の邪魔になってしまいます。よく調べたらpythonw.exeというものを使えばいちいち黒画面が開かないで済むようです。タスクスケジューラの設定を以下の通り変更します。

プログラム:C:¥Program Files¥Anaconda3¥pythonw.exe
引数:C:¥mytask¥refresh.pyw

Anaconda3の中にpythonw.exeファイルが入っていました。refresh.pyの拡張子を.pywに変更することも忘れずに。

これでいちいち黒画面が開かなくなりました。そうすると逆にちゃんと動いているのか不安になりますが、更新対象のExcelファイルが格納されたフォルダを開いて画面を凝視していると1分ごとに更新時間が最新のものに変更される様子が見て取れると思います。これで安心です。

Geventも加える

flask + IISでも問題ないのですが、Gevent+flaskでスピードが早くなるらしいので加えて見ました。よく分かりませんが、ガンダムのマグネットコーティングみたいなものだと勝手に解釈しました。ちなみにgevent-websocketも導入して見たかったのですが、IE9はWebsocketに対応していないようなのでやめました。

tech.guitarrapc.com

最後になぜChartJSなのか

PythonプログラマーとしてはJavaScriptのライブラリであるChartJSよりもPythonライブラリのBokehを使いたかったのですがBokehのグラフはIE9でうまく表示ができないのです。ChartJSはIE9でも問題なく動きます。ということでJavaScriptの軍門に下った、ということになります。くたばれIE9!

医療通訳支援Webアプリ(The record system)をHerokuにデプロイしました

はい徹夜してしまいました。こちらです↓ https://englishtranslationrecord.herokuapp.com 触ってみてください。パスワードとか特に設定しておりません。

GitHubにはコードを上げております。

github.com

いや〜、長い道のりでした。昨夜の10時から今朝6時までかかってしまいました。午後10時からプログラミングを始めるとろくなことがありません。こんな事ばかりしていると人生棒に振ってしまいそうです。 さて色んな山が出てきて一つ一つその場しのぎでやり過ごしてなんとかなったけどまた同じ事次やれと言われてできるか少し不安です。新しい知識を得たら反芻するために睡眠を取らなければなりません。

  • データベースをSQLite3からPostgreSQLに換装したけど、SQLite3に比べてflask_sqlalchemyはfetchall()からpandas.DataFrameにテーブル投げるのが下手

  • virtualenvの使い方(anacondaと標準Pythonの両方に入ってて最初うまくいかなかったけど、pip uninstall virtualenvやってanaconda一本にしておいてカレントディレクトリから

$ virtualenv venv
$ . venv/bin/activate

でうまく言った。今回は必要なライブラリだけpip installしたら後はpip freeze > requirements.txtするだけで後は用無しだった。 )

  • Herokuの環境だとmatplotlibが_tkinter使えなくなるので
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

としてやる必要があった。

$ heroku addons:add heroku-postgresql:dev

とやると「お前はselect民じゃないからダメだ」と言われるので

$ heroku addons:add heroku-postgresql:hobby-dev

と飽くまで趣味だと言い張る必要があった。

他にもあったと思うけどこの辺でやめとくか。爪が伸びて12インチMacbookのバタフライキーボードを打つのが辛い。そして本日は診療情報管理士通信教育後期スクーリング3日目。寝れない。

今回お世話になったページ↓

Making a Flask app using a PostgreSQL database and deploying to Heroku