1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
|
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
"""
FILE: sample_finding_inference.py
DESCRIPTION:
The sample_finding_inference.py module processes a sample radiology document with the Radiology Insights service.
It will initialize a RadiologyInsightsClient, build a Radiology Insights request with the sample document,
submit it to the client, RadiologyInsightsClient, and display
-the Finding resource type,
-the Finding ID,
-the Finding status,
-the Finding category code,
-the Finding code,
-the Finding interpretation,
-the Finding components,
-the Finding component code,
-the Finding component value codeable concept,
-the Finding component value coding,
-the Finding component value boolean,
-the Finding component value quantity,
-the Inference extensions,
-the Inference extension URL,
-the Inference extension value string,
-the Inference extension section
USAGE:
1. Set the environment variables with your own values before running the sample:
- AZURE_HEALTH_INSIGHTS_ENDPOINT - the endpoint to your source Health Insights resource.
- For more details how to use DefaultAzureCredential, please take a look at https://learn.microsoft.com/python/api/azure-identity/azure.identity.defaultazurecredential
2. python sample_finding_inference.py
"""
import datetime
import os
import uuid
from azure.identity import DefaultAzureCredential
from azure.healthinsights.radiologyinsights import RadiologyInsightsClient
from azure.healthinsights.radiologyinsights import models
def radiology_insights_sync() -> None:
credential = DefaultAzureCredential()
ENDPOINT = os.environ["AZURE_HEALTH_INSIGHTS_ENDPOINT"]
job_id = str(uuid.uuid4())
radiology_insights_client = RadiologyInsightsClient(endpoint=ENDPOINT, credential=credential)
doc_content1 = """FINDINGS:
In the right upper lobe, there is a new mass measuring 5.6 x 4.5 x 3.4 cm.
A lobulated soft tissue mass is identified in the superior right lower lobe abutting the major fissure measuring 5.4 x 4.3 x 3.7 cm (series 3 image 94, coronal image 110).
A 4 mm nodule in the right lower lobe (series 3, image 72) is increased dating back to 6/29/2012. This may represent lower lobe metastasis.
IMPRESSION: 4 cm pulmonary nodule posterior aspect of the right upper lobe necessitating additional imaging as described."""
# Create ordered procedure
procedure_coding = models.Coding(
system="Https://loinc.org",
code="24627-2",
display="CT CHEST",
)
procedure_code = models.CodeableConcept(coding=[procedure_coding])
ordered_procedure = models.OrderedProcedure(description="CT CHEST", code=procedure_code)
# Create encounter
start = datetime.datetime(2021, 8, 28, 0, 0, 0, 0)
end = datetime.datetime(2021, 8, 28, 0, 0, 0, 0)
encounter = models.PatientEncounter(
id="encounter2",
class_property=models.EncounterClass.IN_PATIENT,
period=models.TimePeriod(start=start, end=end),
)
# Create patient info
birth_date = datetime.date(1959, 11, 11)
patient_info = models.PatientDetails(sex=models.PatientSex.FEMALE, birth_date=birth_date)
# Create author
author = models.DocumentAuthor(id="author2", full_name="authorName2")
create_date_time = datetime.datetime(2024, 2, 19, 0, 0, 0, 0, tzinfo=datetime.timezone.utc)
patient_document1 = models.PatientDocument(
type=models.DocumentType.NOTE,
clinical_type=models.ClinicalDocumentType.RADIOLOGY_REPORT,
id="doc2",
content=models.DocumentContent(source_type=models.DocumentContentSourceType.INLINE, value=doc_content1),
created_at=create_date_time,
specialty_type=models.SpecialtyType.RADIOLOGY,
administrative_metadata=models.DocumentAdministrativeMetadata(
ordered_procedures=[ordered_procedure], encounter_id="encounter2"
),
authors=[author],
language="en",
)
# Construct patient
patient1 = models.PatientRecord(
id="patient_id2",
details=patient_info,
encounters=[encounter],
patient_documents=[patient_document1],
)
# Create a configuration
configuration = models.RadiologyInsightsModelConfiguration(verbose=False, include_evidence=True, locale="en-US")
# Construct the request with the patient and configuration
patient_data = models.RadiologyInsightsJob(
job_data=models.RadiologyInsightsData(patients=[patient1], configuration=configuration)
)
# Health Insights Radiology Insights
try:
poller = radiology_insights_client.begin_infer_radiology_insights(
id=job_id,
resource=patient_data,
)
radiology_insights_result = poller.result()
display_finding(radiology_insights_result)
except Exception as ex:
raise ex
def display_finding(radiology_insights_result):
for patient_result in radiology_insights_result.patient_results:
counter = 0
for ri_inference in patient_result.inferences:
if ri_inference.kind == models.RadiologyInsightsInferenceType.FINDING:
counter += 1
print(f"Finding {counter} Inference found")
fin = ri_inference.finding
for attribute in dir(fin):
if attribute.startswith("_") or callable(getattr(fin, attribute)):
continue
elif attribute == "resource_type" and fin.resource_type is not None:
print(f"Finding {counter}: Resource Type: {fin.resource_type}")
elif attribute == "id" and fin.id is not None:
print(f"Finding {counter}: ID: {fin.id}")
elif attribute == "status" and fin.status is not None:
print(f"Finding {counter}: Status: {fin.status}")
elif attribute == "category" and fin.category is not None:
for cat in fin.category:
if cat.coding is not None:
for code in cat.coding:
if code.code is not None and code.display is not None:
print(
f"Finding {counter}: Category Code: {code.system} {code.code} {code.display}"
)
elif attribute == "code" and fin.code is not None:
for code in fin.code.coding:
if code.code is not None and code.display is not None:
print(f"Finding {counter}: Code: {code.system} {code.code} {code.display}")
elif attribute == "interpretation" and fin.interpretation is not None:
for interpretation in fin.interpretation:
for code in interpretation.coding:
if code.code is not None and code.display is not None:
print(
f"Finding {counter}: Interpretation: {code.system} {code.code} {code.display}"
)
elif attribute == "component" and fin.component is not None:
print(f"Finding {counter}: COMPONENTS:")
for component in fin.component:
for attribute in dir(component):
if attribute.startswith("_") or callable(getattr(component, attribute)):
continue
if attribute == "code" and component.code is not None:
for code in component.code.coding:
if code.code is not None and code.display is not None:
print(
f"Finding {counter}: COMPONENTS: Code: {code.system} {code.code} {code.display}"
)
elif (
attribute == "value_codeable_concept"
and component.value_codeable_concept is not None
):
for code in component.value_codeable_concept.coding:
if code.code is not None and code.display is not None:
print(
f"Finding {counter}: COMPONENTS: Value Codeable Concept: {code.system} {code.code} {code.display}"
)
elif attribute == "value_coding" and component.value_coding is not None:
for code in component.value_coding.coding:
if code.code is not None and code.display is not None:
print(
f"Finding {counter}: COMPONENTS: Value Coding: {code.system} {code.code} {code.display}"
)
elif attribute == "value_boolean" and component.value_boolean is not None:
print(f"Finding {counter}: COMPONENTS: Value Boolean: {component.value_boolean}")
elif attribute == "value_quantity" and component.value_quantity is not None:
for attribute in dir(component.value_quantity):
if (
not attribute.startswith("_")
and not callable(getattr(component.value_quantity, attribute))
and getattr(component.value_quantity, attribute) is not None
):
print(
f"Finding {counter}: COMPONENTS: Value Quantity: {attribute.capitalize()}: {getattr(component.value_quantity, attribute)}"
)
inference_extension = ri_inference.extension
if inference_extension is not None:
print(f"Finding {counter}: INFERENCE EXTENSIONS:")
for extension in inference_extension:
for attribute in dir(extension):
if attribute.startswith("_") or callable(getattr(extension, attribute)):
continue
elif attribute == "extension" and extension.extension is not None:
for sub_extension in extension.extension:
for sub_attribute in dir(sub_extension):
if sub_attribute.startswith("_") or callable(
getattr(sub_extension, sub_attribute)
):
continue
elif sub_attribute == "url":
if (
sub_extension.url == "code"
or sub_extension.url == "codingSystem"
or sub_extension.url == "codeSystemName"
or sub_extension.url == "displayName"
):
print(
f"Finding {counter}: INFERENCE EXTENSIONS: EXTENSION: {sub_attribute.capitalize()}: {sub_extension.url}"
)
elif sub_attribute == "value_string" and sub_extension.value_string is not None:
print(
f"Finding {counter}: INFERENCE EXTENSIONS: EXTENSION: {sub_attribute.capitalize()}: {sub_extension.value_string}"
)
elif attribute == "url" and extension.url is not None:
if extension.url == "section":
print(
f"Finding {counter}: INFERENCE EXTENSIONS: {attribute.capitalize()}: {extension.url}"
)
if __name__ == "__main__":
radiology_insights_sync()
|