diff --git a/.doc_gen/metadata/medical-imaging_metadata.yaml b/.doc_gen/metadata/medical-imaging_metadata.yaml index 26300dacfd8..1d5fb472950 100644 --- a/.doc_gen/metadata/medical-imaging_metadata.yaml +++ b/.doc_gen/metadata/medical-imaging_metadata.yaml @@ -285,9 +285,18 @@ medical-imaging_SearchImageSets: - sdk_version: 3 github: python/example_code/medical-imaging excerpts: - - description: + - description: The utility function for searching image sets. snippet_tags: - python.example_code.medical-imaging.SearchImageSets + - description: "Use case #1: EQUAL operator." + snippet_tags: + - python.example_code.medical-imaging.SearchImageSets.use_case1 + - description: "Use case #2: BETWEEN operator using DICOMStudyDate and DICOMStudyTime." + snippet_tags: + - python.example_code.medical-imaging.SearchImageSets.use_case2 + - description: "Use case #3: BETWEEN operator using createdAt. Time studies were previously persisted." + snippet_tags: + - python.example_code.medical-imaging.SearchImageSets.use_case3 JavaScript: versions: - sdk_version: 3 @@ -370,9 +379,15 @@ medical-imaging_GetImageSetMetadata: - sdk_version: 3 github: python/example_code/medical-imaging excerpts: - - description: + - description: Utility function to get image set metadata. snippet_tags: - python.example_code.medical-imaging.GetImageSetMetadata + - description: Get image set metadata without version. + snippet_tags: + - python.example_code.medical-imaging.GetImageSetMetadata.withoutVersionID + - description: Get image set metadata with version. + snippet_tags: + - python.example_code.medical-imaging.GetImageSetMetadata.withVersionID JavaScript: versions: - sdk_version: 3 @@ -507,9 +522,18 @@ medical-imaging_CopyImageSet: - sdk_version: 3 github: python/example_code/medical-imaging excerpts: - - description: + - description: Utility function to copy an image set. snippet_tags: - python.example_code.medical-imaging.CopyImageSet + - description: Copy an image set without a destination. + snippet_tags: + - python.example_code.medical-imaging.CopyImageSet1 + - python.example_code.medical-imaging.CopyImageSet3 + - description: Copy an image set with a destination. + snippet_tags: + - python.example_code.medical-imaging.CopyImageSet1 + - python.example_code.medical-imaging.CopyImageSet2 + - python.example_code.medical-imaging.CopyImageSet3 JavaScript: versions: - sdk_version: 3 @@ -668,6 +692,32 @@ medical-imaging_tagging_datastores: synopsis: tag a &AHI; data store. category: Scenarios languages: + Python: + versions: + - sdk_version: 3 + github: javascriptv3/example_code/medical-imaging + excerpts: + - description: To tag a data store. + snippet_tags: + - python.example_code.medical-imaging.tagging_datastore.arn + - python.example_code.medical-imaging.tagging_datastore.tag + - description: The utility function for tagging a resource. + snippet_tags: + - python.example_code.medical-imaging.TagResource + - description: To list tags for a data store. + snippet_tags: + - python.example_code.medical-imaging.tagging_datastore.arn + - python.example_code.medical-imaging.tagging_datastore.list + - description: The utility function for listing a resource's tags. + snippet_tags: + - python.example_code.medical-imaging.ListTagsForResource + - description: To untag a data store. + snippet_tags: + - python.example_code.medical-imaging.tagging_datastore.arn + - python.example_code.medical-imaging.tagging_datastore.untag + - description: The utility function for untagging a resource. + snippet_tags: + - python.example_code.medical-imaging.UntagResource JavaScript: versions: - sdk_version: 3 @@ -725,6 +775,32 @@ medical-imaging_tagging_imagesets: synopsis: tag a &AHI; image set. category: Scenarios languages: + Python: + versions: + - sdk_version: 3 + github: javascriptv3/example_code/medical-imaging + excerpts: + - description: To tag an image set. + snippet_tags: + - python.example_code.medical-imaging.tagging_image_set.arn + - python.example_code.medical-imaging.tagging_image_set.tag + - description: The utility function for tagging a resource. + snippet_tags: + - python.example_code.medical-imaging.TagResource + - description: To list tags for an image set. + snippet_tags: + - python.example_code.medical-imaging.tagging_image_set.arn + - python.example_code.medical-imaging.tagging_image_set.list + - description: The utility function for listing a resource's tags. + snippet_tags: + - python.example_code.medical-imaging.ListTagsForResource + - description: To untag an image set. + snippet_tags: + - python.example_code.medical-imaging.tagging_image_set.arn + - python.example_code.medical-imaging.tagging_image_set.untag + - description: The utility function for untagging a resource. + snippet_tags: + - python.example_code.medical-imaging.UntagResource JavaScript: versions: - sdk_version: 3 diff --git a/python/example_code/medical-imaging/README.md b/python/example_code/medical-imaging/README.md index f1210d18807..eb28349e455 100644 --- a/python/example_code/medical-imaging/README.md +++ b/python/example_code/medical-imaging/README.md @@ -1,4 +1,4 @@ - + # HealthImaging code examples for the SDK for Python ## Overview @@ -39,24 +39,32 @@ python -m pip install -r requirements.txt Code excerpts that show you how to call individual service functions. -* [Add a tag to a resource](medical_imaging_basics.py#L391) (`TagResource`) -* [Copy an image set](medical_imaging_basics.py#L337) (`CopyImageSet`) -* [Create a data store](medical_imaging_basics.py#L21) (`CreateDatastore`) -* [Delete a data store](medical_imaging_basics.py#L84) (`DeleteDatastore`) -* [Delete an image set](medical_imaging_basics.py#L369) (`DeleteImageSet`) -* [Get an image frame](medical_imaging_basics.py#L257) (`GetImageFrame`) -* [Get data store properties](medical_imaging_basics.py#L41) (`GetDatastore`) -* [Get image set properties](medical_imaging_basics.py#L202) (`GetImageSet`) -* [Get import job properties](medical_imaging_basics.py#L131) (`GetDICOMImportJob`) -* [Get metadata for an image set](medical_imaging_basics.py#L225) (`GetImageSetMetadata`) -* [Import bulk data into a data store](medical_imaging_basics.py#L101) (`StartDICOMImportJob`) -* [List data stores](medical_imaging_basics.py#L61) (`ListDatastores`) -* [List image set versions](medical_imaging_basics.py#L284) (`ListImageSetVersions`) -* [List import jobs for a data store](medical_imaging_basics.py#L152) (`ListDICOMImportJobs`) -* [List tags for a resource](medical_imaging_basics.py#L427) (`ListTagsForResource`) -* [Remove a tag from a resource](medical_imaging_basics.py#L409) (`UntagResource`) -* [Search image sets](medical_imaging_basics.py#L176) (`SearchImageSets`) -* [Update image set metadata](medical_imaging_basics.py#L310) (`UpdateImageSetMetadata`) +* [Add a tag to a resource](medical_imaging_basics.py#L411) (`TagResource`) +* [Copy an image set](medical_imaging_basics.py#L351) (`CopyImageSet`) +* [Create a data store](medical_imaging_basics.py#L27) (`CreateDatastore`) +* [Delete a data store](medical_imaging_basics.py#L90) (`DeleteDatastore`) +* [Delete an image set](medical_imaging_basics.py#L389) (`DeleteImageSet`) +* [Get an image frame](medical_imaging_basics.py#L271) (`GetImageFrame`) +* [Get data store properties](medical_imaging_basics.py#L47) (`GetDatastore`) +* [Get image set properties](medical_imaging_basics.py#L208) (`GetImageSet`) +* [Get import job properties](medical_imaging_basics.py#L137) (`GetDICOMImportJob`) +* [Get metadata for an image set](medical_imaging_basics.py#L234) (`GetImageSetMetadata`) +* [Import bulk data into a data store](medical_imaging_basics.py#L107) (`StartDICOMImportJob`) +* [List data stores](medical_imaging_basics.py#L67) (`ListDatastores`) +* [List image set versions](medical_imaging_basics.py#L298) (`ListImageSetVersions`) +* [List import jobs for a data store](medical_imaging_basics.py#L158) (`ListDICOMImportJobs`) +* [List tags for a resource](medical_imaging_basics.py#L447) (`ListTagsForResource`) +* [Remove a tag from a resource](medical_imaging_basics.py#L429) (`UntagResource`) +* [Search image sets](medical_imaging_basics.py#L182) (`SearchImageSets`) +* [Update image set metadata](medical_imaging_basics.py#L324) (`UpdateImageSetMetadata`) + +### Scenarios + +Code examples that show you how to accomplish a specific task by calling multiple +functions within the same service. + +* [Tagging a data store](tagging_data_stores.py) +* [Tagging an image set](tagging_image_sets.py) ## Run the examples @@ -68,6 +76,42 @@ Code excerpts that show you how to call individual service functions. +#### Tagging a data store + +This example shows you how to tag a HealthImaging data store. + + + + + +Start the example by running the following at a command prompt: + +``` +python tagging_data_stores.py +``` + + + + + +#### Tagging an image set + +This example shows you how to tag a HealthImaging image set. + + + + + +Start the example by running the following at a command prompt: + +``` +python tagging_image_sets.py +``` + + + + + ### Tests ⚠ Running tests might result in charges to your AWS account. diff --git a/python/example_code/medical-imaging/medical_imaging_basics.py b/python/example_code/medical-imaging/medical_imaging_basics.py index adc8318d251..1c9b15a98d2 100644 --- a/python/example_code/medical-imaging/medical_imaging_basics.py +++ b/python/example_code/medical-imaging/medical_imaging_basics.py @@ -8,7 +8,14 @@ functions. """ +import datetime +import gzip +import json import logging +import random +import time + +import boto3 from botocore.exceptions import ClientError logger = logging.getLogger(__name__) @@ -229,19 +236,26 @@ def search_image_sets(self, datastore_id, search_filter): # snippet-end:[python.example_code.medical-imaging.SearchImageSets] # snippet-start:[python.example_code.medical-imaging.GetImageSet] - def get_image_set(self, datastore_id, image_set_id, version_id): + def get_image_set(self, datastore_id, image_set_id, version_id=None): """ Get the properties of an image set. :param datastore_id: The ID of the data store. :param image_set_id: The ID of the image set. - :param version_id: The version of the image set. + :param version_id: The optional version of the image set. :return: The image set properties. """ try: - image_set = self.health_imaging_client.get_image_set( - imageSetId=image_set_id, datastoreId=datastore_id, versionId=version_id - ) + if version_id: + image_set = self.health_imaging_client.get_image_set( + imageSetId=image_set_id, + datastoreId=datastore_id, + versionId=version_id, + ) + else: + image_set = self.health_imaging_client.get_image_set( + imageSetId=image_set_id, datastoreId=datastore_id + ) except ClientError as err: logger.error( "Couldn't get image set. Here's why: %s: %s", @@ -268,15 +282,20 @@ def get_image_set_metadata( """ try: if version_id: + # snippet-start:[python.example_code.medical-imaging.GetImageSetMetadata.withVersionID] image_set_metadata = self.health_imaging_client.get_image_set_metadata( imageSetId=image_set_id, datastoreId=datastore_id, versionId=version_id, ) + # snippet-end:[python.example_code.medical-imaging.GetImageSetMetadata.withVersionID] else: + # snippet-start:[python.example_code.medical-imaging.GetImageSetMetadata.withoutVersionID] + image_set_metadata = self.health_imaging_client.get_image_set_metadata( imageSetId=image_set_id, datastoreId=datastore_id ) + # snippet-end:[python.example_code.medical-imaging.GetImageSetMetadata.withoutVersionID] print(image_set_metadata) with open(metadata_file, "wb") as f: for chunk in image_set_metadata["imageSetMetadataBlob"].iter_chunks(): @@ -410,19 +429,25 @@ def copy_image_set( :return: The copied image set ID. """ try: + # snippet-start:[python.example_code.medical-imaging.CopyImageSet1] copy_image_set_information = { "sourceImageSet": {"latestVersionId": version_id} } + # snippet-end:[python.example_code.medical-imaging.CopyImageSet1] + # snippet-start:[python.example_code.medical-imaging.CopyImageSet2] if destination_image_set_id and destination_version_id: copy_image_set_information["destinationImageSet"] = { "imageSetId": destination_image_set_id, "latestVersionId": destination_version_id, } + # snippet-end:[python.example_code.medical-imaging.CopyImageSet2] + # snippet-start:[python.example_code.medical-imaging.CopyImageSet3] copy_results = self.health_imaging_client.copy_image_set( datastoreId=datastore_id, sourceImageSetId=image_set_id, copyImageSetInformation=copy_image_set_information, ) + # snippet-end:[python.example_code.medical-imaging.CopyImageSet3] except ClientError as err: logger.error( "Couldn't copy image set. Here's why: %s: %s", @@ -525,3 +550,238 @@ def list_tags_for_resource(self, resource_arn): return tags["tags"] # snippet-end:[python.example_code.medical-imaging.ListTagsForResource] + + def usage_demo(self, source_s3_uri, dest_s3_uri, data_access_role_arn): + data_store_name = f"python_usage_demo_data_store_{random.randint(0, 200000)}" + + data_store_id = self.create_datastore(data_store_name) + print(f"Data store created with id : {data_store_id}") + + while True: + time.sleep(1) + datastore_properties = self.get_datastore_properties(data_store_id) + datastore_status = datastore_properties["datastoreStatus"] + print(f'data store status: "{datastore_status}"') + if datastore_status == "ACTIVE": + break + elif datastore_status == "CREATE_FAILED": + raise Exception("Create datastore job failed") + + datastores = self.list_datastores() + print(f"datastores : {datastores}") + + job_name = "python_usage_demo_job" + job_id = self.start_dicom_import_job( + job_name, data_store_id, data_access_role_arn, source_s3_uri, dest_s3_uri + ) + print(f"Started import job with id: {job_id}") + + while True: + time.sleep(1) + job = self.get_dicom_import_job(data_store_id, job_id) + job_status = job["jobStatus"] + print(f'Status of import job : "{job_status}"') + if job_status == "COMPLETED": + break + elif job_status == "FAILED": + raise Exception("DICOM import job failed") + + import_jobs = self.list_dicom_import_jobs(data_store_id) + print(import_jobs) + for job in import_jobs: + print(job) + + # Search with EQUAL operator.. + # snippet-start:[python.example_code.medical-imaging.SearchImageSets.use_case1] + filter = { + "filters": [ + {"operator": "EQUAL", "values": [{"DICOMPatientId": "3524578"}]} + ] + } + + image_sets = self.search_image_sets(data_store_id, filter) + # snippet-end:[python.example_code.medical-imaging.SearchImageSets.use_case1] + + # Search with BETWEEN operator using DICOMStudyDate and DICOMStudyTime. + # snippet-start:[python.example_code.medical-imaging.SearchImageSets.use_case2] + filter = { + "filters": [ + { + "operator": "BETWEEN", + "values": [ + { + "DICOMStudyDateAndTime": { + "DICOMStudyDate": "19900101", + "DICOMStudyTime": "000000", + } + }, + { + "DICOMStudyDateAndTime": { + "DICOMStudyDate": "20230101", + "DICOMStudyTime": "000000", + } + }, + ], + } + ] + } + + image_sets = self.search_image_sets(data_store_id, filter) + # snippet-end:[python.example_code.medical-imaging.SearchImageSets.use_case2] + + # Search with BETWEEN operator using createdAt. Time studies were previously persisted. + # snippet-start:[python.example_code.medical-imaging.SearchImageSets.use_case3] + filter = { + "filters": [ + { + "values": [ + { + "createdAt": datetime.datetime( + 2021, 8, 4, 14, 49, 54, 429000 + ) + }, + { + "createdAt": datetime.datetime.now() + + datetime.timedelta(days=1) + }, + ], + "operator": "BETWEEN", + } + ] + } + + image_sets = self.search_image_sets(data_store_id, filter) + # snippet-end:[python.example_code.medical-imaging.SearchImageSets.use_case3] + + image_set_ids = [image_set["imageSetId"] for image_set in image_sets] + for image_set in image_sets: + print(image_set) + + image_set_id = image_sets[0]["imageSetId"] + version_id = image_sets[0]["version"] + returned_image_set = self.get_image_set( + data_store_id, image_set_id, str(version_id) + ) + print(returned_image_set) + + image_metadata_file_name = "metadata.json.gzip" + self.get_image_set_metadata( + image_metadata_file_name, data_store_id, image_set_id + ) + image_frame_id = "" + with gzip.open(image_metadata_file_name, "rb") as f_in: + data = json.load(f_in) + series = data["Study"]["Series"] + for value in series.values(): + for instance in value["Instances"].values(): + image_frame_id = instance["ImageFrames"][0]["ID"] + + if image_frame_id == "": + raise Exception("Image frame id is empty") + + image_file_name = "image_frame.jph" + self.get_pixel_data( + image_file_name, data_store_id, image_set_id, image_frame_id + ) + + returned_versions = self.list_image_set_versions(data_store_id, image_set_id) + for version in returned_versions: + print(version) + + copied_image_set_id = self.copy_image_set( + data_store_id, image_set_id, str(version_id) + ) + print(f"Copied image set to new image set with ID : {copied_image_set_id}") + + image_set_ids.append(copied_image_set_id) + + # Wait for copied image set to be ACTIVE before updating the metadata. + while True: + time.sleep(1) + try: + image_set_properties = self.get_image_set( + data_store_id, copied_image_set_id + ) + except ClientError as err: + print( + f"get_image_set raised an error {err.response['Error']['Message']}" + ) + break + + image_set_state = image_set_properties["imageSetState"] + print( + f'Image set with id : "{copied_image_set_id}" has status: "{image_set_state}"' + ) + if image_set_state != "LOCKED": + break + + attributes = ( + '{"SchemaVersion":1.1,"Patient":{"DICOM":{"PatientName":"Garcia^Gloria"}}}' + ) + metadata = {"DICOMUpdates": {"updatableAttributes": attributes}} + + self.update_image_set_metadata( + data_store_id, copied_image_set_id, "1", metadata + ) + print(f"Updated metadata for image set with id : {copied_image_set_id}") + + # Wait for all image sets to change from LOCKED status before deleting. + for image_set_id in image_set_ids: + while True: + time.sleep(1) + try: + image_set_properties = self.get_image_set( + data_store_id, image_set_id + ) + except ClientError as err: + print( + f"get_image_set raised an error {err.response['Error']['Message']}" + ) + break + + image_set_state = image_set_properties["imageSetState"] + print( + f'Image set with id : "{image_set_id}" has status: "{image_set_state}"' + ) + if image_set_state != "LOCKED": + break + + for image_set_id in image_set_ids: + self.delete_image_set(data_store_id, image_set_id) + print(f"Deleted image set with id : {image_set_id}") + + # Wait for image sets to be deleted before deleting the data store. + for image_set_id in image_set_ids: + while True: + time.sleep(1) + try: + image_set_properties = self.get_image_set( + data_store_id, image_set_id + ) + except ClientError as err: + print( + f"get_image_set raised an error {err.response['Error']['Message']}" + ) + break + + image_set_state = image_set_properties["imageSetState"] + print( + f'Image set with id : "{image_set_id}" has status: "{image_set_state}"' + ) + if image_set_state == "DELETED": + break + + self.delete_datastore(data_store_id) + print(f"Data store deleted with id : {data_store_id}") + + +if __name__ == "__main__": + # Replace these values with your own. + source_s3_uri = "s3://medical-imaging-dicom-input/dicom_input/" + dest_s3_uri = "s3://medical-imaging-output/job_output/" + data_access_role_arn = "arn:aws:iam::123456789012:role/ImportJobDataAccessRole" + + client = boto3.client("medical-imaging") + medical_imaging_wrapper = MedicalImagingWrapper(client) + + medical_imaging_wrapper.usage_demo(source_s3_uri, dest_s3_uri, data_access_role_arn) diff --git a/python/example_code/medical-imaging/tagging_data_stores.py b/python/example_code/medical-imaging/tagging_data_stores.py new file mode 100644 index 00000000000..8e43dcb80a4 --- /dev/null +++ b/python/example_code/medical-imaging/tagging_data_stores.py @@ -0,0 +1,47 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Purpose + +Shows how to use the AWS SDK for Python (Boto3) to tag AWS HealthImaging data stores. +""" +import boto3 + +from medical_imaging_basics import MedicalImagingWrapper + + +def tagging_data_stores(medical_imaging_wrapper, data_store_arn): + """ + Taggging a data store. + + :param medical_imaging_wrapper: A MedicalImagingWrapper instance. + :param data_store_arn: The Amazon Resource Name (ARN) of the data store. + For example: arn:aws:medical-imaging:us-east-1:123456789012:datastore/12345678901234567890123456789012 + """ + + # snippet-start:[python.example_code.medical-imaging.tagging_datastore.tag] + medical_imaging_wrapper.tag_resource(data_store_arn, {"Deployment": "Development"}) + # snippet-end:[python.example_code.medical-imaging.tagging_datastore.tag] + + # snippet-start:[python.example_code.medical-imaging.tagging_datastore.list] + medical_imaging_wrapper.list_tags_for_resource(data_store_arn) + # snippet-end:[python.example_code.medical-imaging.tagging_datastore.list] + + # snippet-start:[python.example_code.medical-imaging.tagging_datastore.untag] + medical_imaging_wrapper.untag_resource(data_store_arn, ["Deployment"]) + + # snippet-end:[python.example_code.medical-imaging.tagging_datastore.untag] + + +if __name__ == "__main__": + # snippet-start:[python.example_code.medical-imaging.tagging_datastore.arn] + a_data_store_arn = "arn:aws:medical-imaging:us-east-1:123456789012:datastore/12345678901234567890123456789012" + # snippet-end:[python.example_code.medical-imaging.tagging_datastore.arn] + + a_data_store_arn = input(f"Enter the ARN of the data store to tag: ") + + client = boto3.client("medical-imaging") + a_medical_imaging_wrapper = MedicalImagingWrapper(client) + + tagging_data_stores(a_medical_imaging_wrapper, a_data_store_arn) diff --git a/python/example_code/medical-imaging/tagging_image_sets.py b/python/example_code/medical-imaging/tagging_image_sets.py new file mode 100644 index 00000000000..c7ca1d0e325 --- /dev/null +++ b/python/example_code/medical-imaging/tagging_image_sets.py @@ -0,0 +1,50 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Purpose + +Shows how to use the AWS SDK for Python (Boto3) to tag AWS HealthImaging image sets. +""" +import boto3 + +from medical_imaging_basics import MedicalImagingWrapper + + +def tagging_image_sets(medical_imaging_wrapper, image_set_arn): + """ + Taggging an image set. + + :param medical_imaging_wrapper: A MedicalImagingWrapper instance. + :param image_set_arn: The Amazon Resource Name (ARN) of the image set. + For example: 'arn:aws:medical-imaging:us-east-1:123456789012:datastore/12345678901234567890123456789012/' \ + 'imageset/12345678901234567890123456789012' + """ + + # snippet-start:[python.example_code.medical-imaging.tagging_image_set.tag] + medical_imaging_wrapper.tag_resource(image_set_arn, {"Deployment": "Development"}) + # snippet-end:[python.example_code.medical-imaging.tagging_image_set.tag] + + # snippet-start:[python.example_code.medical-imaging.tagging_image_set.list] + medical_imaging_wrapper.list_tags_for_resource(image_set_arn) + # snippet-end:[python.example_code.medical-imaging.tagging_image_set.list] + + # snippet-start:[python.example_code.medical-imaging.tagging_image_set.untag] + medical_imaging_wrapper.untag_resource(image_set_arn, ["Deployment"]) + # snippet-end:[python.example_code.medical-imaging.tagging_image_set.untag] + + +if __name__ == "__main__": + # snippet-start:[python.example_code.medical-imaging.tagging_image_set.arn] + an_image_set_arn = ( + "arn:aws:medical-imaging:us-east-1:123456789012:datastore/12345678901234567890123456789012/" + "imageset/12345678901234567890123456789012" + ) + # snippet-end:[python.example_code.medical-imaging.tagging_image_set.arn] + + an_image_set_arn = input(f"Enter the ARN of the image set to tag: ") + + client = boto3.client("medical-imaging") + a_medical_imaging_wrapper = MedicalImagingWrapper(client) + + tagging_image_sets(a_medical_imaging_wrapper, an_image_set_arn)