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