Update archive_discovery.py
✅ New Features Included Live Record Count: Label shows how many studies have been written. Pause/Resume Button: Temporarily halt and resume querying without killing the session. Stop Button: Gracefully terminates the process early. Real-Time File Writing: Ensures each writer.writerow() call flushes immediately. 📝 Notes f.flush() is automatic now with buffering=1 (line-buffered mode). self.pause_event.wait() halts progress until "Resume" is pressed. self.stop_event.is_set() stops the loop immediately.
This commit is contained in:
@@ -5,7 +5,7 @@ import os
|
|||||||
import csv
|
import csv
|
||||||
import logging
|
import logging
|
||||||
import configparser
|
import configparser
|
||||||
from threading import Thread
|
from threading import Thread, Event
|
||||||
from pydicom.dataset import Dataset
|
from pydicom.dataset import Dataset
|
||||||
from pynetdicom import AE, evt, debug_logger
|
from pynetdicom import AE, evt, debug_logger
|
||||||
from pynetdicom.sop_class import StudyRootQueryRetrieveInformationModelFind
|
from pynetdicom.sop_class import StudyRootQueryRetrieveInformationModelFind
|
||||||
@@ -62,6 +62,10 @@ class ConfigDialog:
|
|||||||
self.current_profile = tk.StringVar()
|
self.current_profile = tk.StringVar()
|
||||||
self.log_level = tk.StringVar(value="INFO")
|
self.log_level = tk.StringVar(value="INFO")
|
||||||
self.status = tk.StringVar(value="Ready")
|
self.status = tk.StringVar(value="Ready")
|
||||||
|
self.record_count = tk.IntVar(value=0)
|
||||||
|
self.pause_event = Event()
|
||||||
|
self.stop_event = Event()
|
||||||
|
self.pause_event.set()
|
||||||
self.all_profiles = load_all_profiles()
|
self.all_profiles = load_all_profiles()
|
||||||
self.profile_names = self.all_profiles.sections()
|
self.profile_names = self.all_profiles.sections()
|
||||||
|
|
||||||
@@ -89,9 +93,14 @@ class ConfigDialog:
|
|||||||
self.status_label = tk.Label(self.top, textvariable=self.status, fg="blue")
|
self.status_label = tk.Label(self.top, textvariable=self.status, fg="blue")
|
||||||
self.status_label.grid(row=len(self.fields)+2, column=0, columnspan=2, padx=10, pady=5)
|
self.status_label.grid(row=len(self.fields)+2, column=0, columnspan=2, padx=10, pady=5)
|
||||||
|
|
||||||
|
self.record_label = tk.Label(self.top, text="Records Written: 0")
|
||||||
|
self.record_label.grid(row=len(self.fields)+3, column=0, columnspan=2, padx=10, pady=5)
|
||||||
|
|
||||||
button_frame = tk.Frame(self.top)
|
button_frame = tk.Frame(self.top)
|
||||||
button_frame.grid(row=len(self.fields)+3, column=0, columnspan=2, pady=10)
|
button_frame.grid(row=len(self.fields)+4, column=0, columnspan=2, pady=10)
|
||||||
tk.Button(button_frame, text="Run Query", command=self.on_run).pack(side=tk.LEFT, padx=5)
|
tk.Button(button_frame, text="Run Query", command=self.on_run).pack(side=tk.LEFT, padx=5)
|
||||||
|
tk.Button(button_frame, text="Pause", command=self.on_pause).pack(side=tk.LEFT, padx=5)
|
||||||
|
tk.Button(button_frame, text="Stop", command=self.on_stop).pack(side=tk.LEFT, padx=5)
|
||||||
tk.Button(button_frame, text="Cancel", command=self.on_cancel).pack(side=tk.RIGHT, padx=5)
|
tk.Button(button_frame, text="Cancel", command=self.on_cancel).pack(side=tk.RIGHT, padx=5)
|
||||||
|
|
||||||
if self.profile_names:
|
if self.profile_names:
|
||||||
@@ -124,9 +133,23 @@ class ConfigDialog:
|
|||||||
logging.getLogger().setLevel(LOG_LEVELS[self.log_level.get()])
|
logging.getLogger().setLevel(LOG_LEVELS[self.log_level.get()])
|
||||||
self.values['log_level'] = self.log_level.get()
|
self.values['log_level'] = self.log_level.get()
|
||||||
|
|
||||||
|
self.stop_event.clear()
|
||||||
|
self.pause_event.set()
|
||||||
thread = Thread(target=self.run_query)
|
thread = Thread(target=self.run_query)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
|
def on_pause(self):
|
||||||
|
if self.pause_event.is_set():
|
||||||
|
self.pause_event.clear()
|
||||||
|
self.update_status("Paused")
|
||||||
|
else:
|
||||||
|
self.pause_event.set()
|
||||||
|
self.update_status("Resumed")
|
||||||
|
|
||||||
|
def on_stop(self):
|
||||||
|
self.stop_event.set()
|
||||||
|
self.update_status("Stopping...")
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.top.destroy()
|
self.top.destroy()
|
||||||
|
|
||||||
@@ -136,6 +159,10 @@ class ConfigDialog:
|
|||||||
def _set_status(self, message):
|
def _set_status(self, message):
|
||||||
self.status.set(message)
|
self.status.set(message)
|
||||||
|
|
||||||
|
def update_record_count(self):
|
||||||
|
count = self.record_count.get()
|
||||||
|
self.record_label.config(text=f"Records Written: {count}")
|
||||||
|
|
||||||
def run_query(self):
|
def run_query(self):
|
||||||
config = self.values
|
config = self.values
|
||||||
self.update_status("Initializing DICOM query...")
|
self.update_status("Initializing DICOM query...")
|
||||||
@@ -172,14 +199,16 @@ class ConfigDialog:
|
|||||||
output_path = os.path.join(os.getcwd(), generate_output_filename())
|
output_path = os.path.join(os.getcwd(), generate_output_filename())
|
||||||
logging.info("Starting C-FIND loop from %s to %s", start_date, now)
|
logging.info("Starting C-FIND loop from %s to %s", start_date, now)
|
||||||
|
|
||||||
with open(output_path, 'w', newline='') as f:
|
with open(output_path, 'w', newline='', buffering=1) as f:
|
||||||
writer = csv.writer(f, delimiter='|')
|
writer = csv.writer(f, delimiter='|')
|
||||||
writer.writerow([
|
writer.writerow([
|
||||||
'PatientID', 'PatientName', 'AccessionNumber', 'StudyDate',
|
'PatientID', 'PatientName', 'AccessionNumber', 'StudyDate',
|
||||||
'StudyInstanceUID', 'ModalitiesInStudy', 'NumberOfStudyRelatedInstances'
|
'StudyInstanceUID', 'ModalitiesInStudy', 'NumberOfStudyRelatedInstances'
|
||||||
])
|
])
|
||||||
|
|
||||||
while start_date < now:
|
while start_date < now and not self.stop_event.is_set():
|
||||||
|
self.pause_event.wait()
|
||||||
|
|
||||||
end_date = start_date + timedelta(hours=1)
|
end_date = start_date + timedelta(hours=1)
|
||||||
ds = create_cfind_dataset(start_date, end_date)
|
ds = create_cfind_dataset(start_date, end_date)
|
||||||
self.update_status(f"Querying: {start_date.strftime('%Y-%m-%d %H:%M')}")
|
self.update_status(f"Querying: {start_date.strftime('%Y-%m-%d %H:%M')}")
|
||||||
@@ -187,6 +216,9 @@ class ConfigDialog:
|
|||||||
try:
|
try:
|
||||||
responses = assoc.send_c_find(ds, StudyRootQueryRetrieveInformationModelFind)
|
responses = assoc.send_c_find(ds, StudyRootQueryRetrieveInformationModelFind)
|
||||||
for status, identifier in responses:
|
for status, identifier in responses:
|
||||||
|
if self.stop_event.is_set():
|
||||||
|
break
|
||||||
|
self.pause_event.wait()
|
||||||
if status and status.Status in (0xFF00, 0xFF01) and identifier:
|
if status and status.Status in (0xFF00, 0xFF01) and identifier:
|
||||||
mod_raw = getattr(identifier, 'ModalitiesInStudy', '')
|
mod_raw = getattr(identifier, 'ModalitiesInStudy', '')
|
||||||
try:
|
try:
|
||||||
@@ -211,11 +243,10 @@ class ConfigDialog:
|
|||||||
mod_string,
|
mod_string,
|
||||||
str(getattr(identifier, 'NumberOfStudyRelatedInstances', '')).strip()
|
str(getattr(identifier, 'NumberOfStudyRelatedInstances', '')).strip()
|
||||||
])
|
])
|
||||||
f.flush()
|
self.record_count.set(self.record_count.get() + 1)
|
||||||
|
self.update_record_count()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("Error writing to output file.")
|
logging.exception("Error writing to output file.")
|
||||||
elif status:
|
|
||||||
logging.debug("C-FIND response: 0x%04X", status.Status)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("Exception during C-FIND from %s to %s", start_date, end_date)
|
logging.exception("Exception during C-FIND from %s to %s", start_date, end_date)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user