追記(2021年7月):この記事のアルゴリズムを使って、ウェブアプリ+デスクトップアプリを作って以下に公開しました。無料です。

電子カルテの音声入力ツールを作ってみました。

利用方法は、ばーっと音声入力したら、後は1クリックで項目ごとに整理したものがエクセル形式でダウンロードされます。

例えば、「名前ハチ、次は、体温37度、次は、体重5kg、次は、所見、嘔吐、下痢、次〜〜。」といった後、エクセルで名前、体温、体重、所見のそれぞれの項目に入力される設計です。音声入力なので間違いが発生するという短所はありますが、このツールの長所は、無言の時間が長めにあっても、飼い主さんとの会話が途中に入っても、大丈夫なところです。

利用の際には、Google documentを使用するのでGoogleアカウントが必要です。無料で使えます。実行する際には、実行権限の承認をしてください。

Google documentを開き、ツール → スクリプトエディタを開き、以下のコード.gsとsidebar_create.htmlを作成してください。

function onOpen() { //ドキュメントを開いたときの動作。リボンにカルテ用に整理すると使用方法の説明のリボンを作る
  var ui = DocumentApp.getUi();
  
  //リボン作成
  ui.createMenu('カルテ用に整理する')
    .addItem('実行', 'recognize_text')
    .addToUi();
  //リボン作成
   ui.createMenu('使用方法の説明')
    .addItem('使用方法の説明', 'explaining')
    .addToUi(); 
   //リボン作成
   ui.createMenu('リセット')
    .addItem('リセット実行', 'allreset')
    .addToUi();  
  
  //alert作成
  ui.alert("1.使い方\n\n上のリボンから、「ファイル」を選択→「コピーを作成」を押し、コピーを作ってコピーの方を使用してください。このファイルは全員が見れてしまいます。\n\nリボンから「ツール」を選択→「音声入力」を選択→音声入力のマイクを押して、口述してください。\nSafariは音声入力の項目がでてきませんので、Google chromeをお使いください。\n\n入力を終えたら、リボンの「カルテ用に整理する」を選択→「実行」を押す→EXCELがダウンロードされます。実行するにあたって、承認が必要です。\n\n2.口述の仕方\n\n「〇〇(後述する項目)、△△。つぎへ。」のように、項目を宣言してから、話して、「次へ」or「次は」で話に区切りをつけてください。\n例:「体温37度。つぎ。体重7kg。つぎへ。症状、2日前から下痢。今日から嘔吐。つぎへ。」項目のあとに「は」「が」を入れないでください。\nダメな例:「体温は、37度、次へ」\n\n3.項目\n\n担当\n患者番号\n名前\n住所\n電話番号\n動物\n性別\n生年月日\n年齢\nブリード\n主訴\n既往歴\n体重\nボディコンデションスコア\n体温\nCRT(毛細血管再充満時間)\n心拍数\n呼吸数\n聴診\n触診\n症状\n所見\n処置\n問題点\n計画\n検査結果\n投薬\n次回");
 
}

function explaining(){ //説明用のリボンを押したときの動作
  //サイドバー作成
  var htmlOutput = HtmlService.createHtmlOutputFromFile('sidebar_create');
  DocumentApp.getUi().showSidebar(htmlOutput);
}


function allreset(){
  var app = DocumentApp.getUi();
  var body = DocumentApp.getActiveDocument().getBody();
  var asText = body.asText();
  var text = asText.getText();
  var text1 = body.editAsText() ;
  text1.deleteText(0,text.length-1)
}



