From dcf2f54f82f93134bc6afaadf7e4e51d4b17422c Mon Sep 17 00:00:00 2001 From: john Date: Wed, 14 May 2025 17:18:51 +0000 Subject: [PATCH] Update archive_discovery.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✅ 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. --- archive_discovery.py | 45 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/archive_discovery.py b/archive_discovery.py index 60140ca..d37fb79 100644 --- a/archive_discovery.py +++ b/archive_discovery.py @@ -5,7 +5,7 @@ import os import csv import logging import configparser -from threading import Thread +from threading import Thread, Event from pydicom.dataset import Dataset from pynetdicom import AE, evt, debug_logger from pynetdicom.sop_class import StudyRootQueryRetrieveInformationModelFind @@ -62,6 +62,10 @@ class ConfigDialog: self.current_profile = tk.StringVar() self.log_level = tk.StringVar(value="INFO") 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.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.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.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="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) if self.profile_names: @@ -124,9 +133,23 @@ class ConfigDialog: logging.getLogger().setLevel(LOG_LEVELS[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.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): self.top.destroy() @@ -136,6 +159,10 @@ class ConfigDialog: def _set_status(self, 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): config = self.values self.update_status("Initializing DICOM query...") @@ -172,14 +199,16 @@ class ConfigDialog: output_path = os.path.join(os.getcwd(), generate_output_filename()) 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.writerow([ 'PatientID', 'PatientName', 'AccessionNumber', 'StudyDate', '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) ds = create_cfind_dataset(start_date, end_date) self.update_status(f"Querying: {start_date.strftime('%Y-%m-%d %H:%M')}") @@ -187,6 +216,9 @@ class ConfigDialog: try: responses = assoc.send_c_find(ds, StudyRootQueryRetrieveInformationModelFind) 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: mod_raw = getattr(identifier, 'ModalitiesInStudy', '') try: @@ -211,11 +243,10 @@ class ConfigDialog: mod_string, str(getattr(identifier, 'NumberOfStudyRelatedInstances', '')).strip() ]) - f.flush() + self.record_count.set(self.record_count.get() + 1) + self.update_record_count() except Exception as e: logging.exception("Error writing to output file.") - elif status: - logging.debug("C-FIND response: 0x%04X", status.Status) except Exception as e: logging.exception("Exception during C-FIND from %s to %s", start_date, end_date)