見出し画像

J-OSLER 症例登録の自動入力

*2023年10月以降更新してません。

J-OSLERの症例登録では、IDや施設名などを繰り返し160例にわたるまで入力することになります。
コピー可能な箇所もありますが、ポップアップウインドウを開いて選ばなくてはいけない項目も多くかなり骨が折れます。

手入力を回避するためにプログラミングの勉強を始めたところ、基本的なデータの入力はなんとかなったので学習内容の整理として記載します。

なお本記事によって生じた不都合・有害事象に関して責任は一切負いません。


0. まえがき

ここで紹介する方法は
csv/excelファイルで保存された退院サマリの患者ID・年齢・入院日等を自動入力する方法
です。

1. Pythonの環境を用意

まずはPythonが使える環境をつくります。

  1. Homebrewをインストール

  2. Homebrewを用いてpyenvをインストール

  3. pyenvを用いてPythonをインストール

以上のページの通りです。


2. Chromeを自動で動かす

SeleniumというWebスクレイピングのツールを用いてGoogle Chromeを動かします。

  1. pipをインストール

  2. seleniumをインストール

  3. ChromeDriverをインストール *chromeのバージョンが変わると変更が必要

  4. Jupyter Notebookをインストール

3. 自動入力したい項目の要素を取得

文字列を入力させる場合
Chrome上で入力したいテキストボックスで右クリックをして「検証」を選択します. 
たとえばJ-Oslerのログイン画面でID入力欄で「検証」を選択すると次のように表されています。

<input id="formLogin:loginCd" type="text" name="formLogin:loginCd" size="25">

これで入力欄のnameは"formLogin:loginCd"ということがわかったため

driver.find_element(By.NAME, 'formLogin:password').send_keys("自分のID")

によってID入力欄を取得してそこに自分のIDを入力することができます。

ボタンをクリックさせる場合
例えば最初のログイン画面の「ログイン」ボタンで「検証」を押すと、nameが"formLogin:login"であることがわかります。

driver.find_element(By.NAME, 'formLogin:login').click()

で「ログインボタン」をクリックできます。

ポップアップウインドウを開く場合 
施設名などは readonly となっており、上のようなinputタグのidやnameで検索しても直接の入力はできません(たぶん)
ポップアップウインドウを開いてその中で選ぶ必要があります。

施設選択の虫眼鏡マークのボタンで「検証」を押すと以下の部分があります

<a href="#" onclick="shisetsuPopupClick(■■■)"><img src="/josler/images/h_icon_search.png" alt="検索" class="icon_calendar"></a>

ここで
onlick="shisetsuPopupClick(■■■)"
の太字の部分を丸々コピーし

driver.execute_script("shisetsuPopupClick(■■■)")

とすると施設選択のポップアップウインドウを開くことができます。

施設選択
施設選択画面が開けたら「選択」ボタンで「検証」を選択します。
<input type="button" name="shisetsuSentaku:shisetsuDataTable~~~" value="選択" class="btn_common" onclick="chooeseShisetsuPopupClose(■■■); doubleclickfunction(this);">
と書かれている部分があるので 太字の部分をコピーして

driver.execute_script("chooeseShisetsuPopupClose(■■■); doubleclickfunction(this);") 

と入力すると施設名を選ぶことができます。

疾患項目の入力
同様に選びたい項目の「要素」からonlick="■■■"の部分を探して

driver.execute_script("■■■")

と入れると選択が可能です。

*新しく開いたウインドウに自動で移るわけではないため、ポップアップウインドウを開いた後に一番最後のウインドウに遷移させる必要があります。

driver.switch_to.window(driver.window_handles[-1])


医学的プロブレムの入力

textarea タグで書かれておりinputタグのものと異なります。
以下のコードで入力可能です。

shindan=l[r-1][l[0].index('最終診断名')]

以上で「社会的プロブレム」「症例の概略」「症例を経験しての自己省察」「指導医へのコメント」以外は入力可能です。
作成段階では現病歴・所見・入院後経過をまとめたものをクリップボードにコピーして終わる仕様にしており、適宜それを用いつつ各項目を入力・修正するという方法にしています。

4. Excel/csvファイルの内容を取得

Excel/csvファイルの中から各項目(患者ID・入院日・退院日・年齢・性別・現病歴・検査所見・入院後経過)を取得します。

まずexcel形式の退院サマリをcsvファイルとして保存してください。
そこから■■■行目のデータを取り出します。

