add process per study_iuid
This commit is contained in:
111
main.py
111
main.py
@@ -63,6 +63,11 @@ def parse_args():
|
||||
send_file_parser = subparsers.add_parser('send-file', help='Send a single DICOM file to destination PACS')
|
||||
send_file_parser.add_argument('--file-path', required=True, help='Path to DICOM file to send')
|
||||
|
||||
# Add to the parse_args() function after the 'process' command
|
||||
process_study_parser = subparsers.add_parser('process-study', help='Process full workflow for a specific study by StudyInstanceUID')
|
||||
process_study_parser.add_argument('--study-uid', required=True, help='StudyInstanceUID to process')
|
||||
process_study_parser.add_argument('--log-dir', help='Directory to save JSON logs (default: JSON_OUTPUT_DIR)')
|
||||
|
||||
# Process command - full workflow: find, get, and send DICOM data for a date range
|
||||
process_parser = subparsers.add_parser('process', help='Process full workflow (find, get, send) for a date range')
|
||||
process_parser.add_argument('--start-date', required=True, help='Start date in YYYYMMDD format')
|
||||
@@ -481,7 +486,7 @@ def process_workflow(args):
|
||||
his_url = f"http://{settings.HIS_HOST}{settings.HIS_URL}"
|
||||
try:
|
||||
response = requests.post(his_url, json=study_log)
|
||||
if response.status_code == 200 and response.json().get('status') == "OK":
|
||||
if response.status_code == 200 and response.json().get('OK') == "1":
|
||||
his_log.append(study_log)
|
||||
logger.info(f"Successfully sent JSON {accession_number} to HIS API")
|
||||
else:
|
||||
@@ -506,6 +511,108 @@ def process_workflow(args):
|
||||
logger.error(f"Error during workflow processing: {str(e)}")
|
||||
sys.exit(1)
|
||||
|
||||
def process_workflow_by_study(args):
|
||||
"""
|
||||
Process the full workflow for a specific study: retrieve it, send to destination PACS,
|
||||
and create detailed JSON logs.
|
||||
|
||||
Args:
|
||||
args: Command line arguments
|
||||
"""
|
||||
study_uid = args.study_uid
|
||||
log_dir = args.log_dir or settings.JSON_OUTPUT_DIR
|
||||
|
||||
logger.info(f"Starting full workflow process for study: {study_uid}")
|
||||
|
||||
# Create services
|
||||
finder = DicomFinder()
|
||||
retriever = DicomRetriever()
|
||||
sender = DicomSender()
|
||||
|
||||
# Create directory for logs
|
||||
create_directory_if_not_exists(log_dir)
|
||||
|
||||
# Process the study
|
||||
try:
|
||||
# First, get the study metadata with a query
|
||||
study_metadata = finder.find_study_by_uid(study_uid)
|
||||
|
||||
if not study_metadata:
|
||||
logger.error(f"Study not found: {study_uid}")
|
||||
sys.exit(1)
|
||||
|
||||
accession_number = getattr(study_metadata, 'AccessionNumber', '')
|
||||
logger.info(f"Processing study: {accession_number} with Study_IUID ({study_uid})")
|
||||
|
||||
# STEP 1: Retrieve the study
|
||||
retrieve_result = retriever.retrieve_study(study_uid, accession_number=accession_number)
|
||||
if not retrieve_result['success']:
|
||||
logger.error(f"Failed to retrieve study {study_uid}: {retrieve_result['status']}")
|
||||
sys.exit(1)
|
||||
|
||||
logger.info(f"Retrieved {retrieve_result['successful_instances']} instances for study {study_uid}")
|
||||
|
||||
# STEP 2: Send the study to destination PACS
|
||||
send_result = sender.send_study(os.path.join(settings.DICOM_STORE_DIR, study_uid))
|
||||
if not send_result['success']:
|
||||
logger.error(f"Failed to send study {study_uid}: {send_result.get('error', 'Unknown error')}")
|
||||
|
||||
logger.info(f"Sent {send_result['successful_sends']} of {send_result['total_files']} files to destination PACS")
|
||||
|
||||
# STEP 3: Find all series and create detailed logs
|
||||
series_list = finder.find_series_for_study(study_uid, accession_number=accession_number)
|
||||
|
||||
study_date = getattr(study_metadata, 'StudyDate', '')
|
||||
study_time = getattr(study_metadata, 'StudyTime', '000000') # Default to midnight if StudyTime is not available
|
||||
study_datetime = f"{study_date}{study_time}"
|
||||
|
||||
study_log = {
|
||||
'Study_IUID': study_uid,
|
||||
'AccessionNumber': getattr(study_metadata, 'AccessionNumber', ''),
|
||||
'PatientID': getattr(study_metadata, 'PatientID', ''), # MedrecID
|
||||
'StudyDescription': getattr(study_metadata, 'StudyDescription', ''),
|
||||
'StudyDateTime': study_datetime,
|
||||
'CstoreSuccess': send_result['success'],
|
||||
'Series': []
|
||||
}
|
||||
|
||||
for series in series_list:
|
||||
series_uid = series.SeriesInstanceUID
|
||||
|
||||
# Get first instance for the series
|
||||
instance = finder.find_first_instance_for_series(study_uid, series_uid)
|
||||
|
||||
series_info = {
|
||||
'Series_IUID': getattr(series, 'SeriesInstanceUID', ''),
|
||||
'SeriesNumber': getattr(series, 'SeriesNumber', ''),
|
||||
'SeriesDescription': getattr(series, 'SeriesDescription', ''),
|
||||
'NumberOfInstances': getattr(series, 'NumberOfSeriesRelatedInstances', ''),
|
||||
'SOP_IUID': getattr(instance, 'SOPInstanceUID', '') if instance else ''
|
||||
}
|
||||
|
||||
study_log['Series'].append(series_info)
|
||||
|
||||
# STEP 4: Send study_log to HIS API
|
||||
his_url = f"http://{settings.HIS_HOST}{settings.HIS_URL}"
|
||||
try:
|
||||
response = requests.post(his_url, json=study_log)
|
||||
if response.status_code == 200 and response.json().get('OK') == "1":
|
||||
logger.info(f"Successfully sent JSON {accession_number} to HIS API")
|
||||
# Save successful log
|
||||
save_json_data([study_log], f"sendtohis_study_{study_uid}.json", "logs")
|
||||
else:
|
||||
logger.error(f"Failed to send JSON for {accession_number} to HIS API. Study_IUID: {study_uid}")
|
||||
# Save failed log
|
||||
save_json_data([study_log], f"fail_sendtohis_study_{study_uid}.json", "logs")
|
||||
except Exception as e:
|
||||
logger.error(f"Error sending study for {accession_number} to HIS API: {str(e)}")
|
||||
|
||||
logger.info(f"Completed processing study: {study_uid}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error during study processing: {str(e)}")
|
||||
sys.exit(1)
|
||||
|
||||
def main():
|
||||
"""Main function."""
|
||||
# Register cleanup handlers for graceful exit
|
||||
@@ -544,6 +651,8 @@ def main():
|
||||
send_file(args)
|
||||
elif args.command == 'process':
|
||||
process_workflow(args)
|
||||
elif args.command == 'process-study':
|
||||
process_workflow_by_study(args)
|
||||
else:
|
||||
logger.error("No command specified. Use --help for options.")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -269,4 +269,82 @@ class DicomFinder:
|
||||
logger.error(error_msg)
|
||||
raise DicomQueryError(error_msg)
|
||||
|
||||
return first_instance
|
||||
return first_instance
|
||||
|
||||
@dicom_retry(exception_types=(DicomQueryError, ConnectionError))
|
||||
def find_study_by_uid(self, study_instance_uid):
|
||||
"""
|
||||
Find a specific study by its StudyInstanceUID.
|
||||
|
||||
Args:
|
||||
study_instance_uid (str): StudyInstanceUID to find
|
||||
|
||||
Returns:
|
||||
Dataset: Study dataset or None if not found
|
||||
"""
|
||||
logger.info(f"Finding study with UID: {study_instance_uid}")
|
||||
|
||||
# Create query dataset
|
||||
ds = Dataset()
|
||||
ds.QueryRetrieveLevel = 'STUDY'
|
||||
|
||||
# Set the StudyInstanceUID filter
|
||||
ds.StudyInstanceUID = study_instance_uid
|
||||
|
||||
# Required fields (minimal set)
|
||||
ds.StudyDate = ''
|
||||
|
||||
# Additional fields we want to retrieve
|
||||
ds.AccessionNumber = ''
|
||||
ds.PatientID = '' # MedrecID
|
||||
ds.StudyDescription = ''
|
||||
ds.StudyTime = ''
|
||||
ds.NumberOfStudyRelatedSeries = ''
|
||||
|
||||
# Create association
|
||||
assoc = self.ae.associate(
|
||||
self.pacs_config['host'],
|
||||
self.pacs_config['port'],
|
||||
ae_title=self.pacs_config['aet']
|
||||
)
|
||||
|
||||
study = None
|
||||
|
||||
if assoc.is_established:
|
||||
try:
|
||||
logger.debug("Association established, sending C-FIND request")
|
||||
|
||||
# Send C-FIND request
|
||||
responses = assoc.send_c_find(
|
||||
ds,
|
||||
StudyRootQueryRetrieveInformationModelFind
|
||||
)
|
||||
|
||||
# Process responses - just get the first one
|
||||
for (status, dataset) in responses:
|
||||
if status and status.Status == 0xFF00: # Pending
|
||||
if dataset:
|
||||
study = dataset
|
||||
logger.debug(f"Found study: {dataset.StudyInstanceUID}")
|
||||
break
|
||||
elif status and status.Status != 0x0000: # Not success
|
||||
logger.error(f"C-FIND error: {status}")
|
||||
|
||||
if study:
|
||||
logger.info(f"Found study {study_instance_uid}")
|
||||
else:
|
||||
logger.warning(f"No study found with UID {study_instance_uid}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error during C-FIND: {str(e)}")
|
||||
raise DicomQueryError(f"C-FIND operation failed: {str(e)}")
|
||||
finally:
|
||||
# Release the association
|
||||
assoc.release()
|
||||
logger.debug("Association released")
|
||||
else:
|
||||
error_msg = f"Association rejected, aborted or never connected to {self.pacs_config['aet']}"
|
||||
logger.error(error_msg)
|
||||
raise DicomQueryError(error_msg)
|
||||
|
||||
return study
|
||||
Reference in New Issue
Block a user