「ほっ」と。キャンペーン

カテゴリ:電子カルテ( 18 )

電子カルテ患者検索画面その2

患者検索プログラムが長くなりすぎて、説明文をつけると送信できなかった。プログラムは kanja_kennsaku.html だが、データファイルとして下の patients.json が必要だ。

ID フィールドに 1 から 6 までの数値を入れて ID KENSAKU ボタンをクリックすると患者情報を検索できる。その状態で、HENKOU ボタンをクリックするとダイアログボックスが現れ情報の編集ができる。SINKI ボタンで新しい患者を登録できる。患者情報は振り仮名でも検索できる。振り仮名検索の場合はワイルドカードが使える。*を入力して検索すると全てのデータがセレクトボックスに表示され、リストのクリックで選択できる。K*のように入力すると、最初がKの患者のリストが表示される。英数で表示しているが、UTF-8を使うと日本語も問題なく使える。

サーバとクライアントの通信を JSON で行って、クライアントとサーバの独立性を確保した Ajax プログラムを作成するという最初の目論見のサンプルプログラムが一応できたので、XAMPPのねたはこれでおしまい。JSON アダプタを使ったクライアントプログラムはデータ構造の変更が簡単にできるので楽しい。ItemFileWriteStore アダプタがデータベース対応になったらもっと便利になるだろう。ちょっと疲れたのでプログラムねたはしばらくは扱いたくない。

データファイル名: patients.json

{
identifier: "id",
label: "name",
items: [
{id: "1", ruby: "Kaguya", name: "Kaguya", date_of_birth: "1981/08/05", sex: "female", address: "Tokyo"},
{id: "2", ruby: "Momotarou", name: "Momotarou", date_of_birth: "1955/01/07", sex: "male", address: "Wakayama"},
{id: "3", ruby: "Kintarou", name: "Kintarou", date_of_birth: "1955/01/07", sex: "male", address: "Kanagawa"},
{id: "4", ruby: "Urasima Tarou", name: "Urasima Tarou", date_of_birth: "1959/01/21", sex: "male", address: "Takatuki City, Osaka"},
{id: "5", ruby: "Snow White", name: "Snow White", date_of_birth: "1987/01/09", sex: "female", address: "Yokohama"},
{id: "6", ruby: "Yuki", name: "Yuki", date_of_birth: "1975/08/01", sex: "female", address: "Yokohama"},
]}
[PR]
by tnomura9 | 2007-12-22 18:55 | 電子カルテ | Comments(0)

電子カルテ患者検索画面

電子カルテの患者検索画面がやっとできた。

d0038298_18392063.jpg


プログラム名: kanja_kensaku.html (onClick は全角英数)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <style type="text/css">
        @import "http://o.aolcdn.com/dojo/1.0.0/dijit/themes/tundra/tundra.css";
        @import "http://o.aolcdn.com/dojo/1.0.0/dojo/dojo.css"
    </style>
    <style type="text/css">
        /* NOTE: for a full screen layout, must set body size equal to the viewport. */
        html, body { height: 100%; width: 100%; margin: 0; padding: 0; }
    </style>
    <script type="text/javascript" src="http://o.aolcdn.com/dojo/1.0.0/dojo/dojo.xd.js"
            djConfig="parseOnLoad: true"></script>
    <script type="text/javascript">
        dojo.require("dojo.parser");
        dojo.require("dijit.layout.TabContainer");
        dojo.require("dijit.layout.ContentPane");
        dojo.require("dojo.data.ItemFileWriteStore");