#csvファイルを開いて r行目のデータを取り出す
r = int(input("■行目のデータを取得します:"))

import csv
import pprint
with open('/Users/███/███.csv') as f:
    reader = csv.reader(f)
    l = [row for row in reader]   

ファイルの場所がわからない場合は、Finderでcsvファイルが開かれたフォルダを開いてターミナルにドラッグすると

/Users/■■■/Downloads/■■■.csv

のようにファイルがある場所を取得できます。

以下のようにして
特定の行の患者ID・入院日・退院日・年齢・性別・現病歴・検査所見・入院後経過を取得します。

#r行目の患者ID・入院日・退院日・年齢・性別・現病歴・検査所見・入院後経過を取得 
    
    kanjaid=  l[r-1][l[0].index('患者ID')]
    ukemochiKikanFr = l[r-1][l[0].index('入院日')]
    ukemochiKikanTo= l[r-1][l[0].index('退院日')]
    nenrei = l[r-1][l[0].index('患者年齢')]
    seibetsu = l[r-1][l[0].index('性別')]
    genbyo = l[r-1][l[0].index('現病歴')]
    shoken = l[r-1][l[0].index('検査所見')]
    keika=l[r-1][l[0].index('入院後経過')]
    shindan=l[r-1][l[0].index('最終診断名')]

5. 完成例

あくまで一例です。
経験時期のデフォルトは「専門研修」となっていることに注意してください。

以下を■■■の箇所を書き換えてからjupyter notebookにコピペして"Run"させると、自動でchromeの新しいウインドウが開いて入力がはじまります。

下の例では r = 1 なのでexcel上で2行目の情報を入力します。
残った空欄の記入を終えたら保存し、次は r=2 で動かして3行目の情報を入力してください。
*サーバー負荷の点から自動で次の行に移って次々に保存する方法はとっていません。

# 必要なライブラリをインポート
import csv
import pprint
import time
from selenium import webdriver
import pyperclip

#csvファイルを開いて r行目のデータを取り出す
r = int(input("■行目のデータを取得します:"))

import csv
import pprint
with open('/███/███.csv') as f:
    reader = csv.reader(f)
    l = [row for row in reader]   
    
#「r」行目の患者ID・入院日・退院日・年齢・性別・現病歴・検査所見・入院後経過を取得 
    
    kanjaid=  l[r-1][l[0].index('患者ID')]
    ukemochiKikanFr = l[r-1][l[0].index('入院日')]
    ukemochiKikanTo= l[r-1][l[0].index('退院日')]
    nenrei = l[r-1][l[0].index('患者年齢')]
    seibetsu = l[r-1][l[0].index('性別')]
    genbyo = l[r-1][l[0].index('現病歴')]
    shoken = l[r-1][l[0].index('検査所見')]
    keika=l[r-1][l[0].index('入院後経過')]
    shindan=l[r-1][l[0].index('最終診断名')]
#年齢を年のみに修正
    nenrei=nenrei.split('歳')[0]
    
#月・日を2桁に修正    
    ukemochiKikanFr = l[r][1]
    ukemochiKikanFr = ukemochiKikanFr.split('/')
    nenFr =  ukemochiKikanFr[0]
    tsukiFr =  ukemochiKikanFr[1]
    hiFr =   ukemochiKikanFr[2]
    ukemochikiKanFr = nenFr+'/'+tsukiFr.zfill(2)+'/'+hiFr.zfill(2)
        
    ukemochiKikanTo = l[r][2]
    ukemochiKikanTo = ukemochiKikanTo.split('/')
    nenTo=  ukemochiKikanTo[0]
    tsukiTo =  ukemochiKikanTo[1]
    hiTo =   ukemochiKikanTo[2]
    ukemochikiKanTo = nenTo+'/'+tsukiTo.zfill(2)+'/'+hiTo.zfill(2)
