add process per study_iuid

This commit is contained in:
mario
2025-05-30 12:54:51 +07:00
parent dfe0c18be2
commit c945ead72f
2 changed files with 189 additions and 2 deletions

111
main.py
View File

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

View File

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