dojo.require("dojo.date.locale");
        dojo.require("dijit.Dialog");
    
    </script>
    <script type="text/javascript">

        // variables and functions

        patient =
            {id: "",
            date: "",
            name: "",
            date_of_birth: "",
            sex: "", address: ""};

        function found_patient(item) {
            patient.id = patientStore.getValue(item, 'id');
            patient.ruby = patientStore.getValue(item, 'ruby');
            patient.name = patientStore.getValue(item, 'name');
            patient.date_of_birth = patientStore.getValue(item, 'date_of_birth');
            patient.sex = patientStore.getValue(item, 'sex');
            patient.address = patientStore.getValue(item, 'address');

            dojo.byId('id').value = patient.id;
            dojo.byId('ruby').value = patient.ruby;
            dojo.byId('name').value = patient.name;
            dojo.byId('date_of_birth').value = patient.date_of_birth;
            dojo.byId('sex').value = patient.sex;
            dojo.byId('address').value = patient.address;
        }

        function find_patient(id) {
            patientStore.fetchItemByIdentity({identity: id, onItem: found_patient});
        }

        function clearOption(select) {
            var idx = select.length;
            for (var i = 0; i < idx ; i++) {
                select.removeChild(select.lastChild);
            }
        }

        function addOption(select, label, id) {
            var idx = select.length;
            select.options[idx] = new Option(label, id);
        }

        function mkPatientSelect(items, request) {
            clearOption(dojo.byId('patient_select'));
            for (var i = 0; i < items.length; i++) {
                item = items[i];
                label = patientStore.getValue(item, 'id') + ' ' +
                patientStore.getValue(item, 'ruby') + ' ' +
                patientStore.getValue(item, 'name') + ' ' +
                patientStore.getValue(item, 'date_of_birth') + ' ' +
                patientStore.getValue(item, 'sex') + ' ' +
                patientStore.getValue(item, 'address');
                id = patientStore.getValue(item, 'id');
                addOption(dojo.byId('patient_select'), label, id);
            }
        }

        function findPatientsByName() {
            clearOption(dojo.byId('patient_select'));
            patient_ruby = dojo.byId('ruby').value;
            patientStore.fetch({query: {ruby: patient_ruby}, onComplete: mkPatientSelect});
        }

        function _getNumberOfItems(id, query) {
            dojo.byId('dlg_id').value = id + 1;
            dijit.byId('dialog1').show();
        }

        function getNumberOfItems(store) {
            
            store.fetch({onBegin: _getNumberOfItems});
        }

        function newItem(id) {
            getNumberOfItems(patientStore);
        }

        function updateData() {
            var onComplete = function() {
                    find_patient(dojo.byId('dlg_id').value);
            }
            var item = {
                        id: dojo.byId('dlg_id').value,
                        ruby: dojo.byId('dlg_ruby').value,
                        name: dojo.byId('dlg_name').value,
                        date_of_birth: dojo.byId('dlg_date_of_birth').value,
                        sex: dojo.byId('dlg_sex').value,
                        address: dojo.byId('dlg_address').value
                        };
            patientStore.newItem(item);
            patientStore.save({onComplete: onComplete});
        }

        function updateItem() {
            dojo.byId('dlg2_id').value = dojo.byId('id').value;
            dojo.byId('dlg2_ruby').value = dojo.byId('ruby').value;
            dojo.byId('dlg2_name').value = dojo.byId('name').value;
            dojo.byId('dlg2_date_of_birth').value = dojo.byId('date_of_birth').value;
            dojo.byId('dlg2_sex').value = dojo.byId('sex').value;
            dojo.byId('dlg2_address').value = dojo.byId('address').value;
            dijit.byId('dialog2').show();
        }

        function updateDataS() {
            var onComplete = function() {
                    find_patient(dojo.byId('dlg2_id').value);
            }

            var onCompleteFetch = function(item) {
                patientStore.setValue(item, "ruby", dojo.byId('dlg2_ruby').value);
                patientStore.setValue(item, "name", dojo.byId('dlg2_name').value);
                patientStore.setValue(item, "date_of_birth", dojo.byId('dlg2_date_of_birth').value);
                patientStore.setValue(item, "sex", dojo.byId('dlg2_sex').value);
                patientStore.setValue(item, "address", dojo.byId('dlg2_address').value);
                patientStore.save({onComplete: onComplete});
            }

            patientStore.fetchItemByIdentity({identity: dojo.byId('dlg2_id').value, onItem: onCompleteFetch});
        }

    </script>
</head>

<body class="tundra">
<div dojoType="dojo.data.ItemFileWriteStore" url="patients.json" jsId="patientStore"></div>

<div dojoType="dijit.layout.TabContainer" layoutAlign="client" id="mainTabContainer" style="width: 100%; height: 100%">
    <div dojoType="dijit.layout.ContentPane" title="KANJA KENSAKU">
    <h2 align="center">KANJA KENSAKU</h2>
    <table align="center">
        <tr><td>ID: </td><td><input type="text" id="id"></td>
        <td><button onClick="find_patient(dojo.byId('id').value)" style="font-size: 10pt">ID KENSAKU</button></td>
        <td><button onClick="newItem()" style="font-size: 10pt">SINKI</button></td>
        <td><button onClick="updateItem()" style="font-size: 10pt">HENKOU</button></td>
        </tr>
        <tr><td>FURIGANA</td><td><input type="text" id="ruby"></td>
        <td><button onClick="findPatientsByName()" style="font-size: 10pt">HURIGANA KENSAKU</button></td>
        </tr>
        <tr><td>SIMEI</td><td><input type="text" id="name"></td></tr>
        <tr><td>SEINENGAPPI</td><td><input type="text" id="date_of_birth"></td></tr>
        <tr><td>SEIBETU</td><td><input type="text" id="sex"></td></tr>
        <tr><td>JUSHO</td><td><input type="text" id="address"></td></tr>
        <tr><td colspan="5"><select size="16" id="patient_select" onChange="find_patient(this.options[this.selectedIndex].value)" style="width: 100%;"></select></td></tr>
    </table>
    </div>