#seleniumでgoogle driverを用いてoslerを開いてログイン
from selenium import webdriver
driver = webdriver.Chrome(executable_path = '/Users/■■■/Downloads/chromedriver')
driver.get('https://web.j-osler.jp/josler/cm0101/login.html')
driver.find_element_by_name('formLogin:loginCd').send_keys("■■■自分のID■■■")
driver.find_element_by_name('formLogin:password').send_keys("■■■自分のパスワード■■■")
driver.find_element_by_name('formLogin:login').click()
# 症例登録の画面を開く
driver.find_element_by_id("closer_0").click()
driver.find_element_by_id("d1_0").click()
# 経験時期:「専門研修(プログラム研修)」の選択
driver.find_element_by_name("shoreiHyokaIraiForm:keikenJiki").click()
# 受け持ち期間の入力 *サマリの入院日・退院日を入力
driver.find_element_by_name("shoreiHyokaIraiForm:ukemochiKikanFr").send_keys(nenFr+'/'+tsukiFr.zfill(2)+'/'+hiFr.zfill(2))
driver.find_element_by_name("shoreiHyokaIraiForm:ukemochiKikanTo").send_keys(nenTo+'/'+tsukiTo.zfill(2)+'/'+hiTo.zfill(2))
# 受け持ち患者のID・年齢・担当状況・所属科
driver.find_element_by_name("shoreiHyokaIraiForm:kanjaId").send_keys(kanjaid)
driver.find_element_by_name("shoreiHyokaIraiForm:ukemochiJiKanjaNenrei").send_keys(nenrei)
driver.find_element_by_name("shoreiHyokaIraiForm:tantoJokyo").click()
driver.find_element_by_name("shoreiHyokaIraiForm:shozokuKa").send_keys('■■■所属科■■■')
# 受け持ち患者の性別の選択
if seibetsu == '男性':
    driver.find_element_by_id("shoreiHyokaIraiForm:seibetsu:0").click()
else:
    driver.find_element_by_id("shoreiHyokaIraiForm:seibetsu:1").click()
# 患者の施設名
driver.execute_script("shisetsuPopupClick(■■■)")
import time
time.sleep(0.3)
driver.switch_to.window(driver.window_handles[-1])
time.sleep(0.3)
driver.execute_script("chooeseShisetsuPopupClose(■■■);doubleclickfunction(this);")
# 指導医の施設名
driver.switch_to.window(driver.window_handles[-1])
driver.execute_script("shisetsuPopupClick(■■■)")
time.sleep(0.3)
driver.switch_to.window(driver.window_handles[-1])
driver.execute_script("chooeseShisetsuPopupClose(■■■);")
# 指導医の氏名
driver.switch_to.window(driver.window_handles[-1])
driver.execute_script("userPopupClick(■■■)")
time.sleep(0.3)
driver.switch_to.window(driver.window_handles[-1])
driver.execute_script("userPopupClose(■■■)")
# 疾患項目の入力 (悪性リンパ腫の場合)
time.sleep(0.3)
driver.switch_to.window(driver.window_handles[-1])
driver.execute_script("ryoikiPopupClick('shoreiHyokaIraiForm:ryoiki','shoreiHyokaIraiForm:ryoikiKubun','shoreiHyokaIraiForm:shikkanGunCd','shoreiHyokaIraiForm:shikkanKomokuCd','shoreiHyokaIraiForm:kaisoKubunLv1','shoreiHyokaIraiForm:kaisoKubunLv2','shoreiHyokaIraiForm:kaisoKubunLv3','shoreiHyokaIraiForm:shikkanKomoku','shoreiHyokaIraiForm:shikkanKomokuHidden')")
time.sleep(0.3)
driver.switch_to.window(driver.window_handles[-1])
driver.execute_script("ryoikiPopupClose('血液','shoreiHyokaIraiForm:ryoiki','10','shoreiHyokaIraiForm:ryoikiKubun','2','shoreiHyokaIraiForm:shikkanGunCd','023','shoreiHyokaIraiForm:shikkanKomokuCd','02','shoreiHyokaIraiForm:kaisoKubunLv1','07','shoreiHyokaIraiForm:kaisoKubunLv2','','shoreiHyokaIraiForm:kaisoKubunLv3','白血球系疾患','7)悪性リンパ腫(Hodgkinリンパ腫,非Hodgkinリンパ腫)','','shoreiHyokaIraiForm:shikkanKomoku','shoreiHyokaIraiForm:shikkanKomokuHidden')")
# 医学的プロブレムの入力
element = driver.find_element(By.ID, "shoreiHyokaIraiForm:omonaIgakuTekiProblem1_input")
driver.execute_script("arguments[0].style.display = 'block';", element)
element.send_keys(shindan)
# 現病歴・所見・入院後経過をクリップボードにコピー
import pyperclip    
all = genbyo + shoken + keika
s = ''.join(all)
pyperclip.copy(s)

以上です。
プログラミングのわかる方は直すべき箇所をコメントで教えていただけると嬉しいです。

この記事が気に入ったらサポートをしてみませんか?