皆さん、ご自身の病名(医学的には診断名)を覚えていますか?
僕の本職は医師(開業医)です。
日常診療の課題感から、以下のような、『診断名を保管でき、かつ、必要に応じて引き出せる』アプリを実装しました。
動作は以下です。
診察券番号を伝えると、診断名を返してくれるBot完成!#protoout#デジタルヘルス学会#プログラミング初学者#評論家ではなく創造者になろう#linebotpic.twitter.com/IJuusFHLkF
— 北城雅照|ゼロからプログラミング勉強を始めた整形外科医 (@teru3_kitashiro) December 22, 2020
医師として、月曜から土曜まで毎週700人以上の患者さまを診察させていただいております。
外来診療を行う中で、「多くの方がご自身の診断名を覚えていらっしゃらない」ことに課題感を持っていました。
診断名がわからないと、次の処置を行っても良いものか、非常に悩む瞬間があるからです。
でも例えば、「好酸球性多発血管炎性肉芽腫症」と診断されても、「飛影邪王炎殺黒龍波」(分かる人いるかな?)みたいに難しく、なかなか覚えられるものではありません。
そこで、上述の『診断名を保管でき、かつ、必要に応じて引き出せる』アプリを実装しました。
システム概要は以下です。
1-1 webappか診察券番号でIDを設定し病名の登録
1-2 webappで診察券番号で病名の確認
2-1 LINEBOTから診察券番号でIDを設定し病名の登録
2-2 LINEBOTで診察券番号で病名の確認
それぞれについて確認します。
下準備
必要なした準備は以下の2つ
① VScordのインストール
② firebaseの設定
上記はgoogle先生に譲ります。
Webappの実装
今回のwebappはvue.jsで実装します。
webappから病名の登録
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>リアルタイムにデータ取得</title></head><body><divid="app"><p>診察券番号:<inputv-model="cardId"placeholder="00000000"><br>名前:<inputv-model="name"placeholder="名前"><br>誕生日:<inputtype="date"v-model="birthday"placeholder="1900/1/1"><br>性別:<inputv-model="gender"placeholder="男or女"><br>診断名:<inputv-model="dgname"placeholder="診断名"><br>診断日:<inputtype="date"v-model="dgday"placeholder="1900/1/1"><br><buttonv-on:click='post'>送信</button><br><ahref="https://unruffled-curran-e76f78.netlify.app/searchmydg">登録確認のために診断名検索ページに移動する</a></p></div><script src="https://unpkg.com/vue"></script><script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-app.js"></script><script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-firestore.js"></script><script>// firebaseの設定から下記を記入constfirebaseConfig={apiKey:"",authDomain:"",projectId:"",storageBucket:"",messagingSenderId:"",appId:"",measurementId:""};// Initialize Firebasefirebase.initializeApp(firebaseConfig);constdb=firebase.firestore();letpatient='';letpatientId='';constapp=newVue({el:'#app',data:{allData:[],cardId:'',name:'',birthday:'',gender:'',dgname:'',dgday:'',},methods:{//データ追加post:asyncfunction(){patient=this.cardIdconstres=awaitdb.collection("medicalRecord1").doc(patient).set({name:this.name,birthday:this.birthday,gender:this.gender,diagnosis:{dgday:this.dgday,dgname:this.dgname},cardId:this.cardId});console.log(patient);},}})</script></body></html>
webappで病名の確認
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><title>診断名検索</title></head><body><divid="app"><h1>診断名検索</h1><inputv-model:value="cardID"placeholder="診察券番号"><br>診察券番号:{{ cardID }}
<buttonv-on:click="searchDg">検索</button><br><p>氏名:{{ name }}<br>診断名:{{ diagnosis }}<br>診断日:{{ diagnosisday }}
</p><buttonv-on:click="clearAll">全てをクリア</button><br><ahref="https://mystifying-tesla-bb64e7.netlify.app/addmydg">診断名登録ページに移動する</a></div><script src="https://unpkg.com/vue"></script><script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-app.js"></script><script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-firestore.js"></script><script>// firebaseの設定から下記を記入constfirebaseConfig={apiKey:"",authDomain:"",projectId:"",storageBucket:"",messagingSenderId:"",appId:"",measurementId:""};// Initialize Firebasefirebase.initializeApp(firebaseConfig);constdb=firebase.firestore();constapp=newVue({el:'#app',// Vueが管理する一番外側のDOM要素data:{// Vue内部で使いたい変数は全てこの中に定義するcardID:'',name:'',diagnosis:'',diagnosisday:'',},methods:{// 関数はここに記入searchDg:asyncfunction(){console.log('次の診察券番号の患者さまが検索されました:',this.cardID);constpatientData=awaitdb.collection("medicalRecord1").doc(this.cardID).get();console.log(patientData.date);//データの格納constpData=patientData.data();//繰り返し各箇所なので一回定数に this.name=pData.name; this.diagnosis=pData.diagnosis.dgname;this.diagnosisday=pData.diagnosis.dgday;},clearAll:function(){this.cardID='';this.name='';this.diagnosis='';this.diagnosisday='';console.log('全てのToDoが消去されました');},},});</script></body></html>
LINEBotから病名の登録
リッチメニューを設定し、病名登録のwebappがLINEの中で立ち上がるように設定しました。
同様に、病名確認のwebappも立ち上がるように設定しました。
診断名の新規登録はリッチメニューから、登録用のサイトに飛ぶ設定になってます。#protoout#デジタルヘルス学会#プログラミング初学者#評論家ではなく創造者になろうpic.twitter.com/iYpa1BMKI4
— 北城雅照|ゼロからプログラミング勉強を始めた整形外科医 (@teru3_kitashiro) December 22, 2020
LINEBotで病名の確認
診断名を確認する場合、“診断名確認”とLINEBotに送ると、“診察券番号を教えてください”と返信させ、診察券番号を送ると名前・診断名・診断日を返すように設定しました。
まずは、必要なパッケージの導入
以下のコードを順番にターミナルに入力
$ npm init -y
$ npm i express
$ npm i @line/bot-sdk
$ npm i firebase
今回作成したコードは以下の通り。
'use strict';// ########################################// 初期設定など// ########################################// パッケージを使用しますconstexpress=require('express');constline=require('@line/bot-sdk');constfirebase=require("firebase/app");require("firebase/firestore");// firebaseの設定から下記を記入constfirebaseConfig={apiKey:"",authDomain:"",projectId:"",storageBucket:"",messagingSenderId:"",appId:"",measurementId:""};// Initialize Firebasefirebase.initializeApp(firebaseConfig);constdb=firebase.firestore();// ローカル(自分のPC)でサーバーを公開するときのポート番号ですconstPORT=process.env.PORT||3000;// Messaging APIの設定から記入constconfig={channelSecret:'',channelAccessToken:''};// ########################################// LINEサーバーからのWebhookデータを処理する部分// ########################################// LINE SDKを初期化しますconstclient=newline.Client(config);// LINEサーバーからWebhookがあると「サーバー部分」から以下の "handleEvent" という関数が呼び出されますasyncfunctionhandleEvent(event){// 受信したWebhookが「テキストメッセージ以外」であればnullを返すことで無視しますif(event.type!=='message'||event.message.type!=='text'){returnPromise.resolve(null);}//診断名検索というテキストを受け取ったら診察券番号を確認するリプライを出すif(event.message.text=='診断名確認'){returnclient.replyMessage(event.replyToken,{type:'text',text:'診察番号を入力してください'});}//cardIDの中に審査券番号が格納constcardID=event.message.text;console.log(cardID);//診察券番号からfirebase上の診断名を検索constpatientData=awaitdb.collection('medicalRecord1').doc(cardID).get();console.log(patientData.data());constpData=patientData.data();if(pData !=undefined){constname=pData.name;constdiagnosis=pData.diagnosis.dgname;constdiagnosisDay=pData.diagnosis.dgday;letmsg='';msg='お名前:'+name+'\n'+'診断名:'+diagnosis+'\n'+'診断日:'+diagnosisDayconsole.log(msg);constmessage={type:'text',text:msg};client.replyMessage(event.replyToken,message);}else{constmessage={type:'text',text:'再度診察券番号を記入してください。'};client.replyMessage(event.replyToken,message);};}// ########################################// Expressによるサーバー部分// ########################################// expressを初期化しますconstapp=express();// HTTP GETによって '/' のパスにアクセスがあったときに 'Hello LINE BOT! (HTTP GET)' と返事します// これはMessaging APIとは関係のない確認用のものですapp.get('/',(req,res)=>res.send('Hello LINE BOT! (HTTP GET)'));// HTTP POSTによって '/webhook' のパスにアクセスがあったら、POSTされた内容に応じて様々な処理をしますapp.post('/webhook',line.middleware(config),(req,res)=>{// Webhookの中身を確認用にターミナルに表示しますconsole.log(req.body.events);// 空っぽの場合、検証ボタンをクリックしたときに飛んできた"接続確認"用// 削除しても問題ありませんif(req.body.events.length==0){res.send('Hello LINE BOT! (HTTP POST)');// LINEサーバーに返答しますconsole.log('検証イベントを受信しました!');// ターミナルに表示しますreturn;// これより下は実行されません}// あらかじめ宣言しておいた "handleEvent" 関数にWebhookの中身を渡して処理してもらい、// 関数から戻ってきたデータをそのままLINEサーバーに「レスポンス」として返しますPromise.all(req.body.events.map(handleEvent)).then((result)=>res.json(result));});// 最初に決めたポート番号でサーバーをPC内だけに公開します// (環境によってはローカルネットワーク内にも公開されます)app.listen(PORT);console.log(`ポート${PORT}番でExpressサーバーを実行中です…`);
今後の課題
診断名を一つではないので、診断名をリスト化し、LINEBotに返せる設定を実装したいと思いました。
もう少し深めて行く予定です。
その他の記事
近すぎると小池都知事が『密です。』と連呼するデバイスを作ったら腹筋が崩壊したので、皆さんにも試して欲しい。
誰が使うかわからないけど、膝のレントゲン写真を送ったら、その膝がどの程度痛んでいるのか教えてくれるラインbotを作ってみた。