</div>

<div dojoType="dijit.Dialog" id="dialog1" title="Sinki Kanja" execute="updateData(arguments[0]);">
    <table>
        <tr><td>ID: </td><td><input type="text" id="dlg_id" disabled></td>
        </tr>
        <tr><td>FURIGANA</td><td><input type="text" id="dlg_ruby"></td>
        </tr>
        <tr><td>SIMEI</td><td><input type="text" id="dlg_name"></td></tr>
        <tr><td>SEINENGAPPI</td><td><input type="text" id="dlg_date_of_birth"></td></tr>
        <tr><td>SEIBETU</td><td><input type="text" id="dlg_sex"></td></tr>
        <tr><td>JUSHO</td><td><input type="text" id="dlg_address"></td></tr>
        <tr><td colspan="2" align="center"><button dojoType=dijit.form.Button type="submit">OK</button></td></tr>
    </table>
</div>

<div dojoType="dijit.Dialog" id="dialog2" title="Syusei" execute="updateDataS(arguments[0]);">
    <table>
        <tr><td>ID: </td><td><input type="text" id="dlg2_id" disabled></td>
        </tr>
        <tr><td>FURIGANA</td><td><input type="text" id="dlg2_ruby"></td>
        </tr>
        <tr><td>SIMEI</td><td><input type="text" id="dlg2_name"></td></tr>
        <tr><td>SEINENGAPPI</td><td><input type="text" id="dlg2_date_of_birth"></td></tr>
        <tr><td>SEIBETU</td><td><input type="text" id="dlg2_sex"></td></tr>
        <tr><td>JUSHO</td><td><input type="text" id="dlg2_address"></td></tr>
        <tr><td colspan="2" align="center"><button dojoType=dijit.form.Button type="submit">OK</button></td></tr>
    </table>
</div>
</body>
</html>
[PR]
by tnomura9 | 2007-12-22 18:41 | 電子カルテ | Comments(0)

ItemFileWriteStore の item 数の取得

ItemFileWriteStore の item 数の取得は、fetch() 関数を呼び出したときの、onBegin イベントのコールバック関数で行う。サンプルプログラムは次のようになる。

ファイル名: get_no_of_items.html (alert, onclick は全角英数。 patients.json が必要)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <style type="text/css">
        @import "http://o.aolcdn.com/dojo/1.0.0/dijit/themes/tundra/tundra.css";
        @import "http://o.aolcdn.com/dojo/1.0.0/dojo/dojo.css"
    </style>
    <style type="text/css">
        /* NOTE: for a full screen layout, must set body size equal to the viewport. */
        html, body { height: 100%; width: 100%; margin: 0; padding: 0; }
    </style>
    <script type="text/javascript" src="http://o.aolcdn.com/dojo/1.0.0/dojo/dojo.xd.js"
            djConfig="parseOnLoad: true"></script>
    <script type="text/javascript">
        dojo.require("dojo.parser");
        dojo.require("dojo.data.ItemFileWriteStore");
    
    </script>
    <script type="text/javascript">
        function _getNumberOfItems(size, query) {
            alert('size = ' + size);
        }

        function getNumberOfItems(store) {
            store.fetch({onBegin: _getNumberOfItems});
        }
    </script>
</head>
<body class="tundra">
<div dojoType="dojo.data.ItemFileWriteStore" url="patients.json" jsId="patientStore"></div>
<button onClick="getNumberOfItems(patientStore)">Click me!</button>
</body>
</html>

ファイル名: patients.json

{
identifier: "id",
label: "name",
items: [
{id: "1", ruby: "Kaguya", name: "Kaguya", date_of_birth: "1981/08/05", sex: "female", address: "Tokyo"},
{id: "2", ruby: "Momotarou", name: "Momotarou", date_of_birth: "1955/01/07", sex: "male", address: "Wakayama"},
{id: "3", ruby: "Kintarou", name: "Kintarou", date_of_birth: "1955/01/07", sex: "male", address: "Kanagawa"},
{id: "4", ruby: "Urasima Tarou", name: "Urasima Tarou", date_of_birth: "1959/01/21", sex: "male", address: "Takatuki City, Osaka"},
{id: "5", ruby: "Snow White", name: "Snow White", date_of_birth: "1987/01/09", sex: "female", address: "Yokohama"},
{id: "6", ruby: "Yuki", name: "Yuki", date_of_birth: "1975/08/01", sex: "female", address: "Yokohama"},
]}
[PR]
by tnomura9 | 2007-12-22 07:21 | 電子カルテ | Comments(0)