function recognize_text(){ //メインの関数
  
  //配列を作成
  var ary = [
  ["1.担当",""],
  ["2.患者番号",""],
  ["3.名前",""],
  ["4.住所",""],
  ["5.電話番号",""],
  ["6.動物",""],
  ["7.性別",""],
  ["8.生年月日",""],
  ["9.年齢",""],
  ["10.ブリード",""],
  ["11.主訴",""],
  ["12.既往歴",""],
  ["13.体重",""],
  ["14.ボディコンデションスコア",""],
  ["15.体温",""],
  ["16.CRT(毛細血管再充満時間)",""],
  ["17.心拍数",""],
  ["18.呼吸数",""],
  ["19.聴診",""],
  ["20.触診",""],
  ["21.症状",""],
  ["22.所見",""],
  ["23.処置",""],
  ["24.問題点",""],
  ["25.計画",""],
  ["26.検査結果",""],
  ["27.投薬",""],
  ["28.次回",""],
  ["29.記入日",""]
  ];
  
  //テキスト情報を得る
  var app = DocumentApp.getUi();
  var body = DocumentApp.getActiveDocument().getBody();
  var asText = body.asText();
  var text = asText.getText();

  /*音声入力の項目を拾い集めるアルゴリズムの例
  const kanjyabango_start= text.indexOf("患者番号"); はじめの位置を得る
  if(kanjyabango_start!=-1){ はじめの位置が−1は、ないということなので、あった場合のみその後の処理を行う
    const kanjyabango_stop= text.indexOf("次",kanjyabango_start); 上の位置から「次」が出てくるまで
    const kanjyabango_content=text.slice(kanjyabango_start+4,kanjyabango_stop); 項目の終わりから次までの言葉を取得
    ary[0].pop();    2次元配列の(a,b)のbを消す
    ary[0].push(kanjyabango_content); 2次元配列の(a,c)のcを入れる
  } 
  */
  
  const kanjyabango_start_1= text.indexOf("担当");
  if(kanjyabango_start_1!=-1){
    const kanjyabango_stop_1= text.indexOf("次",kanjyabango_start_1);
    const kanjyabango_content_1=text.slice(kanjyabango_start_1+2,kanjyabango_stop_1);
    ary[0].pop();
    ary[0].push(kanjyabango_content_1);
  }
   
  const kanjyabango_start_2= text.indexOf("患者番号");
  if(kanjyabango_start_2!=-1){
    const kanjyabango_stop_2= text.indexOf("次",kanjyabango_start_2);
    const kanjyabango_content_2=text.slice(kanjyabango_start_2+4,kanjyabango_stop_2);
    ary[1].pop();
    ary[1].push(kanjyabango_content_2);
  }
   
  const kanjyabango_start_3= text.indexOf("名前");
  if(kanjyabango_start_3!=-1){
    const kanjyabango_stop_3= text.indexOf("次",kanjyabango_start_3);
    const kanjyabango_content_3=text.slice(kanjyabango_start_3+2,kanjyabango_stop_3);
    ary[2].pop();
    ary[2].push(kanjyabango_content_3);
  }
 
  const kanjyabango_start_4= text.indexOf("住所");
  if(kanjyabango_start_4!=-1){
    const kanjyabango_stop_4= text.indexOf("次",kanjyabango_start_4);
    const kanjyabango_content_4=text.slice(kanjyabango_start_4+2,kanjyabango_stop_4);
    ary[3].pop();
    ary[3].push(kanjyabango_content_4);
  }

  const kanjyabango_start_5= text.indexOf("電話番号");
  if(kanjyabango_start_5!=-1){
    const kanjyabango_stop_5= text.indexOf("次",kanjyabango_start_5);
    const kanjyabango_content_5=text.slice(kanjyabango_start_5+4,kanjyabango_stop_5);
    ary[4].pop();
    ary[4].push(kanjyabango_content_5);
  }

  const kanjyabango_start_6= text.indexOf("動物");
  if(kanjyabango_start_6!=-1){
    const kanjyabango_stop_6= text.indexOf("次",kanjyabango_start_6);
    const kanjyabango_content_6=text.slice(kanjyabango_start_6+2,kanjyabango_stop_6);
    ary[5].pop();
    ary[5].push(kanjyabango_content_6);
  }

  const kanjyabango_start_7= text.indexOf("性別");
  if(kanjyabango_start_7!=-1){
    const kanjyabango_stop_7= text.indexOf("次",kanjyabango_start_7);
    const kanjyabango_content_7=text.slice(kanjyabango_start_7+2,kanjyabango_stop_7);
    ary[6].pop();
    ary[6].push(kanjyabango_content_7);
  }

  const kanjyabango_start_8= text.indexOf("生年月日");
  if(kanjyabango_start_8!=-1){
    const kanjyabango_stop_8= text.indexOf("次",kanjyabango_start_8);
    const kanjyabango_content_8=text.slice(kanjyabango_start_8+4,kanjyabango_stop_8);
    ary[7].pop();
    ary[7].push(kanjyabango_content_8);
  }
 
  const kanjyabango_start_9= text.indexOf("年齢");
  if(kanjyabango_start_9!=-1){
    const kanjyabango_stop_9= text.indexOf("次",kanjyabango_start_9);
    const kanjyabango_content_9=text.slice(kanjyabango_start_9+2,kanjyabango_stop_9);
    ary[8].pop();
    ary[8].push(kanjyabango_content_9);
  }

  const kanjyabango_start_10= text.indexOf("ブリード");
  if(kanjyabango_start_10!=-1){
    const kanjyabango_stop_10= text.indexOf("次",kanjyabango_start_10);
    const kanjyabango_content_10=text.slice(kanjyabango_start_10+4,kanjyabango_stop_10);
    ary[9].pop();
    ary[9].push(kanjyabango_content_10);
  }

  const kanjyabango_start_11= text.indexOf("主訴");
  if(kanjyabango_start_11!=-1){
    const kanjyabango_stop_11= text.indexOf("次",kanjyabango_start_11);
    const kanjyabango_content_11=text.slice(kanjyabango_start_11+2,kanjyabango_stop_11);
    ary[10].pop();
    ary[10].push(kanjyabango_content_11);
  }

  const kanjyabango_start_12= text.indexOf("既往歴");
  if(kanjyabango_start_12!=-1){
    const kanjyabango_stop_12= text.indexOf("次",kanjyabango_start_12);
    const kanjyabango_content_12=text.slice(kanjyabango_start_12+3,kanjyabango_stop_12);
    ary[11].pop();
    ary[11].push(kanjyabango_content_12);
  }

  const kanjyabango_start_13= text.indexOf("体重");
  if(kanjyabango_start_13!=-1){
    const kanjyabango_stop_13= text.indexOf("次",kanjyabango_start_13);
    const kanjyabango_content_13=text.slice(kanjyabango_start_13+2,kanjyabango_stop_13);
    ary[12].pop();
    ary[12].push(kanjyabango_content_13);
  }

  const kanjyabango_start_14= text.indexOf("ボディコンデションスコア");
  if(kanjyabango_start_14!=-1){
    const kanjyabango_stop_14= text.indexOf("次",kanjyabango_start_14);
    const kanjyabango_content_14=text.slice(kanjyabango_start_14+12,kanjyabango_stop_14);
    ary[13].pop();
    ary[13].push(kanjyabango_content_14);
  }

  const kanjyabango_start_15= text.indexOf("体温");
  if(kanjyabango_start_15!=-1){
    const kanjyabango_stop_15= text.indexOf("次",kanjyabango_start_15);
    const kanjyabango_content_15=text.slice(kanjyabango_start_15+2,kanjyabango_stop_15);
    ary[14].pop();
    ary[14].push(kanjyabango_content_15);
  }

  const kanjyabango_start_16= text.indexOf("CRT");
  if(kanjyabango_start_16!=-1){
    const kanjyabango_stop_16= text.indexOf("次",kanjyabango_start_16);
    const kanjyabango_content_16=text.slice(kanjyabango_start_16+3,kanjyabango_stop_16);
    ary[15].pop();
    ary[15].push(kanjyabango_content_16);
  }

  const kanjyabango_start_17= text.indexOf("心拍数");
  if(kanjyabango_start_17!=-1){
    const kanjyabango_stop_17= text.indexOf("次",kanjyabango_start_17);
    const kanjyabango_content_17=text.slice(kanjyabango_start_17+3,kanjyabango_stop_17);
    ary[16].pop();
    ary[16].push(kanjyabango_content_17);
  }

  const kanjyabango_start_18= text.indexOf("呼吸数");
  if(kanjyabango_start_18!=-1){
    const kanjyabango_stop_18= text.indexOf("次",kanjyabango_start_18);
    const kanjyabango_content_18=text.slice(kanjyabango_start_18+3,kanjyabango_stop_18);
    ary[17].pop();
    ary[17].push(kanjyabango_content_18);
  }

  const kanjyabango_start_19= text.indexOf("聴診");
  if(kanjyabango_start_19!=-1){
    const kanjyabango_stop_19= text.indexOf("次",kanjyabango_start_19);
    const kanjyabango_content_19=text.slice(kanjyabango_start_19+2,kanjyabango_stop_19);
    ary[18].pop();
    ary[18].push(kanjyabango_content_19);
  }

  const kanjyabango_start_20= text.indexOf("触診");
  if(kanjyabango_start_20!=-1){
    const kanjyabango_stop_20= text.indexOf("次",kanjyabango_start_20);
    const kanjyabango_content_20=text.slice(kanjyabango_start_20+2,kanjyabango_stop_20);
    ary[19].pop();
    ary[19].push(kanjyabango_content_20);
  }

  const kanjyabango_start_21= text.indexOf("症状");
  if(kanjyabango_start_21!=-1){
    const kanjyabango_stop_21= text.indexOf("次",kanjyabango_start_21);
    const kanjyabango_content_21=text.slice(kanjyabango_start_21+2,kanjyabango_stop_21);
    ary[20].pop();
    ary[20].push(kanjyabango_content_21);
  }

  const kanjyabango_start_22= text.indexOf("所見");
  if(kanjyabango_start_22!=-1){
    const kanjyabango_stop_22= text.indexOf("次",kanjyabango_start_22);
    const kanjyabango_content_22=text.slice(kanjyabango_start_22+2,kanjyabango_stop_22);
    ary[21].pop();
    ary[21].push(kanjyabango_content_22);
  }

  const kanjyabango_start_23= text.indexOf("処置");
  if(kanjyabango_start_23!=-1){
    const kanjyabango_stop_23= text.indexOf("次",kanjyabango_start_23);
    const kanjyabango_content_23=text.slice(kanjyabango_start_23+2,kanjyabango_stop_23);
    ary[22].pop();
    ary[22].push(kanjyabango_content_23);
  }

  const kanjyabango_start_24= text.indexOf("問題点");
  if(kanjyabango_start_24!=-1){
    const kanjyabango_stop_24= text.indexOf("次",kanjyabango_start_24);
    const kanjyabango_content_24=text.slice(kanjyabango_start_24+3,kanjyabango_stop_24);
    ary[23].pop();
    ary[23].push(kanjyabango_content_24);
  }

  const kanjyabango_start_25= text.indexOf("計画");
  if(kanjyabango_start_25!=-1){
    const kanjyabango_stop_25= text.indexOf("次",kanjyabango_start_25);
    const kanjyabango_content_25=text.slice(kanjyabango_start_25+2,kanjyabango_stop_25);
    ary[24].pop();
    ary[24].push(kanjyabango_content_25);
  }

  const kanjyabango_start_26= text.indexOf("検査結果");
  if(kanjyabango_start_26!=-1){
    const kanjyabango_stop_26= text.indexOf("次",kanjyabango_start_26);
    const kanjyabango_content_26=text.slice(kanjyabango_start_26+4,kanjyabango_stop_26);
    ary[25].pop();
    ary[25].push(kanjyabango_content_26);
  }

  const kanjyabango_start_27= text.indexOf("投薬");
  if(kanjyabango_start_27!=-1){
    const kanjyabango_stop_27= text.indexOf("次",kanjyabango_start_27);
    const kanjyabango_content_27=text.slice(kanjyabango_start_27+2,kanjyabango_stop_27);
    ary[26].pop();
    ary[26].push(kanjyabango_content_27);
   }

  const kanjyabango_start_28= text.indexOf("次回");
  if(kanjyabango_start_28!=-1){
    const kanjyabango_stop_28= text.indexOf("次",kanjyabango_start_28);
    const kanjyabango_content_28=text.slice(kanjyabango_start_28+2,kanjyabango_stop_28);
    ary[27].pop();
    ary[27].push(kanjyabango_content_28);
   }
  
  var currentTime  = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd');
  ary[28].pop();
  ary[28].push(currentTime);
  
  //テーブルに配列を書き出し
  table = body.appendTable(ary);
  
  //スプレッドシートに書き出し
  //シートの名前は、患者番号がある場合は患者番号優先。優先順位は 患者番号>名前。これらがなければカルテとする
  if(ary[1][1]!=""){
  var ss = SpreadsheetApp.create(ary[1][1]);
  console.log(ss.getName());
  }else if(ary[2][1]!=""){
  var ss = SpreadsheetApp.create(ary[2][1]);
  console.log(ss.getName());    
  }else{
  var ss = SpreadsheetApp.create("カルテ");
  console.log(ss.getName());
  }
  
  //配列書き込み
  var sheet = ss.getSheetByName("シート1");
  sheet.getRange(2,1,29,2).setValues(ary);
  //調整
  sheet.getRange(1,1).setValue(["項目"]); //タイトル
  sheet.getRange(1,2).setValue(["内容"]); //タイトル
  sheet.setColumnWidth(1, 200); //セルの幅
  sheet.setColumnWidth(2, 450); //セルの幅
  ss.getRange("B2:B30").setHorizontalAlignment("left"); //左配置
  ss.getRange("A1:B1").setHorizontalAlignment("center"); //右配置
  ss.getRange("A1:B1").setBackground("#797979"); //色

  //エクセルとしてダウンロード  
  var ID =ss.getId();
  var URL = 'https://docs.google.com/spreadsheets/d/'+ID+'/export?format=xlsx';
  var options = {
    method: "get",
    headers: {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
    muteHttpExceptions: true
  };
  
  var htmlOutput = HtmlService
                  .createHtmlOutput('<a href="'+URL+'">エクセルをダウンロード</a>')
                  .setSandboxMode(HtmlService.SandboxMode.IFRAME)
                  .setWidth(80)
                  .setHeight(60);
  DocumentApp.getUi().showModalDialog(htmlOutput, 'エクセルをダウンロード');
}
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
  </head>
  <body>

<div class="block"> 

<h3>1.使い方</h3>
<h5>○上のリボンから、「ファイル」を選択→「コピーを作成」を押し、コピーを作って使用してください。<br>このファイルは全員が見れてしまいます。</h5>
<h5>○リボンから「ツール」を選択→「音声入力」を選択→音声入力のマイクを押して、口述してください。<br>safariは、音声入力の項目がでてきません。Google chromeをお使いください。</h5>
<h5>○入力を終えたら、リボンの「カルテ用に整理する」を選択→「実行」を押す→表が作られ、EXCELがダウンロードされます。ダウンロードがされない場合は、Google drive内のGoogle spreadsheetを見てください。</h5>
<h5>○上記を実行するにあたり、承認が必要です。「このアプリは確認されていません」と出ますが、詳細を押し、安全ではないページに進み認証を進めてください。一度認証したらその後はでてきません。</h5>
<h3>2.口述の仕方</h3>
<h5>「〇〇(後述する項目)、△△。つぎへ。」のように、項目を宣言してから、話して、「次へ」or「次は」で区切って下さい。</h5>
<h5>例:「体温37度。<br>つぎは、体重7kg。つぎへ。<br>症状、2日前から下痢。今日から嘔吐。つぎへ。」<br>項目のあとに「は」「が」を入れないでください。</h5>
<h5>ダメな例:「体温は、37度、次へ」<br>この場合には、「は、37度」が登録されます。</h5>
<h3>3.項目</h3>
<h5>担当<br>患者番号<br>名前<br>住所<br>電話番号<br>動物<br>性別<br>生年月日<br>年齢<br>ブリード<br>主訴<br>既往歴<br>体重<br>ボディコンデションスコア<br>体温
<br>CRT(毛細血管再充満時間)<br>心拍数<br>呼吸数<br>聴診<br>触診<br>症状<br>所見<br>処置<br>問題点<br>計画<br>検査結果<br>投薬<br>次回
</h5>

</div>



</body>
</html>


ダウンロードしたエクセルと電カルをRPAでつないだら、結構使えるのではないか思います!

Categories:

category