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:
2025-05-14 17:18:51 +00:00
parent 59823679b7
commit dcf2f54f82

View File

@@ -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)