Dojo のダイアログボックス

Dojo でダイアログボックスを表示するサンプルプログラム。

ファイル名: dialog.html (alert,onclick は全角英数)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
            "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Dialog demo</title>
    <style type="text/css">
        @import "http://o.aolcdn.com/dojo/1.0.0/dijit/themes/tundra/tundra.css";
        @import "http://o.aolcdn.com/dojo/1.0.0/dojo/resources/dojo.css"
    </style>
    <script type="text/javascript" src="http://o.aolcdn.com/dojo/1.0.0/dojo/dojo.xd.js"
        djConfig="parseOnLoad: true"></script>
    <script type="text/javascript">
     dojo.require("dojo.parser");
     dojo.require("dijit.form.Button");
     dojo.require("dijit.Dialog");
     dojo.require("dijit.form.TextBox");
     function updateData(dialogFields) {
         alert('OK');
     }
     </script>
</head>
<body class="tundra">

<button dojoType="dijit.form.Button" onclick="dijit.byId('dialog1').show()">Show Dialog</button>

<div dojoType="dijit.Dialog" id="dialog1" title="First Dialog" execute="updateData(arguments[0]);">
    <table>
        <tr><td>ID: </td><td><input type="text" id="id"></td>
        </tr>
        <tr><td>FURIGANA</td><td><input type="text" id="ruby"></td>
        </tr>
        <tr><td>SIMEI</td><td><input type="text" id="name"></td></tr>
        <tr><td>SEINENGAPPI</td><td><input type="text" id="date_of_birth"></td></tr>
        <tr><td>NENREI</td><td><input type="text" id="age"></td></tr>
        <tr><td>SEIBETU</td><td><input type="text" id="sex"></td></tr>
        <tr><td>JUSHO</td><td><input type="text" id="address"></td></tr>
        <tr><td>DENWABANGO</td><td><input type="text"></td></tr>
        <tr><td>RAIINNITIJI</td><td><input type="text" id="date"></td></tr>
        <tr><td colspan="2" align="center"><button dojoType=dijit.form.Button type="submit">OK</button></td></tr>
    </table>
</div>
</body></html>
[PR]
by tnomura9 | 2007-12-21 17:55 | 電子カルテ | Comments(0)

select 要素の option 要素を操作する

select 要素の中の option 要素を追加したり、削除したりするのは良く使う操作なので汎用の関数にしておくと便利だ。

下の関数で、引数 select は DOM の geElementByIdName(idName) で参照できる select 要素オブジェクトのことだ。 label はoption で表示する項目名の文字列、val は option の value 属性値。option が選択されたときに発生する onChange イベントで呼び出されるコールバック関数に引数として渡される。

function clearOption(select) {
    var idx = select.length;
    for (var i = 0; i < idx ; i++) {
        select.removeChild(select.lastChild);
    }
}

function addOption(select, label, val) {
    var idx = select.length;
    select.options[idx] = new Option(label, val);
}

結局、何の技術であっても、技術を身につけるというのは、こういう簡単な部品を自分の道具箱に集めてきて組み合わせることができるようになるということではないのだろうか。個々の部品が間違いなく動けば高品質の技術ということになるし、道具の種類が多ければ応用力のある幅広い技術ということになる。

上のコードの意味が完全には分かっていなくても、引数に何を与えて、なにが起きるかを知っていれば実用的には問題ないし、逆にその意味を探っていって、DOMの構造の知識や、果ては、FireFox のDOMを処理する部分のソースまで調べることができたら、深い知識があることになる。

ただし、知識を得るためには時間を使うことになるので、自分の目的に合わせて、知識の深さとそれに投資する自分の有限な時間とのトレードオフを考えなければならなくなる。自分をどこに連れて行きたいかということが意外に重要なのだ。

管理人が長々と Ajax プログラムの話を書き続けているのは、XAMPP と Dojo を手に入れたことで、サーバーの処理プログラムと、JSON を利用したサーバーとクライアントのデータのやり取りと、クライアントのプログラムの3層を独立させることできるかどうかということに興味がでてきたからだ。

