adm18/IMPAX/download.py
2025-09-16 13:20:19 +08:00

665 lines
23 KiB
Python

from pathlib import PureWindowsPath
import base64
import datetime
import re
import time
from appium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from models import *
MIN_NUM_IMAGES = 9
RootPath = PureWindowsPath(r'S:\storage\0')
RunStamp = base64.urlsafe_b64encode(int(time.time()).to_bytes(4,'big')).decode().replace('=','')
RunPath = RootPath/RunStamp
print(RunStamp, RunPath)
session = Session()
def launchApp(desired_caps):
# desired_caps["ms:experimental-webdriver"] = True
# dut_url = "http://win10z:4723"
dut_url = "http://192.168.11.101:4723"
driver = webdriver.Remote(
command_executor = dut_url,
desired_capabilities = desired_caps)
return driver
def getDriverFromWin( win):
win_handle1 = win.get_attribute("NativeWindowHandle")
win_handle = format(int(win_handle1), 'x') # convert to hex string
# Launch new session attached to the window
desired_caps = {}
desired_caps["appTopLevelWindow"] = win_handle
driver = launchApp(desired_caps)
driver.switch_to.window(win_handle)
return driver
def getDict(row):
d = {}
# num_images = row.find_elements_by_name('study.num_images')
# if not num_images or int(num_images[0] < 9):
# return d
# for e in row.find_elements_by_xpath('//DataItem'):
for e in row.find_elements_by_tag_name('DataItem'):
# d[e.get_attribute('Name')] = e.get_attribute('Value.Value')
name = e.get_attribute('Name').strip()
value = e.get_attribute('Value.Value')
if value:
value = value.strip()
d[name] = value
# if name == 'study.num_images' and int(value)<MIN_NUM_IMAGES:
# return d
return d
# Launch a driver for the Windows Desktop (Root)
desired_caps = {}
desired_caps["app"] = "Root"
desktop = launchApp(desired_caps)
# Input and check (Chinese?)
def edit_input(control, text):
control.send_keys(text)
time.sleep(1)
if control.text != text: # Chinese input method?
control.send_keys(Keys.LEFT_SHIFT)
control.send_keys(Keys.BACKSPACE * len(text))
control.send_keys(text)
if control.text != text:
print('Error in edit_input',control,text)
exit()
control.send_keys(Keys.ALT, Keys.F4)
def CloseItemPanel():
for i in range(9):
OpenItemPanel = desktop.find_elements_by_accessibility_id("OpenItemPanel")
if not OpenItemPanel:
break
print('CloseItemPanel', i)
ActionChains(desktop).move_to_element(OpenItemPanel[0]).perform()
button = OpenItemPanel[0].find_element_by_xpath('//Button')
button.click()
# exit()
def check_error():
button = desktop.find_elements_by_xpath('*/Window[@Name="錯誤"]/Button[@Name="確定"]')
if button:
print (button[0].text)
button[0].send_keys(Keys.ENTER)
# view = WebDriverWait(desktop, 20).until(
# EC.presence_of_element_located((By.ACCESSIBILITY_ID, "LoginView"))
# )
button = desktop.find_elements_by_xpath('*/Window[@Name="發生錯誤"]/Button[@Name="確定"]')
if button:
print (button[0].text)
button[0].send_keys(Keys.ENTER)
view = WebDriverWait(desktop, 20).until(
EC.presence_of_element_located((By.ACCESSIBILITY_ID, "LoginView"))
)
view = desktop.find_elements_by_accessibility_id('ApplicationView')
# Close Viewer
CloseItemPanel()
# if view:
# view[0].send_keys(Keys.ALT, Keys.F4)
# exit()
# Login if necessary, return ApplicationView
def login():
ID = '004552'
password = 'n122119493'
win = desktop.find_elements_by_accessibility_id('ApplicationView')
if win:
win[0].send_keys(Keys.ALT, Keys.F4)
time.sleep(5)
# view = desktop.find_element_by_accessibility_id('LoginView')
# Start app as Administrator
try:
view = desktop.find_element_by_accessibility_id('LoginView')
except:
desired_caps = {}
desired_caps["app"] = r"C:\Program Files (x86)\AGFA\IMPAX Client\6.5.2.114\impax-client-main.exe"
view = launchApp(desired_caps).find_element_by_accessibility_id('LoginView')
userid = view.find_element_by_accessibility_id('userIDText')
edit_input(userid, ID)
# userid.send_keys(ID)
# if userid.text != ID: # Chinese input method?
# userid.send_keys(Keys.LEFT_SHIFT)
# userid.send_keys(Keys.BACKSPACE * len(ID))
# userid.send_keys(ID)
view.find_element_by_accessibility_id('passwordText').send_keys(password)
view.find_element_by_accessibility_id('loginButton').click()
win = WebDriverWait(desktop, 20).until(
EC.presence_of_element_located((By.ACCESSIBILITY_ID, "ApplicationView"))
)
return win
# win = desktop.find_element_by_name('IMPAX 6.5.2.114 Enterprise Unlimited')
# print(win)
# driver = getDriverFromWin(win)
# print(driver)
check_error() # also kill ApplicationView
win = login()
# try:
# win = desktop.find_element_by_accessibility_id('ApplicationView')
# except:
# login()
# win = WebDriverWait(desktop, 20).until(
# EC.presence_of_element_located((By.ACCESSIBILITY_ID, "ApplicationView"))
# )
# print(win)
driver = getDriverFromWin(win)
# driver = win #AttributeError: 'WebElement' object has no attribute 'w3c' (ActionChains)
print(driver)
def saveSeries(quality, exportPath):
position = driver.find_element_by_accessibility_id('StudyImageViewer').find_element_by_name("Screen_0_format_5_Position_0_0")
# ActionChains(driver).context_click(driver.find_element_by_name("Screen_0_format_5_Position_0_0")).perform()
ActionChains(driver).context_click(position).perform()
# try:
# DisplayMenuForm = desktop.find_element_by_accessibility_id('DisplayMenuForm0')
# except:
# DisplayMenuForm = desktop.find_element_by_accessibility_id('DisplayMenuForm0')
DisplayMenuForm = WebDriverWait(desktop, 20).until(
EC.presence_of_element_located((By.ACCESSIBILITY_ID, "DisplayMenuForm0"))
)
# time.sleep(1)
# # desktop.find_elements_by_xpath('*')
# ActionChains(desktop).send_keys(Keys.ARROW_DOWN).perform()
# ActionChains(desktop).send_keys(Keys.ARROW_DOWN).perform()
# DisplayMenuForm = desktop.find_element_by_accessibility_id('DisplayMenuForm0')
# desktop2 = launchApp(desired_caps)
# DisplayMenuForm = desktop.find_element_by_accessibility_id('DisplayMenuForm0')
# DisplayMenuForm = desktop.find_element_by_name('DisplayMenuForm')
# print(DisplayMenuForm, DisplayMenuForm.tag_name, DisplayMenuForm.text)
ActionChains(desktop).move_to_element(DisplayMenuForm.find_element_by_name('輸出')).perform()
ActionChains(desktop).click(DisplayMenuForm.find_element_by_name('影像序列')).perform()
# export = WebDriverWait(DisplayMenuForm, 20).until(
# # EC.presence_of_element_located((By.NAME, "影像序列"))
# EC.element_to_be_clickable((By.NAME, "影像序列"))
# )
# export.click()
# DisplayMenuForm.find_element_by_name('影像序列').click()
view = WebDriverWait(driver, 20).until(
EC.presence_of_element_located((By.ACCESSIBILITY_ID, "JPEGDlgView"))
)
# view = driver.find_element_by_accessibility_id('JPEGDlgView')
cb = view.find_element_by_xpath('//ComboBox[2]')
edit = cb.find_element_by_xpath('//Edit')
up = cb.find_element_by_name('Up')
down = cb.find_element_by_name('Down')
while int(edit.get_attribute('Value.Value')) < quality:
up.click()
while int(edit.get_attribute('Value.Value')) > quality:
down.click()
export = view.find_element_by_xpath(r'//Edit[@Name="匯出位置:"]')
while len(export.text):
export.send_keys(Keys.BACKSPACE)
export.send_keys(str(exportPath)+'\\')
# exit()
driver.find_element_by_accessibility_id('btnOK').click()
driver.find_element_by_accessibility_id('btnCancel').click()
# desktop2.close()
# desktop2.quit()
def screen_1x1():
# top = WebDriverWait(driver, 600).until(
# EC.presence_of_element_located((By.ACCESSIBILITY_ID, "TOP_TOOLBAR0"))
# )
box = desktop.find_elements_by_accessibility_id('GenericDialogBox')
if box:
box[0].find_element_by_name("確定").click()
# if driver.find_element_by_accessibility_id('StudyImageViewer').find_elements_by_name("Screen_0_format_5_Position_0_0"):
# return
top = driver.find_element_by_accessibility_id("TOP_TOOLBAR0")
button = top.find_element_by_xpath('//Button[5]')
ActionChains(driver).click(button).perform()
AfxWnd90u = driver.find_element_by_xpath(r'//Pane[@ClassName="AfxWnd90u"][1]')
ActionChains(driver).click(AfxWnd90u).perform()
return
# desktop = launchApp({'app': 'Root'})
# # position = driver.find_element_by_accessibility_id('StudyImageViewer').find_element_by_name("Screen_0_format_5_Position_0_0")
# box = desktop.find_elements_by_accessibility_id('GenericDialogBox')
# if box:
# box[0].find_element_by_name("確定").click()
# for i in range(3):
# desktop = launchApp({'app': 'Root'})
# win = desktop.find_element_by_accessibility_id('ApplicationView')
# d2 = getDriverFromWin(win)
# StudyImageViewer = d2.find_elements_by_accessibility_id('StudyImageViewer')
# if StudyImageViewer:
# position = StudyImageViewer[0].find_elements_by_name("Screen_0_format_5_Position_0_0")
# if position:
# break
# print('screen 1x1', i)
# top = d2.find_elements_by_accessibility_id("TOP_TOOLBAR0")
# if top:
# button = top[0].find_element_by_xpath('//Button[5]')
# ActionChains(d2).click(button).perform()
# AfxWnd90u = d2.find_element_by_xpath(r'//Pane[@ClassName="AfxWnd90u"][1]')
# ActionChains(d2).click(AfxWnd90u).perform()
pattern_series_uid = r'series_uid ] = "([.0-9T]+)"'
prog_series_uid = re.compile(pattern_series_uid)
def getSeriesDocument():
screen_1x1()
position = driver.find_element_by_accessibility_id('StudyImageViewer').find_element_by_name("Screen_0_format_5_Position_0_0")
# position = screen_1x1()
ActionChains(driver).key_down(Keys.LEFT_CONTROL).context_click(position).key_down(Keys.LEFT_CONTROL).perform()
ActionChains(desktop).send_keys(Keys.ARROW_DOWN).perform()
ActionChains(desktop).send_keys(Keys.ARROW_DOWN).perform()
DisplayMenuForm = desktop.find_element_by_accessibility_id('DisplayMenuForm0')
ActionChains(desktop).move_to_element(DisplayMenuForm.find_element_by_name('資訊')).perform()
# ActionChains(desktop).click(DisplayMenuForm.find_element_by_name('服務視窗')).perform()
DisplayMenuForm.find_element_by_name('服務視窗').click()
# DisplayMenuForm.find_element_by_accessibility_id('lr_informationStrip').click()
# DisplayMenuForm.find_element_by_accessibility_id('資訊Strip_SubMenu').click()
position.click()
tool = driver.find_element_by_name("Display Service Tool")
doc = []
for e in tool.find_elements_by_xpath("//Document"):
# print(e, e.tag_name, e.text, e.get_attribute('AutomationId'))
doc.append(e.text)
result = prog_series_uid.search(doc[1])
print(result)
# [ series, date_time_modified ] = 1453532533 ( 4, 4 )\r\n
# [ series, series_number ] = "3" ( 1, 2 )\r\n
# [ series, manufacturer ] = "GE MEDICAL SYSTEMS" ( 18, 19 )\r\n
# [ series, series_ref ] = 31044392 ( 8, 8 )\r\n
# [ series, manufacturer_model_name ] = "Signa HDxt" ( 10, 11 )\r\n
# [ series, series_time ] = "150149" ( 6, 7 )\r\n
# [ series, series_uid ] = "1.2.840.113619.2.244.6945.1127238.26918.1453507651.969" ( 54, 55 )\r\n
# [ series, modality ] = "MR" ( 2, 3 )\r\n
# [ series, station_name ] = "CGEMR1C1" ( 8, 9 )\r\n
# [ series, study_ref ] = 29907821 ( 8, 8 )\r\n
# [ series, series_date ] = "20160123" ( 8, 9 )\r\n
# [ series, date_time_created ] = 1453532533 ( 4, 4 )\r\n
# [ series, series_description ] = "Ax T2 FLAIR" ( 11, 12 )
tool.find_element_by_accessibility_id('Close').click()
ActionChains(driver).context_click(position).perform()
return result[1], doc
def getStudyByViewer(accessionNumber, studyPath):
# ### Get study report last
# driver.find_element_by_accessibility_id('ShowTextArea').click()
# # driver.find_element_by_accessibility_id('reportTab').click()
# report = driver.find_element_by_accessibility_id('reportControl').find_element_by_xpath('//Document').text
# driver.find_element_by_accessibility_id('_textAreaHideButton').click()
# print(report)
# exit()
# time.sleep(1)
# Make it 1x1 view, Try to moved to saveSeries and getSeriesDocument?
# driver.find_element_by_accessibility_id("TOP_TOOLBAR0").find_element_by_xpath('//Button[5]').click()
# driver.find_element_by_xpath(r'//Pane[@ClassName="AfxWnd90u"][1]').click()
# top = WebDriverWait(driver, 20).until(
# EC.presence_of_element_located((By.ACCESSIBILITY_ID, "TOP_TOOLBAR0"))
# )
screen_1x1()
# top = win.find_element_by_accessibility_id("TOP_TOOLBAR0")
# button = top.find_element_by_xpath('//Button[5]')
# button.click()
# AfxWnd90u = win.find_element_by_xpath(r'//Pane[@ClassName="AfxWnd90u"][1]')
# AfxWnd90u.click()
# driver = getDriverFromWin(win)
items = driver.find_elements_by_xpath(r'//ListItem[@AutomationId="ListViewItem-0"]/../ListItem')
if len(items) == 0:
toolbar = driver.find_element_by_accessibility_id("STANDARD_TOOLBAR0")
toolbar.find_element_by_xpath('//Button[1]').click()
items = driver.find_elements_by_xpath(r'//ListItem[@AutomationId="ListViewItem-0"]/../ListItem')
num_images = 0
for c in driver.find_elements_by_xpath(r'//ListItem[@AutomationId="ListViewItem-0"]/../ListItem'):
# AutomationId =
print(c.get_attribute('AutomationId'), c.tag_name, c.text)
ActionChains(driver).double_click(c).perform()
name = c.text
num_images += int(name.split(' : ')[1])
uid, doc = getSeriesDocument()
# print(doc)
# print(uid)
s = session.query(Series).get(uid)
if s is None:
s = Series(series_uid = uid)
session.add(s)
s.name = name
s.document0 = doc[0]
s.document1 = doc[1]
s.accession_number = accessionNumber
# p.saved = datetime.datetime.now()
session.commit()
# Save Demographics shown
saveSeries(100, studyPath / 'shown' / s.slugify())
driver.find_element_by_accessibility_id("TOP_TOOLBAR0").find_element_by_xpath('//Button[1]').click()
# Save Demographics hidden
saveSeries(100, studyPath / 'hidden' / s.slugify())
driver.find_element_by_accessibility_id("TOP_TOOLBAR0").find_element_by_xpath('//Button[1]').click()
### Get study report last
driver.find_element_by_accessibility_id('ShowTextArea').click()
try:
report = driver.find_element_by_accessibility_id('reportControl').find_element_by_xpath('//Document').text
except:
report = None
driver.find_element_by_accessibility_id('_textAreaHideButton').click()
s = session.query(Study).get(accessionNumber)
if s is None:
s = Study(accession_number = accessionNumber)
session.add(s)
s.report = report
if s.num_images == num_images and (report or (datetime.date.today()-s.study_date).days>99):
s.success = True
s.run = RunStamp
session.commit()
def getStudyByGrid(study, patientPath):
# print(patientPath)
# exit()
# print(driver.find_element_by_accessibility_id("TOP_TOOLBAR0"))
driver.find_element_by_accessibility_id("resetButton").click()
# driver.find_element_by_accessibility_id("accessionNumberText").find_element_by_xpath(r'//Edit[@Name="檢查流水號"]').send_keys(accessionNumber)
accession = win.find_element_by_accessibility_id('accessionNumberText').find_element_by_tag_name('Edit')
edit_input(accession, study.accession_number.replace('@', ''))
# accession.send_keys(study.accession_number)
# time.sleep(1)
# if accession.text != study.accession_number: # Chinese input method?
# accession.send_keys(Keys.LEFT_SHIFT)
# accession.send_keys(Keys.BACKSPACE * len(study.accession_number))
# accession.send_keys(study.accession_number)
driver.find_element_by_accessibility_id("searchButton").click()
grid = driver.find_element_by_accessibility_id("grid")
# print(1)
# for i, e in enumerate(grid.find_elements_by_xpath('*')):
# print(e, e.tag_name, e.text)
# exit()
# print(2)
for i, e in enumerate(grid.find_elements_by_xpath('//DataItem[@Name="View"]/..')):
print(i, e.tag_name)
d = getDict(e)
print(d)
if 'study.num_images' not in d or int(d['study.num_images']) < MIN_NUM_IMAGES:
continue
# 取回 nearline, But dont wait
if d['View'] == 'nearline':
ActionChains(driver).context_click(e.find_element_by_name('View')).perform()
# desktop.find_element_by_xpath(r'Pane/Menu[@Name="路徑位置"]/MenuItem[@Name="取回"]').click()
desktop.find_element_by_name('路徑位置').find_element_by_name("取回").click() # faster
print('skip nearline',study.accession_number)
return
ActionChains(driver).double_click(e.find_element_by_name('View')).perform()
# print(study.naming())
# exit()
# getStudyByViewer(study.accession_number, patientPath/(datetime.datetime.strptime(d['study.study_date'], '%Y/%m/%d').strftime('%Y-%m-%d-')+accessionNumber))
getStudyByViewer(study.accession_number, patientPath/study.naming())
#close image viewer
CloseItemPanel()
# exit()
# "/Pane[@ClassName=\"#32769\"][@Name=\"桌面 1\"]/Window[@Name=\"IMPAX 6.5.2.114 Enterprise Unlimited\"][@AutomationId=\"ApplicationView\"]/Pane[@AutomationId=\"displayPanel\"]/Pane[@AutomationId=\"DisplayView\"]/Pane[@Name=\"TOP_TOOLBAR0\"][starts-with(@AutomationId,\"TOP_TOOLBAR\")]/Button[starts-with(@ClassName,\"WindowsForms10\")]";
def getPatient(ID, rescanStudy = True):
if rescanStudy:
driver.find_element_by_accessibility_id("resetButton").click()
# patientId = win.find_element_by_accessibility_id('patientIdText').find_element_by_xpath(r'//Edit[@Name="病歷號碼"]')
# patientId = win.find_element_by_accessibility_id('patientIdText').find_element_by_tag_name('Edit')
patientId = driver.find_element_by_accessibility_id('patientIdText').find_element_by_tag_name('Edit')
edit_input(patientId, ID)
# patientId.send_keys(ID)
# time.sleep(1)
# if patientId.text != ID: # Chinese input method?
# patientId.send_keys(Keys.LEFT_SHIFT)
# patientId.send_keys(Keys.BACKSPACE * len(ID))
# patientId.send_keys(ID)
# driver.find_element_by_accessibility_id("patientIdText").find_element_by_xpath(r'//Edit[@Name="病歷號碼"]').send_keys(patientId)
driver.find_element_by_accessibility_id("searchButton").click()
grid = driver.find_element_by_accessibility_id("grid")
try:
DownButton = grid.find_element_by_accessibility_id("DownButton")
except:
DownButton = None
print(1)
# for i, e in enumerate(grid.find_elements_by_xpath('*/*')):
# for i, e in enumerate(grid.find_elements_by_xpath('//DataItem[@Name="View"]/..')):
for i, e in enumerate(grid.find_elements_by_xpath('*/Custom')):
print(i, e.tag_name, e.text)
if not e.text.isdigit():
continue
d = getDict(e)
print(d)
if ('study.accession_number' not in d) or not d['study.accession_number']:
continue
p = session.query(Patient).get(d['patient.patient_id'])
if p is None:
p = Patient(patient_id = d['patient.patient_id'])
session.add(p)
p.patient_name = d['patient.patient_name']
p.patient_name_ph_utf8 = d['patient.patient_name_ph_utf8']
# p.saved = datetime.datetime.now()
session.commit()
s = session.query(Study).get(d['study.accession_number'])
if s is None:
s = Study(accession_number = d['study.accession_number'])
session.add(s)
# else:
# #skip study already success
# if s.success:
# continue
s.date_time_created = d['study.date_time_created']
s.modality = d['study.modality']
s.num_images = d['study.num_images']
s.status = d['study.status']
s.study_date = datetime.datetime.strptime(d['study.study_date'], '%Y/%m/%d')
s.study_description = d['study.study_description']
s.study_time = d['study.study_time']
s.patient_id = d['patient.patient_id']
# p.saved = datetime.datetime.now()
session.commit()
# if int(d['study.num_images']) < MIN_NUM_IMAGES:
# continue
# if 'View'in d and d['View']==None:
# driver.find_element_by_accessibility_id("DownButton").click()
# exit()
# continue
if (not s.success) and s.num_images > MIN_NUM_IMAGES:
# if d['View'] not in ['Available', 'VIEWABLE', 'VIEWING']:
if d['View'] == 'nearline':
ActionChains(driver).context_click(e.find_element_by_name('View')).perform()
# desktop.find_element_by_xpath(r'Pane/Menu[@Name="路徑位置"]/MenuItem[@Name="取回"]').click()
desktop.find_element_by_name('路徑位置').find_element_by_name("取回").click() # faster
# getDriverFromWin(desktop.find_element_by_name('路徑位置')).find_element_by_name("取回").click()
# print(desktop.find_element_by_xpath(r'Pane/Menu[@Name="路徑位置"]/MenuItem[@Name="取回"]'))
# exit()
if DownButton:
DownButton.click()
if DownButton:
UpPageButton = grid.find_element_by_accessibility_id("UpPageButton")
try:
while True:
UpPageButton.click()
except:
pass
if p.counter is None:
p.counter = 1
else:
p.counter += 1
session.commit()
for study in session.query(Patient).get(ID).studies:
if study.success or study.num_images < MIN_NUM_IMAGES:
continue
getStudyByGrid(study, RunPath/ID)
if __name__ == '__main__':
rescan = True
getPatient('3009684', rescanStudy=rescan)
# getPatient('2347157', rescanStudy=rescan)
# getStudyByViewer('T0159343504', r'S:\storage\0')
# getStudyByGrid('T0159343504', r'S:\storage\0')