Skip to content

Commit 2f0c315

Browse files
committed
add IDCRequestHandler
1 parent 016ef41 commit 2f0c315

File tree

1 file changed

+176
-0
lines changed

1 file changed

+176
-0
lines changed

IDCBrowser/IDCRequestHandler.py

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# slicer_server_utils.py
2+
3+
import os
4+
import subprocess
5+
import time
6+
import logging
7+
import requests
8+
from slicer.util import VTKObservationMixin
9+
import qt
10+
import slicer
11+
import ctk
12+
# IDCRequestHandler.py
13+
14+
from WebServerLib.BaseRequestHandler import BaseRequestHandler, BaseRequestLoggingFunction
15+
import urllib
16+
import os
17+
from typing import Optional
18+
from idc_index import index
19+
20+
# PORT = 2042
21+
22+
# def get_slicer_location():
23+
# launcherPath = qt.QDir.toNativeSeparators(
24+
# qt.QFileInfo(slicer.app.launcherExecutableFilePath).absoluteFilePath()
25+
# )
26+
# return launcherPath
27+
28+
# def is_server_running():
29+
# try:
30+
# response = requests.get(f"http://127.0.0.1:{PORT}/idc/collections", timeout=3)
31+
# if 'applicationName' in response.json():
32+
# return True
33+
# except Exception as e:
34+
# logging.debug("Application is not available: " + str(e))
35+
# return False
36+
37+
# def start_server(slicer_executable=None, timeoutSec=60):
38+
# # if not slicer_executable:
39+
# # if 'SLICER_EXECUTABLE' not in os.environ:
40+
# # os.environ['SLICER_EXECUTABLE'] = get_slicer_location()
41+
# # slicer_executable = get_slicer_location()
42+
# # p = subprocess.Popen([slicer_executable, "--python-code", f"wslogic = getModuleLogic('WebServer'); wslogic.port={PORT}; wslogic.enableSlicer=False; wslogic.enableStaticPages=False; wslogic.enableDICOM=True; wslogic.requestHandlers = [IDCRequestHandler()], wslogic.start()"])
43+
# # start = time.time()
44+
# # connected = False
45+
# # while not connected:
46+
# # connected = is_server_running()
47+
# # if time.time() - start > timeoutSec:
48+
# # raise requests.exceptions.ConnectTimeout("Timeout while waiting for application to start")
49+
# # return p
50+
# if not is_server_running():
51+
52+
# The following part remains unchanged
53+
# PORT = 2042
54+
# import WebServer
55+
56+
# try:
57+
# logic.stop()
58+
# except NameError:
59+
# pass
60+
61+
# logMessage = WebServer.WebServerLogic.defaultLogMessage
62+
# requestHandlers = [IDCRequestHandler()]
63+
# logic = WebServer.WebServerLogic(port=PORT, logMessage=logMessage, enableSlicer=True, enableStaticPages=False, enableDICOM=False, requestHandlers=requestHandlers)
64+
65+
# logic.start()
66+
67+
import os
68+
import subprocess
69+
import time
70+
import logging
71+
import requests
72+
from slicer.util import VTKObservationMixin
73+
import qt
74+
import slicer
75+
import ctk
76+
# IDCRequestHandler.py
77+
78+
from WebServerLib.BaseRequestHandler import BaseRequestHandler, BaseRequestLoggingFunction
79+
import urllib
80+
import os
81+
from typing import Optional
82+
from idc_index import index
83+
84+
class IDCRequestHandler(BaseRequestHandler):
85+
86+
def __init__(self, logMessage: Optional[BaseRequestLoggingFunction] = None):
87+
self.logMessage = logMessage
88+
self.client = index.IDCClient()
89+
self.index_df = self.client.index
90+
91+
# if not is_server_running():
92+
# start_server()
93+
94+
def canHandleRequest(self, uri: bytes, **_kwargs) -> float:
95+
parsedURL = urllib.parse.urlparse(uri)
96+
if parsedURL.path.startswith(b"/idc"):
97+
return 0.5
98+
return 0.0
99+
100+
def handleRequest(self, method: str, uri: bytes, requestBody: bytes, **_kwargs) -> tuple[bytes, bytes]:
101+
parsedURL = urllib.parse.urlparse(uri)
102+
splitPath = parsedURL.path.split(b"/")
103+
104+
if len(splitPath) > 2:
105+
if splitPath[2] == b"collections":
106+
return self.handleCollections()
107+
elif splitPath[2] == b"download" and splitPath[3] == b"seriesInstanceUID":
108+
series_uids = splitPath[4].decode().split(",")
109+
return self.handleDownload(series_uids)
110+
elif splitPath[2] == b"download" and splitPath[3] == b"studyInstanceUID":
111+
study_uids = splitPath[4].decode().split(",")
112+
filtered_df = self.index_df[self.index_df['StudyInstanceUID'].isin(study_uids)]
113+
series_uids_from_study_uid = filtered_df['SeriesInstanceUID'].tolist()
114+
return self.handleDownload(series_uids_from_study_uid)
115+
else:
116+
return b"text/plain", b"Unhandled IDC request path"
117+
else:
118+
return b"text/plain", b"Invalid IDC request path"
119+
120+
def handleCollections(self) -> tuple[bytes, bytes]:
121+
try:
122+
collections = self.client.get_collections()
123+
responseBody = f"Available collections: {', '.join(collections)}".encode()
124+
contentType = b"text/plain"
125+
except Exception as e:
126+
responseBody = f"Error fetching collections: {e}".encode()
127+
contentType = b"text/plain"
128+
if self.logMessage:
129+
self.logMessage(responseBody.decode())
130+
131+
return contentType, responseBody
132+
133+
def handleDownload(self, uids: list[str]) -> tuple[bytes, bytes]:
134+
destFolderPath = slicer.mrmlScene.GetCacheManager().GetRemoteCacheDirectory()
135+
136+
try:
137+
self.client.download_from_selection(seriesInstanceUID=uids, downloadDir=destFolderPath, dirTemplate="%SeriesInstanceUID")
138+
indexer = ctk.ctkDICOMIndexer()
139+
for uid in uids:
140+
download_folder_path = os.path.join(destFolderPath, uid)
141+
indexer.addDirectory(slicer.dicomDatabase, download_folder_path)
142+
plugin = slicer.modules.dicomPlugins["DICOMScalarVolumePlugin"]()
143+
dicomDatabase = slicer.dicomDatabase
144+
fileList = dicomDatabase.filesForSeries(uid.replace("'", ""))
145+
loadables = plugin.examine([fileList])
146+
if len(loadables) > 0:
147+
volume = plugin.load(loadables[0])
148+
logging.debug("Loaded volume: " + volume.GetName())
149+
else:
150+
raise Exception("Unable to load DICOM content. Please retry from DICOM Browser!")
151+
152+
responseBody = f"Downloaded and indexed UID(s): {', '.join(uids)}".encode()
153+
contentType = b"text/plain"
154+
except Exception as e:
155+
responseBody = f"Error downloading or indexing UID(s): {e}".encode()
156+
contentType = b"text/plain"
157+
if self.logMessage:
158+
self.logMessage(responseBody.decode())
159+
160+
return contentType, responseBody
161+
162+
163+
# The following part remains unchanged
164+
PORT = 2042
165+
import WebServer
166+
167+
try:
168+
logic.stop()
169+
except NameError:
170+
pass
171+
172+
logMessage = WebServer.WebServerLogic.defaultLogMessage
173+
requestHandlers = [IDCRequestHandler()]
174+
logic = WebServer.WebServerLogic(port=PORT, logMessage=logMessage, enableSlicer=True, enableStaticPages=False, enableDICOM=True, requestHandlers=requestHandlers)
175+
176+
logic.start()

0 commit comments

Comments
 (0)