見栄えも機能も充実した実用的な ASP プログラムを、HTML + JavaScript で記述できるのかどうかは分からない。しかし、上に述べた3層を分離できるような方法があれば、ユーザーによるクライアントプログラムのカスタマイズが簡単になる。ユーザの要求と、きちんと動くプログラムの動作とのギャップを柔軟に埋めることができるかもしれないという感じがするのだ。

Dojo で試験的な電子カルテプログラムを作ってみて感じたのが、ASP 型のプログラムのカルテ管理業務との相性のよさだ。

前にも書いたように、カルテは患者さんのベッドサイドで記述するのが一番いい。また、同じカルテを医師とパラメディカルと共有しなくてはならない。そういうわけで、多くのクライアントがひとつのデータベースにアクセスするというASPの形態は便利に感じる。また、業務の内容が日々進歩していくので、クライアントのプログラムの変更も頻繁に必要になってくる、そういう場合もASP型のプログラムなら簡単に施行できるのだ。

そういうことを考えると、ASP型のプログラムは非常に便利なのだ。しかし、HTTPというプロトコルを利用して、ウェブブラウザをDOMやJavaScriptで操作するという方法には表現力やセキュリティの管理に限界があるような気がする。将来的には、新しい通信プロトコルとASPにより適合したブラウザを使った表現力のあるシステムが出現するかもしれない。

一時期提唱されて消えていったシン・クライアントと Ajax プログラムの違いは、シン・クライアントがクライアントの全てのコードをダウンロードしなくてはならなかったのと比べ、Ajax の場合は基本的な機能はブラウザのほうで持っていて、その組み合わせ方をサーバーからダウンロードするというところだ。

この方式ならブラウザのバージョンアップ以外の普通の業務の場合は、クライアントプログラムをダウンロードするための通信量が激減する。それだけ、機動的な動作が可能になるということだ。これは、また、頻繁なマイナーチェンジはクライアント側のコンピュータを一切いじらないでできるという利点がある。

ウェブブラウザの興隆と Ajax の出現で新しいタイプのコンピュータシステムが登場してきたのだ。
[PR]
by tnomura9 | 2007-12-20 06:38 | 電子カルテ | Comments(0)

Dojo の共通ヘッダー

Dojo でプログラムを作るとき、ほとんどの場合で共通しているヘッダー部分は次のようなものだ。個々のプログラムは、head の最後の部分に JavaScript プログラムを記述し、body の部分に画面のレイアウトを記述するという形になる。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <style type="text/css">
        @import "http://o.aolcdn.com/dojo/1.0.0/dijit/themes/tundra/tundra.css";
        @import "http://o.aolcdn.com/dojo/1.0.0/dojo/dojo.css"
    </style>
    <style type="text/css">
        /* NOTE: for a full screen layout, must set body size equal to the viewport. */
        html, body { height: 100%; width: 100%; margin: 0; padding: 0; }
    </style>
    <script type="text/javascript" src="http://o.aolcdn.com/dojo/1.0.0/dojo/dojo.xd.js"
            djConfig="parseOnLoad: true"></script>
    <script type="text/javascript">
        dojo.require("dojo.parser");
    
    </script>
</head>
<body class="tundra">

</body>
</html>
[PR]
by tnomura9 | 2007-12-19 06:45 | 電子カルテ | Comments(0)

患者検索タスク

電子カルテのタスクのうちで一番作りやすそうなのは患者検索タスクだ。患者検索タスクは全く他のタスクからの情報を必要としないので単体としても作りやすい。

患者検索タスクの作業は、大まかに言うと、患者データベースから検索キーに合うタプルを検索しそのアトリビュートの値をクライアントの画面に表示するというものだ。また、新しいデータをクライアントの画面からデータベースに挿入したり、既にあるデータの変更を行うという作業もあるが、要するにデータベースと画面のテキスト input 要素とのデータの相互移動を行うということだ。

ここで、開発するものはプロトタイプなので、データベースとの接続は行わず、dojo.data.ItemFileWriteStore オブジェクトを使う。このアダプタをつかって JSON ファイルとのデータをやり取りする API については、このブログで既に述べたので省略する。

患者検索タスクのプログラミングはおおむね次のような順序で行う。

  1. 他のタスクで利用するための変数値を保持するための patient オブジェクトを作る。
  2. 患者データベースのJSONとのデータやり取りのための ItemFileWriteStore オブジェクトを作る。
  3. 一人分の患者データの各アトリビュートの値を表示するための、テキスト input と、複数の患者候補を表示するための select 要素を含むクライアント画面を HTML で記述する。
  4. 患者データベースを ID で検索して、取得した結果を表示する関数を記述する。
  5. 患者データベースをふりがなであいまい検索して、取得した複数の患者データを select  要素の option 要素として登録する関数を記述する。
  6. 表示中のデータの変更をデータベースに反映させるための関数を記述する。ただし、ItemFileWriteStore ではもともとの JSON ファイルを変更することはできない。
  7. 新規の患者情報を入力するために、未使用の ID を取得して、画面に表示する関数を記述する。
  8. 個々の関数を呼び出すためのボタンを記述する。

