Symfoware ODBC接続からChartJSで電子カルテデータをWebにリアルタイム表示するまで 〜院内データ見える化システムの開発〜
基本コンセプト
- Fujitsu Symfoware ODBCドライバをPythonで動かしてDWHの情報を取得
- 取得したデータをpandasで加工
- flask, jinja2でWebページを作成(ChartJSでグラフ描写、Ajaxを使ったリアルタイム更新)
- WebサーバとしてIISを使用してWebページ表示
- 以上全てを電子カルテネットワーク内でやる
環境
- Windows Server 2012 R2 (64 bit)
- Anaconda3 (64 bit)
- Symfoware (32 bit)
- Symfoware ODBC driver (32 bit)
- IE9(富士通電子カルテ端末クライアント)
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)
やっぱりダメだった
上記の対応でも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に対応していないようなのでやめました。
最後になぜChartJSなのか
PythonプログラマーとしてはJavaScriptのライブラリであるChartJSよりもPythonライブラリのBokehを使いたかったのですがBokehのグラフはIE9でうまく表示ができないのです。ChartJSはIE9でも問題なく動きます。ということでJavaScriptの軍門に下った、ということになります。くたばれIE9!
医療通訳支援Webアプリ(The record system)をHerokuにデプロイしました
はい徹夜してしまいました。こちらです↓ https://englishtranslationrecord.herokuapp.com 触ってみてください。パスワードとか特に設定しておりません。
GitHubにはコードを上げております。
いや〜、長い道のりでした。昨夜の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にPostgreSQL置くとき、
$ 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
医療通訳支援Webアプリ第2弾 医療通訳対応記録システム
The record system for medical interpretation in a hospital(English only)
医療通訳担当者(英語)のための通訳後記(反省録)保管システム
医療機関のスタッフが医療通訳の現場経験を記録しておき、後学に生かすためのシステムです。
機能1: 対応記録記入(データベースへの書き込み)
以下の情報を記載可能です。(1案件ごとに書き込む方式です)
- 通訳実施者氏名
- 通訳開始日時
- 通訳終了日時
- 依頼者
- 依頼部署
- 外国人患者さんの入外区分
- 外国人患者さんの国籍
- サマリー
データベースはSQLite3を使用。同時多発的書き込みに対応するため将来的にはPosgresに変更予定。
機能2: 対応記録表の表示(データベースのテーブルを表示)
機能1で書き込んだ内容を確認できます。検索機能あり。そのため例えば「眼科だけの対応記録」を眼科から通訳依頼を受けた直後にささっとおさらいをして現場に入るといった使い方が可能です。その他特定の文言が記載されたレコードだけ絞り込む機能もあり。
JavaScriptのライブラリVue.jsのグリッドコンポーネント機能を借用させていただきました。(中身については目下勉強中) https://jp.vuejs.org/v2/examples/grid-component.html
機能3: 累積対応時間のビジュアライゼーション(対応時間を月別にスタックバーチャートとして表示)
担当者別に出ます。医療通訳系の資格を取る際現場実習時間の報告とかに使えるかなと。集計したい期間を選択し、Getボタンを押すとpngファイルをダウンロードできます。
matplotlibを使っています。集計にはpandasを使っています。バーチャートの下にデータテーブルを表示させています。 こちらをほぼパクりました。 https://matplotlib.org/examples/pylab_examples/table_demo.html
Requirements(すみません、バージョンとか細かいことは省略させてください)
- Python3
- Flask
- SQLite3
- matplotlib
- pandas
- numpy
- Vue.js
- bootstrap.css
改修予定リスト
機能3で集計期間に対応情報が皆無だった場合Flaskのエラーページに飛んでしまう(functool.reduceのエラー) が、エラー処理を書き加えてポップアップメッセージ「集計期間にデータはありませんでした。」を出すなどエレガントな対応をしたい。
機能2の対応記録表で一番必要になるのはサマリーの列だが、左端で列幅が狭いなどやや見にくいので何とかする。
5人程度の少人数で使用するならばSQLite3で問題ないと思うが、それ以上になるとやはりサーバ型のデータベースが欲しい。近いうちにPosgresバージョンを公開したい。
以上GitHubからの転記でした。
医療通訳支援Webアプリ第1弾 視力換算システム
以前投稿で視力の伝え方について書きました。 healthcareit-interpreter.hatenablog.com
iOSのアプリでも作ってみるかと構想しておりましたが、友人(こちらのブログの著者
)からWebアプリにした方が楽だという助言をいただきました。 それで出来上がったのがこちらです。
視力換算システム
https://riow1983.github.io/visual_acuity_conversion/
JavaScriptのライブラリvue.jsを使った実装の大方はその友人によるものです。私はレイアウトを少しいじった程度です。じっくり腰を据えてFlask(Pythonのライブラリ)あたりを使って作ろうと思っていたのにこんなに早く出来上がるなんて。JavaScripterに完敗です。
さてさて、それではこれを眼科の先生に見せて感想を聞いてみることにしますか。
病院の医療情報部門の端末払出しアルゴリズム
「スマホ5台新規で払出してください。」 「電子カルテ端末が足りないのよ。あと3台出して。」
こんな要望を受けて、さて在庫数は○○台だから、いくつ払出せるな・・・とその度に頭を悩ませることが多いのが病院の医療情報部門のあるあるではないでしょうか?基準が無くて恣意的で人治主義的なさじ加減で無駄に幅を効かせている総務課の事務員ならまだしも、病院のIT化を推進する医療情報部門の医療情報技師ならドクターの診断アルゴリズムも顔負けな”端末払出しアルゴリズム”で払出業務の透明化を図るべきでしょう。
さて、数学を使わなければならない局面というのはこういう所に出てくるんですね。払出優先度指数の算出はともかく、当月最大払出可能数については数式を考える前にフリーハンドで「在庫管理にはこういう曲線が望ましいよな」と線を描いてから、さてこういうカーブの曲線を描く式ってなんだ?とネットで調べて行き当たるという体験をしました。(今回はロジスティック曲線というものらしいです。)兵站業務(logistics)に向いている曲線だからlogistic曲線というのか?と一瞬思ってしまうほどの運命的な出会いでした。 そういった意味合いはないのですが語源としては同源のようですね。 http://www.weblio.jp/content/%E3%83%AD%E3%82%B8%E3%82%B9%E3%83%86%E3%82%A3%E3%83%83%E3%82%AF
外国人観光客が病院に受診した際、病院の医事課が旅行保険会社に確認すべき事項
通訳に呼ばれた外国人患者が観光のため来日した旅行者だった場合、医療通訳担当者にお会計のことで医事課から色々と注文がついて面倒なことになります。 (うちの病院はスキー場に近いことから、スキー観光客が多いようです。) 外国人観光客が旅行保険に加入していた場合、旅行保険会社から病院に電話してもらいます。日本人スタッフがいれば日本語で電話対応可能ですが(その場合は速攻で医事課に転送しましょう)、英語で対応する必要がある可能性もあります。医療通訳担当者が旅行保険会社に確認すべき事項をまとめてみました。
旅行保険会社に確認すべき事項 (Things to confirm with a travel insurance company):
「まず最初に1つ確認させてください。日本語を話せるスタッフはいますか?」(まだ諦めていません)
"First let me verify one thing. Does your company have any personnel who speak Japanese?"
・会計の方法(The method for billing):
「会計に必要なものを教えてください。請求書、診断書、その他にありますか?」
"Let me know things we must have to prepare for billing. We suppose an invoice, a certificate, and others?"
「御社が指定する様式の診断書はありますか?(御社のメールアドレスをメールしてもらえますか?当院のメールアドレスはfoo@hogeです。
"Should we make a private style document you specify? (Please send your e-mail to our address. The address is foo@hoge.)"
「患者の要望で作成した書類は保険適用されますか?」
"Are documents on patient’s demand covered by the insurance?"
「請求書の送付先を教えてください。患者による立替払いになりますか?それとも保険会社による直接支払いでしょうか?」
"To whom should we bill to? Is it an advance payment on behalf of the insurer or a direct payment from the insurer?"
(保険会社による直接支払いの場合)「保険会社様の社名、ご住所、患者様のお名前、案件番号を確認させてください。」
(In case of a direct payment) "Can I verify the company’s name and address, the patient’s name, and case No.?"
「他に準備すべきものはありますか?」
"Is there anything else we should prepare?"
・その他の費用について (About other expenses):
「松葉杖や付き添い寝具など入院中に発生する(した)その他の費用はどのように扱われますでしょうか?それらは患者様負担でしょうか?それとも保険会社様支払いでしょうか?」
"How do you deal with other expenses incurred during admission, namely a crutch, a bedding for rent? Are these things to be paid by the patient himself (herself) or by the the insurer? "
・・・と、こんな感じです。こうやって準備万端にしておけば少しは気が楽になります。
ランダムパスワードの生成
電子カルテの利用者IDの初期パスワードとして、大文字アルファベットと数字で構成された6桁のランダムパスワードを設定する方針が決定されました。え!今更かよ!
さて生成コードをPythonで実装してみます。
import string from random import choice def get_PW(figures): pw = ''.join([choice(string.ascii_uppercase.replace('I','').replace('O','').replace('Z','')+string.digits.replace('0','').replace('1','').replace('2','')) for i in range(figures)]) if pw.isdigit():#数字だけの場合 return 'A'+pw[1:] elif set(pw).issubset(set(string.ascii_uppercase)):#文字だけの場合 return "9"+pw[1:] else:#数字と文字ともに含有している場合 return pw get_PW(6)
数字だけの場合はint(pw)としても良いです。文字だけの場合はどうするか悩みましたが、文字列を集合に変換し、集合「パスワード」が集合「大文字アルファベット」のサブセット(部分集合)であればすなわちパスワードが全て大文字アルファベットで構成されていることになる、という性質を利用することにしました。 あとは、紛らわしい文字と数字の組み合わせ(1とI, 0とO,2とZ)を除外しておいて視認性を高める工夫をしています。 後はこれをVBAで実装し直すだけですね(笑)!