-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
152 lines (126 loc) · 5.05 KB
/
main.py
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
import os
import time
from pathlib import Path
from uuid import UUID
import pydicom
from encord.orm.storage import (
CustomerProvidedDicomSeriesDicomFileMetadata,
DataUploadDicomSeries,
DataUploadDicomSeriesDicomFile,
DataUploadItems,
)
from encord.user_client import EncordUserClient
REQUIRED_DICOM_TAGS = [
"00080018", # SOPInstanceUID
"00100020", # PatientID
"00180050", # SliceThickness
"00181114", # EstimatedRadiographicMagnificationFactor
"00181164", # ImagerPixelSpacing
"0020000D", # StudyInstanceUID
"0020000E", # SeriesInstanceUID
"00200013", # InstanceNumber
"00200032", # ImagePositionPatient
"00200037", # ImageOrientationPatient
"00209113", # PlanePositionSequence
"00209116", # PlaneOrientationSequence
"00280004", # PhotometricInterpretation
"00280008", # NumberOfFrames
"00280010", # Rows
"00280011", # Columns
"00280030", # PixelSpacing
"00281050", # WindowCenter
"00281051", # WindowWidth
"00289110", # PixelMeasuresSequence
"52009229", # SharedFunctionalGroupsSequence
"52009230", # PerFrameFunctionalGroupsSequence
]
def get_integration_uuid(user_client: EncordUserClient, title: str) -> UUID:
integrations = [x for x in user_client.get_cloud_integrations() if x.title == title]
if len(integrations) == 0:
raise ValueError(
f"get_integration_uuid error, no integration found with {title=}"
)
if len(integrations) > 1:
raise ValueError(
f"get_integration_uuid error, multiple integrations found with {title=}"
)
return UUID(integrations[0].id)
def pydicom_dataset_to_metadata_tags(pydicom_dataset: pydicom.Dataset) -> dict:
tags = pydicom_dataset.to_json_dict()
return {x: tags.get(x) for x in REQUIRED_DICOM_TAGS}
def main(
ssh_private_key_path: Path,
cloud_integration_title: str,
dicom_dir: Path,
):
if not ssh_private_key_path.is_file():
raise ValueError(f"{ssh_private_key_path=} does not exist")
if not dicom_dir.is_dir():
raise ValueError(f"{dicom_dir=} does not exist")
user_client = EncordUserClient.create_with_ssh_private_key(
ssh_private_key_path=ssh_private_key_path,
)
storage_folder = user_client.create_storage_folder(
name=f"dicom data folder {int(time.time())}"
)
integration_uuid = get_integration_uuid(user_client, cloud_integration_title)
print(f"main-log: found integration {integration_uuid=} ")
dicom_series_map = {
dicom_series_path.name: [
dicom_file_path.name
for dicom_file_path in sorted(dicom_series_path.iterdir())
]
for dicom_series_path in sorted(dicom_dir.iterdir())
}
print(f"main-log: found dicom series {len(dicom_series_map)=} ")
upload_job_uuid = storage_folder.add_private_data_to_folder_start(
str(integration_uuid),
DataUploadItems(
dicom_series=[
DataUploadDicomSeries(
title=f"dicom series - {dicom_series_name}",
dicom_files=[
DataUploadDicomSeriesDicomFile( # type: ignore[call-arg]
url=f"https://custom-direct-access-url.com/{dicom_series_name}/{dicom_file}",
title=f"dicom file - {dicom_series_name}/{dicom_file}",
dicom_metadata=CustomerProvidedDicomSeriesDicomFileMetadata(
tags=pydicom_dataset_to_metadata_tags(
pydicom.dcmread(
dicom_dir / dicom_series_name / dicom_file,
force=True,
),
),
),
)
for dicom_file in dicom_series_map[dicom_series_name]
],
)
for dicom_series_name in dicom_series_map
],
),
True,
)
print(f"main-log: {upload_job_uuid=} upload job started")
res = storage_folder.add_private_data_to_folder_get_result(upload_job_uuid)
print(f"main-log: {upload_job_uuid=} upload job done")
print(f"main-log: {res.status=} {res.units_done_count=} {res.units_error_count=}")
for item_success in res.items_with_names:
print(
f"main-log: dicom series integrated successfully "
f"{item_success.item_uuid=} {item_success.name=}"
)
for item_error in res.unit_errors:
print(
f"main-log: dicom series integration error "
f"{item_error.error=} {item_error.object_urls=}"
)
if __name__ == "__main__":
ssh_private_key_path = Path(os.environ["ENCORD_SSH_KEY_FILE"]).resolve()
# type of Encord integration: direct - client only
cloud_integration_title = os.environ["CLOUD_INTEGRATION_TITLE"]
dicom_dir = Path(os.environ["DICOM_DIR"]).resolve()
main(
ssh_private_key_path,
cloud_integration_title,
dicom_dir,
)