必要な技術要件としては次のようなものがある。

  1. JSON ファイルとのデータのやり取りのためのアダプタである、ItemFileWriteStore の API を使うことができる。
  2. HTML で画面の設計ができる。また、各要素に id 属性をつけることで、DOMを使ってその内容を変更することができる。
  3. イベントがおきることでコールバック関数が呼び出されるイベント駆動型のプログラムを作ることができる。

上に述べた用件のうちで、マニュアルそのままではない知識がいるのは次のようなものだろう。

  1. select 要素のオプションを全てクリアする。
  2. select 要素にオプションを追加する。

これは、他のタスクにも使いそうなので汎用関数として作っておいたほうがいいかもしれない。

電子カルテのクライアントを作るといっても、その表現力は必然的に HTML と CSS と JavaScript の表現能力に規定される。それ以上のものはできないのだ。

自分で電子カルテのプログラムを作るということは、どのようにデータベースのデータを使った処理をしたいのかということを確認するためなので、デザインは後回しにして、論理構造が分かるものができれば十分だ。また、Dojoを使ったASPを作るときに定型的な部分はどこかを知ることができる。
[PR]
by tnomura9 | 2007-12-18 07:15 | 電子カルテ | Comments(0)

電子カルテのテーブル

電子カルテのテーブルはタスク別に考えると便利だ。

患者検索タスク

患者テーブル

  1. 患者ID (プライマリーキー、検索キー)
  2. ふりがな (検索キー)
  3. 氏名
  4. 生年月日 (検索キー)
  5. 住所
  6. 保険証の種別、記号、番号等(省略、本当は大切)


診療記録タスク

診療記録テーブル

  1. 診療記録のID (プライマリーキー)
  2. 患者ID (検索キー)
  3. 日付時刻
  4. 記録内容(テキスト)


検査オーダー

検査項目マスターテーブル

  1. 検査項目コード (検索キー)
  2. 検査項目名 (検索キー)


検査オーダーテーブル

  1. 検査オーダーID
  2. 患者ID (検索キー)
  3. 日付時刻 (検索キー)
  4. 検査項目コード
  5. 検査項目名


以下省略、(まだプログラムを作ってもいないので)
[PR]
by tnomura9 | 2007-12-17 12:30 | 電子カルテ | Comments(0)

Dojo を使った Agile な ASP クライアントプログラム

Dojo で電子カルテを作っているが、行き詰っている。最初に実際の紙のカルテを模したタブをもつ画面のレイアウトを作成し、各々にアクションを追加していったが、コードの量が増えてくると以前に書いた部分について忘れてしまっている。おまけに、関数や変数間の関連が複雑になってくると、変更した部分が思いもかけない副作用を発生するようになった。そこで、もう一度最初から何をどうしたかったのかを整理してみることにした。

電子カルテに何をさせたいのかということを整理してみたら次のようになった。

  1. 患者データベースからIDやルビや生年月日で、検索したい。検索の結果が複数のときはリスト表示してクリックすれば一人を特定できるようにしたい。
  2. 臨床記録をデータベースに記録したり、読み出したりしたい。
  3. 検査のオーダーを、オーダー候補のリストから取り出したい。
  4. セット検査をカスタマイズしたい
  5. 普段良く使う検査は、検査項目のデータベースとは別にすぐに参照できるようにしたい。
  6. 普段使わない検査を検査項目のデータベースから検索したい。
  7. 検査のオーダーは、臨床記録と関連付けて表示させたい
  8. 処方薬を薬剤データベースから取り出して、処方欄に登録したい。
  9. 定期処方のセットを登録したい。以前に使った薬剤はデータベースとは別に保存して利用したい
  10. 処方内容を臨床記録と並べて表示できるようにしたい
  11. 現在の処方内容を紹介状のフォームに転送したい
  12. 以前に処方した薬剤のリストや副作用情報のデータベースを作って検索したい
  13. 検査データの検索をしたい
  14. 検査データの入力を、メディアや、手入力や、バーコードスキャナからやりたい
  15. 任意の検査データを紹介状に添付する別紙として印刷したい
  16. 画像データの検索をしたい
  17. 画像データのリストを表示し、リストのクリックで画像を表示したい
  18. 画像データを種類別、日付別に検索したい
  19. 画像データを紹介状に添付したい。紹介状に組み込まなくて別紙として印刷できたほうがよい
  20. 紹介状を作成できるようにしたい。患者情報はデータベースからコピーしたい。照会先のデータベースを作って、頻回に紹介するところは登録しておいてワンタッチでコピーできるようにしたい
  21. 紹介状の内容をデータベース化して保存、検索したい
  22. 他の医療機関からの回答書や紹介状をスキャナで取り込んでデータベース化したい

こういうふうに書き出してみたら、電子カルテが患者検索、臨床記録、処方管理、検査データ管理、画像データ管理、紹介状管理、他の医療機関からの文書の管理という6つの独立した処理区分のデータベースの管理を目的としていることが分かった。

そうして、それぞれの処理区分には、登録、検索、印刷という共通の処理作業がある。また、臨床記録と、処方、検査データ、画像、紹介状のあいだにはデータの関連があることも分かる。

したがって、電子カルテの開発はつぎのような開発方針をとったほうが良いことがわかる。

  1. まず、患者検索、臨床記録、処方管理、検査データ、画像データ、紹介状の6つのデータベースのためのテーブルを作る。
  2. 6つのデータベースにアクセスするためのアダプターは、どの処理区分のプログラムにも共通になるようにして、データ関連が実現できるようにしておく
  3. 上の6つの区分を一まとめにしたクライアントを開発せず、処理区分ごとに別々のクライアントプログラムを作成し、最後にひとつのクライアントプログラムに統合する
  4. 各モジュールは、患者IDだけを入力してやればテストすることができる。
  5. データベースのアダプタは、dojo.data.ItemFileWriteStore クラスのオブジェクトを作れば、JSON ファイルを記述するだけでテストすることができる
  6. dojo.data.ItemFileWriteStore を使うと、テーブルの構造が、JSON ファイルの記述を変えるだけで変更することができるので、開発の段階でのテーブルの構造の変更に容易に対応することができる。

このような開発方法で特に便利なのは、dojo.data.ItemFileWriteStore だ。データベースからの読み取り、書込を簡単にシミュレートできるし、JSONファイルを書き換えるだけで簡単にテーブルの設計を変更することができる。

dojo.data.ItemFileWriteStore を利用することによって、データベースを全く使わずクライアントプログラムを開発することができる。また、ItemFileWriteStore に読み込ませる JSON のフォーマットは、データベースのテーブルと同じ形式だから、ItemFileWriteStore の部分は容易に、PHPを使ったデータベースとの連携プログラムに置き換えることができる。ひょっとしたら、Dojo.org のほうで、データベースとのアダプタを作ってくれるかもしれない。したがって、データベース部分のプログラムとクライアントのプログラムを分離することができるのだ。

こうしてみると見栄えとセキュリティとデータ入力の部分を除けば、Dojoで十分実用的な電子カルテ (電子カルテにかかわらずASPクライアントプログラム) を作ることができることが分かる。

また、データベースの部分とクライアントの部分を分けることで、バグだしが簡単になるし、クライアントのカスタマイズも自由にできるようになる。

データのセキュリティの問題や、データ入力の多様性の問題があるので実用的なプログラムで Dojo を使うことは難しいかもしれないが、現場が使いやすいと感じるプログラムを開発するのに十分 Agile な開発手段になると思う。
[PR]
by tnomura9 | 2007-12-16 19:30 | 電子カルテ | Comments(0)

電子カルテの便利さ

レイアウトだけだったDojo の勉強用の電子カルテも、だんだんいろいろな機能が動くようになってきた。

患者IDやルビから患者情報を検索できるようになったし、あいまい検索の結果をリスト表示してそれをクリックすると特定の患者情報を取り出せるようになった。検索ページで検索した情報は他のページに参照され、過去の記録を呼び出すことができる。

電子カルテを作ってみて感じたのは、既に記録された情報を利用するのにはすごく便利だということだ。患者検索のワンクリックでカルテを瞬時に呼び出すことができる。タブを切り替えることで、臨床記録の情報から、検査情報へすばやく移動することができる。いままでの、カルテをさがす手間や、カルテのページを繰って検査情報のあるページを探す手間を考えたら夢のようだ。

それでも導入がためらわれるのは、入力の手間についての不安と、呼び出す患者情報を自分の好みにカスタマイズできないことと、費用についての不安だ。

入力の手間について

入力の手間については、前回のエントリーで少し感情的に書いたが、知り合いのメディカルスタッフが困っている様子や、コンピュータの入力に時間をとられて、ベッドサイドの観察時間が激減している様子を聞いていたので、ついきつい口調になってしまった。

それは電子カルテが悪いのではなく、電子カルテの操作性を現場の作業とすり合わせるという作業を省いて闇雲に電子カルテを導入しようという現場無視の管理側の風潮に腹を立てていただけだ。入力の問題が解決すれば電子カルテの紙のカルテに対する機能の優位性は圧倒的で急速に普及していく可能性がある。

記録の問題については、臨床記録の入力とともに、検査データの入力がある。検査データは一回の採血で十数個の項目の数値データとして発生するので、手入力していたらとてもではないが対応できない。検査会社からの報告書の一部分に二次元バーコードとして印刷されていればバーコードリーダーで簡単に読み取らせることができる。

入力についてもう一つ手間がいるのは、画像のデータだ。心電図はスキャナで取り込まなくてはならないし、胸写やCTはデジタル化されていても、そのままではパソコンに取り入れることができない。画像のデータフォーマットが規格化されていて、心電図や、エコーや、FCRや、内視鏡からUSBメモリーで簡単に取り出せれば、機器をいつ買い換えてもすぐにデータを取り込むことができる。

このように、電子カルテの導入がためらわれる理由の一つは他の電子機器との連携が難しいということだ。POSが現在のように普及したのも生産者側が規格化されたバーコードを印刷するようになったため、入力の手間が激減したからだ。電子カルテが使い勝手のいいものになるためには何らかの入力情報の規格化が必要だろう。部品の規格化によって工業製品の製造能率が飛躍的に伸びたように、電子カルテにも何らかの要素データの規格化が必要だ。

カスタマイズ可能なこと

電子カルテを使ってみたいという気持ちが少し出てきたので、ネットで電子カルテを検索してデモ画面の写真をみたりしているが、もう一つしっくりくるレイアウトがない。したがって、購入した後でカスタマイズできないならば、不便な部分がずっと残ってしまうという危険性がある。

これは、クライアントのデータ入力部分と、データベースの部分が独立していないためではないかと思った。この二つが独立していれば、クライアント部分のカスタマイズは比較的簡単にできる。また、カスタマイズはユーザが比較的簡単にできるようになっていないと、カスタマイズのために導入費用と同じ位の費用が発生するようでは安心して導入できない。

ここにも規格化の利点がでてくる。カルテ情報は最終的にはデータベースに収められるので、複数のテーブルの集合として取り扱われる。したがって、このデータベースのテーブルの構成の部分とAPIを規格化してクライアントと分離しておけば、クライアントアプリケーションのカスタマイズはかなり自由にできるはずだ。

いろいろな会社がそれぞれのデータベースエンジンや、開発言語を使ったとしても、データベースのAPIが規格化していれば、クライアントの部分はそれを操作するだけなので自由に変更することができる。クライアントアプリケーションがデータベースと独立していないとそう簡単に変更はできないだろう。

政府が本当に電子カルテの普及を望んでいるのであれば、データベースのテーブルの構成とAPIの規格化ぐらいは行って欲しいものだ。

費用について

医療従事者が電子カルテの内部について知るということは不可能だ。したがって、それは専門の業者に依存しなくてはならなくなる。たとえ、バグの多いシステムを導入してしまったとしても簡単には変更できない。さらに、ソフトの修正やメンテナンスのために導入費の費用以上の費用が発生してしまうことにも繋がる。

そのような事態を避けたければ、結局電子カルテを導入しないのが一番いいということになってしまう。うっかりできの悪いシステムを導入したばかりに、ブラックホールのように費用を吸い込んでしまうという事態は避けたいからだ。

データベース部分のAPIの規格化がされていれば、システムの信頼性を見極めるための材料にもなるし、業務に合わせたカスタマイズも楽になるので費用の見通しをつけることができる。

本当は便利な電子カルテ

電子カルテは本当は便利なのだ、それが導入をためらわなければならなくなるのは、業務内容をどう電子化していくか、どのようなフォーマットの情報にすればよいのか、クライアントのカスタマイズをどう行うのかなどの基本設計の部分の規格化がなされていないためだ。

いろいろな規模や種類の病院があったとしても、電子カルテとして蓄積するデータのフォーマットやテーブル間のリレーションには共通性があるはずだ。それを規格として統一することによって、逆に多様な用途に柔軟に対応できるシステムを作ることができるはずだ。

電子化すれば便利だろうというようなあいまいな発想で電子カルテを振興するのではなく、カルテ情報の本質や現場の業務内容を見据えた基本設計を政府は提示する必要があるのではないだろうか。
[PR]
by tnomura9 | 2007-12-16 08:03 | 電子カルテ | Comments(0)