dcmspec 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dcmspec/__init__.py +0 -0
- dcmspec/apps/__init__.py +1 -0
- dcmspec/apps/cli/__init__.py +0 -0
- dcmspec/apps/cli/dataelements.py +89 -0
- dcmspec/apps/cli/iodattributes.py +125 -0
- dcmspec/apps/cli/iodmodules.py +92 -0
- dcmspec/apps/cli/modattributes.py +265 -0
- dcmspec/apps/cli/tdwiicontent.py +365 -0
- dcmspec/apps/cli/uidvalues.py +84 -0
- dcmspec/apps/cli/upsdimseattributes.py +109 -0
- dcmspec/apps/cli/upsioddimseattributes.py +330 -0
- dcmspec/apps/ui/iod_explorer/README.md +33 -0
- dcmspec/apps/ui/iod_explorer/__init__.py +9 -0
- dcmspec/apps/ui/iod_explorer/config/README.md +171 -0
- dcmspec/apps/ui/iod_explorer/config/iod_explorer_config.json +4 -0
- dcmspec/apps/ui/iod_explorer/config/iod_explorer_config_debug.json +4 -0
- dcmspec/apps/ui/iod_explorer/config/iod_explorer_config_example.json +4 -0
- dcmspec/apps/ui/iod_explorer/config/iod_explorer_config_minimal_logging.json +4 -0
- dcmspec/apps/ui/iod_explorer/iod_explorer.py +989 -0
- dcmspec/config.py +90 -0
- dcmspec/csv_table_spec_parser.py +85 -0
- dcmspec/doc_handler.py +214 -0
- dcmspec/dom_table_spec_parser.py +831 -0
- dcmspec/dom_utils.py +116 -0
- dcmspec/iod_spec_builder.py +444 -0
- dcmspec/iod_spec_printer.py +59 -0
- dcmspec/json_spec_store.py +110 -0
- dcmspec/module_registry.py +51 -0
- dcmspec/pdf_doc_handler.py +451 -0
- dcmspec/progress.py +232 -0
- dcmspec/service_attribute_defaults.py +124 -0
- dcmspec/service_attribute_model.py +231 -0
- dcmspec/spec_factory.py +451 -0
- dcmspec/spec_merger.py +536 -0
- dcmspec/spec_model.py +461 -0
- dcmspec/spec_parser.py +37 -0
- dcmspec/spec_printer.py +131 -0
- dcmspec/spec_store.py +50 -0
- dcmspec/ups_xhtml_doc_handler.py +117 -0
- dcmspec/xhtml_doc_handler.py +182 -0
- dcmspec-0.2.1.dist-info/METADATA +139 -0
- dcmspec-0.2.1.dist-info/RECORD +45 -0
- dcmspec-0.2.1.dist-info/WHEEL +4 -0
- dcmspec-0.2.1.dist-info/entry_points.txt +11 -0
- dcmspec-0.2.1.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"""CLI for extracting and printing the TDW-II content specifications from the IHE-RO Supplement.
|
|
2
|
+
|
|
3
|
+
IHE profiles define additional requirements for DICOM Services and Objects.
|
|
4
|
+
IOD specifications (which modules are required for each IOD) are defined in section 7.3, "IOD Definitions."
|
|
5
|
+
Module specifications (which attributes are defined within each module) are found in section 7.4, "Module Definitions."
|
|
6
|
+
In IHE-RO profiles, module specifications are organized into two parts:
|
|
7
|
+
- a base definition, which applies to all transactions or content definitions where the module is used
|
|
8
|
+
- an extension, which applies only to a particular transaction or content definition.
|
|
9
|
+
|
|
10
|
+
This CLI tool focuses on the IHE-RO TDW-II workflow profile and extract the content definitions relevant to each
|
|
11
|
+
transaction.
|
|
12
|
+
|
|
13
|
+
Features:
|
|
14
|
+
- Download and parse TDW-II module base and extension definition tables from the IHE-RO Supplement in PDF format.
|
|
15
|
+
- Extract and print the module definition as a table and a tree.
|
|
16
|
+
- Cache the resulting model as a JSON file for future runs and as a structured representation of the table.
|
|
17
|
+
- Supports configuration, caching, and command-line options for flexible workflows.
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
poetry run python -m src.dcmspec.apps.cli.tdwiimoddefinition [options]
|
|
21
|
+
|
|
22
|
+
For more details, use the --help option.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import argparse
|
|
26
|
+
from copy import deepcopy
|
|
27
|
+
import logging
|
|
28
|
+
|
|
29
|
+
from anytree import Node
|
|
30
|
+
|
|
31
|
+
from dcmspec.json_spec_store import JSONSpecStore
|
|
32
|
+
from dcmspec.pdf_doc_handler import PDFDocHandler
|
|
33
|
+
from dcmspec.csv_table_spec_parser import CSVTableSpecParser
|
|
34
|
+
from dcmspec.spec_factory import SpecFactory
|
|
35
|
+
from dcmspec.spec_model import SpecModel
|
|
36
|
+
from dcmspec.spec_printer import SpecPrinter
|
|
37
|
+
|
|
38
|
+
# Configurations for each content definition
|
|
39
|
+
CONTENT_DEFINITION_CONFIGS = {
|
|
40
|
+
"ups_scheduled": {
|
|
41
|
+
"page_numbers": [57, 58],
|
|
42
|
+
"table_indices": [(57, 1), (58, 0)],
|
|
43
|
+
"table_id": "tdwii_ups_scheduled_info",
|
|
44
|
+
"column_to_attr": {0: "elem_name", 1: "elem_tag", 2: "elem_type", 3: "elem_description"},
|
|
45
|
+
},
|
|
46
|
+
"ups_performed": {
|
|
47
|
+
"page_numbers": [61, 62],
|
|
48
|
+
"table_indices": [(61, 0), (62, 0)],
|
|
49
|
+
"table_id": "tdwii_ups_performed_info",
|
|
50
|
+
"column_to_attr": {0: "elem_name", 1: "elem_tag", 2: "elem_type", 3: "elem_description"},
|
|
51
|
+
},
|
|
52
|
+
"ups_progress": {
|
|
53
|
+
"page_numbers": [64],
|
|
54
|
+
"table_indices": [(64,0)],
|
|
55
|
+
"table_header_rowspan": {(64,0): 1},
|
|
56
|
+
"table_id": "tdwii_ups_progress_info",
|
|
57
|
+
"column_to_attr": {0: "elem_name", 1: "elem_tag", 2: "elem_type", 3: "elem_description"},
|
|
58
|
+
},
|
|
59
|
+
"ups_relationship": {
|
|
60
|
+
"page_numbers": [60],
|
|
61
|
+
"table_indices": [(60, 1)],
|
|
62
|
+
"table_id": "tdwii_ups_relationship_info",
|
|
63
|
+
"column_to_attr": {0: "elem_name", 1: "elem_tag", 2: "elem_type", 3: "elem_description"},
|
|
64
|
+
},
|
|
65
|
+
"ups_query": {
|
|
66
|
+
"page_numbers": [63],
|
|
67
|
+
"table_indices": [(63, 0)],
|
|
68
|
+
"table_header_rowspan": {(63, 0): 2},
|
|
69
|
+
"table_id": "tdwii_ups_query_info",
|
|
70
|
+
"column_to_attr": {
|
|
71
|
+
0: "elem_name",
|
|
72
|
+
1: "elem_tag",
|
|
73
|
+
2: "key_matching_scu",
|
|
74
|
+
3: "key_matching_scp",
|
|
75
|
+
4: "key_return_scu",
|
|
76
|
+
5: "key_return_scp",
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
"rt_bdi": {
|
|
80
|
+
"page_numbers": [55, 56, 57],
|
|
81
|
+
"table_indices": [(55, 1), (56, 0), (57, 0)],
|
|
82
|
+
"table_id": "tdwii_bdi_instruction_info",
|
|
83
|
+
"column_to_attr": {0: "elem_name", 1: "elem_tag", 2: "elem_type", 3: "elem_description"},
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
def _extract_module_definition(
|
|
88
|
+
pdf_file,
|
|
89
|
+
url,
|
|
90
|
+
page_numbers,
|
|
91
|
+
table_indices,
|
|
92
|
+
table_header_rowspan,
|
|
93
|
+
table_id,
|
|
94
|
+
column_to_attr,
|
|
95
|
+
logger,
|
|
96
|
+
force_parse=False,
|
|
97
|
+
):
|
|
98
|
+
"""Extract and return the TDW-II module definition model."""
|
|
99
|
+
factory = SpecFactory(
|
|
100
|
+
input_handler=PDFDocHandler(logger=logger, extractor="pdfplumber"),
|
|
101
|
+
table_parser=CSVTableSpecParser(logger=logger),
|
|
102
|
+
column_to_attr=column_to_attr,
|
|
103
|
+
name_attr="elem_name",
|
|
104
|
+
logger=logger
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
handler_kwargs = {
|
|
108
|
+
"page_numbers": page_numbers,
|
|
109
|
+
"table_indices": table_indices,
|
|
110
|
+
"table_header_rowspan": table_header_rowspan,
|
|
111
|
+
"table_id": table_id,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return factory.create_model(
|
|
115
|
+
url=url,
|
|
116
|
+
cache_file_name=pdf_file,
|
|
117
|
+
table_id=table_id,
|
|
118
|
+
force_parse=force_parse,
|
|
119
|
+
json_file_name=f"{table_id}.json",
|
|
120
|
+
handler_kwargs=handler_kwargs,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def _parse_args():
|
|
124
|
+
"""Parse and return command-line arguments for the TDW-II CLI."""
|
|
125
|
+
parser = argparse.ArgumentParser(description="Extract and print TDW-II UPS module information tables.")
|
|
126
|
+
parser.add_argument(
|
|
127
|
+
"content_definition",
|
|
128
|
+
choices=[
|
|
129
|
+
"ups_create",
|
|
130
|
+
"ups_performed",
|
|
131
|
+
"ups_progress",
|
|
132
|
+
"ups_query",
|
|
133
|
+
"rt_bdi"
|
|
134
|
+
# Add more content_definition names here as you add configs
|
|
135
|
+
],
|
|
136
|
+
help=(
|
|
137
|
+
"Select which content definition to extract: "
|
|
138
|
+
"'ups_create', 'ups_query', 'ups_progress', 'ups_performed', 'rt_bdi'"
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
parser.add_argument("-d", "--debug", action="store_true", help="Enable debug logging")
|
|
142
|
+
parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose output")
|
|
143
|
+
return parser.parse_args()
|
|
144
|
+
|
|
145
|
+
def _setup_logger(debug=False, verbose=False):
|
|
146
|
+
"""Set up and return a logger with the appropriate log level."""
|
|
147
|
+
if debug:
|
|
148
|
+
log_level = logging.DEBUG
|
|
149
|
+
elif verbose:
|
|
150
|
+
log_level = logging.INFO
|
|
151
|
+
else:
|
|
152
|
+
log_level = logging.WARNING
|
|
153
|
+
|
|
154
|
+
logging.basicConfig(
|
|
155
|
+
level=log_level,
|
|
156
|
+
format="%(asctime)s - %(levelname)s - %(message)s"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Suppress PDFMiner debug logs
|
|
160
|
+
logging.getLogger("pdfminer").setLevel(logging.WARNING)
|
|
161
|
+
|
|
162
|
+
logger = logging.getLogger("tdwiimoddefinition")
|
|
163
|
+
logger.setLevel(log_level) # <-- This ensures the logger is always at the correct level
|
|
164
|
+
|
|
165
|
+
return logger
|
|
166
|
+
|
|
167
|
+
def _extract_ups_create_model(pdf_file, url, logger):
|
|
168
|
+
"""Extract and combine UPS Scheduled and Relationship module definition."""
|
|
169
|
+
base_config = CONTENT_DEFINITION_CONFIGS["ups_scheduled"]
|
|
170
|
+
base_config_part2 = CONTENT_DEFINITION_CONFIGS["ups_relationship"]
|
|
171
|
+
|
|
172
|
+
scheduled_model = _extract_module_definition(
|
|
173
|
+
pdf_file=pdf_file,
|
|
174
|
+
url=url,
|
|
175
|
+
page_numbers=base_config["page_numbers"],
|
|
176
|
+
table_indices=base_config["table_indices"],
|
|
177
|
+
table_header_rowspan=base_config.get("table_header_rowspan", None),
|
|
178
|
+
table_id=base_config["table_id"],
|
|
179
|
+
column_to_attr=base_config["column_to_attr"],
|
|
180
|
+
logger=logger,
|
|
181
|
+
force_parse=False,
|
|
182
|
+
)
|
|
183
|
+
relationship_model = _extract_module_definition(
|
|
184
|
+
pdf_file=pdf_file,
|
|
185
|
+
url=url,
|
|
186
|
+
page_numbers=base_config_part2["page_numbers"],
|
|
187
|
+
table_indices=base_config_part2["table_indices"],
|
|
188
|
+
table_header_rowspan=base_config.get("table_header_rowspan", None),
|
|
189
|
+
table_id=base_config_part2["table_id"],
|
|
190
|
+
column_to_attr=base_config_part2["column_to_attr"],
|
|
191
|
+
logger=logger,
|
|
192
|
+
force_parse=False,
|
|
193
|
+
)
|
|
194
|
+
# Combine the two models under a new root node
|
|
195
|
+
combined_root = Node("content")
|
|
196
|
+
# Attach all children of scheduled_model.content
|
|
197
|
+
for child in list(scheduled_model.content.children):
|
|
198
|
+
child.parent = combined_root
|
|
199
|
+
# Attach all children of relationship_model.content
|
|
200
|
+
for child in list(relationship_model.content.children):
|
|
201
|
+
child.parent = combined_root
|
|
202
|
+
|
|
203
|
+
# Use metadata from scheduled_model, but set a new table_id for the combined model
|
|
204
|
+
combined_metadata = deepcopy(scheduled_model.metadata)
|
|
205
|
+
combined_metadata.table_id = "tdwii_ups_create_info"
|
|
206
|
+
return SpecModel(
|
|
207
|
+
metadata=combined_metadata,
|
|
208
|
+
content=combined_root,
|
|
209
|
+
logger=logger,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
def _apply_hard_coded_extensions(model, content_definition_name):
|
|
213
|
+
"""Apply hard-coded extensions to the base model for specific modules."""
|
|
214
|
+
for node in model.content.children:
|
|
215
|
+
elem_name = getattr(node, "elem_name", "").strip()
|
|
216
|
+
|
|
217
|
+
if content_definition_name == "ups_create":
|
|
218
|
+
if elem_name == "Scheduled Workitem Code Sequence":
|
|
219
|
+
for child in node.children:
|
|
220
|
+
child_name = getattr(child, "elem_name", "").strip()
|
|
221
|
+
if child_name == ">Code Value":
|
|
222
|
+
child.elem_description = 'shall be equal to "121726"'
|
|
223
|
+
elif child_name == ">Coding Scheme Designator":
|
|
224
|
+
child.elem_description = 'shall be equal to "DCM"'
|
|
225
|
+
elif child_name == ">Code Meaning":
|
|
226
|
+
child.elem_description = 'shall be equal to "RT Treatment with Internal Verification"'
|
|
227
|
+
continue
|
|
228
|
+
if elem_name == "Input Information Sequence":
|
|
229
|
+
node.elem_description = (
|
|
230
|
+
"Shall contain at least 2 Referenced DICOM Instances:"
|
|
231
|
+
"\nRT Plan Storage or RT Ion Plan Storage retrieved from OST."
|
|
232
|
+
"\nRT Beams Delivery Instruction Storage retrieved from TMS."
|
|
233
|
+
"\nShall contain more Referenced DICOM Instances if Treatment Delivery Type is equal to "
|
|
234
|
+
"CONTINUATION:"
|
|
235
|
+
"\nRT Beams Treatment Record Storage or RT Ion Beams Treatment Record Storage retrieved from OST."
|
|
236
|
+
"\nMay contain additional items for other reasons, which are out of scope for this profile"
|
|
237
|
+
)
|
|
238
|
+
continue
|
|
239
|
+
if elem_name == "Scheduled Processing Parameters Sequence":
|
|
240
|
+
node.elem_description = (
|
|
241
|
+
"Shall include 4 Content Items per Template:"
|
|
242
|
+
'\nEV (121740, DCM, "Treatment Delivery Type"), VT:TEXT (TREAMENT or CONTINUATION).'
|
|
243
|
+
'\nEV (2018001, 99IHERO2018, "Plan Label"), VT:TEXT (RT Plan Label (300A,0002) value).'
|
|
244
|
+
'\nEV (2018002, 99IHERO2018, "Current Fraction Number"), VT:NUMERIC '
|
|
245
|
+
"(Current Fraction Number (3008,0022) value)."
|
|
246
|
+
'\nEV (2018003, 99IHERO2018, "Number of Fractions Planned"), VT:NUMERIC '
|
|
247
|
+
"(Number of Fractions Planned (300A,0078) value)."
|
|
248
|
+
)
|
|
249
|
+
continue
|
|
250
|
+
|
|
251
|
+
if content_definition_name == "ups_performed":
|
|
252
|
+
if elem_name == ">Performed Workitem Code Sequence":
|
|
253
|
+
for child in node.children:
|
|
254
|
+
child_name = getattr(child, "elem_name", "").strip()
|
|
255
|
+
if child_name == ">Code Value":
|
|
256
|
+
child.elem_description = 'shall be equal to "121726"'
|
|
257
|
+
elif child_name == ">Coding Scheme Designator":
|
|
258
|
+
child.elem_description = 'shall be equal to "DCM"'
|
|
259
|
+
elif child_name == ">Code Meaning":
|
|
260
|
+
child.elem_description = 'shall be equal to "RT Treatment with Internal Verification"'
|
|
261
|
+
continue
|
|
262
|
+
if elem_name == ">Output Information Sequence":
|
|
263
|
+
node.elem_description = (
|
|
264
|
+
"Shall contain at least 1 item if any therapeutic treatment was delivered to the patient:"
|
|
265
|
+
"\nRT Beams Treatment Record Storage (1.2.840.10008.5.1.4.1.1.481.4) or "
|
|
266
|
+
"RT Ion Beams Treatment Record Storage (1.2.840.10008.5.1.4.1.1.481.9) stored to OST."
|
|
267
|
+
"\nMay be present otherwise."
|
|
268
|
+
)
|
|
269
|
+
continue
|
|
270
|
+
|
|
271
|
+
if content_definition_name == "ups_progress" and elem_name == "Procedure Step Progress Information Sequence":
|
|
272
|
+
# Add missing Procedure Step Progress Parameters Sequence to the model
|
|
273
|
+
Node(
|
|
274
|
+
"procedure_step_progress_parameters_sequence",
|
|
275
|
+
parent=node,
|
|
276
|
+
elem_name=">Procedure Step Progress Parameters Sequence",
|
|
277
|
+
elem_tag="(0074,1007)",
|
|
278
|
+
elem_type="R+*",
|
|
279
|
+
elem_description=(
|
|
280
|
+
"Shall include 1 Content Item per Template:"
|
|
281
|
+
'\nEV (2018004, 99IHERO2018, "Referenced Beam Number"), VT:NUM '
|
|
282
|
+
"(Beam Number (300A,00C0) value of beam in progress)."
|
|
283
|
+
),
|
|
284
|
+
)
|
|
285
|
+
continue
|
|
286
|
+
|
|
287
|
+
def main():
|
|
288
|
+
"""CLI for extracting and printing the TDW-II Content Definitions from the IHE-RO Supplement.
|
|
289
|
+
|
|
290
|
+
This CLI downloads, parses, and prints the TDW-II Content Definitions from the IHE-RO Supplement (PDF).
|
|
291
|
+
The tool extracts the relevant table(s) from the PDF, parses the content definition, and outputs the result as a
|
|
292
|
+
table and a tree.
|
|
293
|
+
|
|
294
|
+
The resulting model is cached as a JSON file. The primary purpose of this cache file is to provide a structured,
|
|
295
|
+
machine-readable representation of the content definition, which can be used for further processing or integration
|
|
296
|
+
in other tools. As a secondary benefit, the cache file is also used to speed up subsequent runs of the CLI scripts.
|
|
297
|
+
|
|
298
|
+
Usage:
|
|
299
|
+
poetry run python -m src.dcmspec.apps.cli.tdwiicontent <content_definition> [options]
|
|
300
|
+
|
|
301
|
+
Where <content_definition> is one of:
|
|
302
|
+
- ups_create: content of scheduled UPS creation (combined UPS Scheduled and Relationship definitions).
|
|
303
|
+
- ups_query: content of C-FIND identifier for UPS Query transaction.
|
|
304
|
+
- ups_progress: content of N-SET dataset for UPS Progress Update transaction.
|
|
305
|
+
- ups_performed: content of N-SET dataset for UPS Final Status Update transaction.
|
|
306
|
+
- rt_bdi: content of RT Beam Delivery Instruction Module.
|
|
307
|
+
|
|
308
|
+
Options:
|
|
309
|
+
-d, --debug: Enable debug logging.
|
|
310
|
+
-v, --verbose: Enable verbose output.
|
|
311
|
+
|
|
312
|
+
Example:
|
|
313
|
+
poetry run python -m src.dcmspec.apps.cli.tdwiicontent ups_progress --debug
|
|
314
|
+
|
|
315
|
+
For more details, use the --help option.
|
|
316
|
+
|
|
317
|
+
"""
|
|
318
|
+
args = _parse_args()
|
|
319
|
+
logger = _setup_logger(debug=args.debug, verbose=args.verbose)
|
|
320
|
+
|
|
321
|
+
logger.info(f"Extracting TDW-II module definition for: {args.content_definition}")
|
|
322
|
+
logger.debug(f"Arguments: {args}")
|
|
323
|
+
|
|
324
|
+
# TDW-II Supplement PDF and URL (shared for all module extractions)
|
|
325
|
+
pdf_file = "IHE_RO_Suppl_TDW_II.pdf"
|
|
326
|
+
url = "https://www.ihe.net/uploadedFiles/Documents/Radiation_Oncology/IHE_RO_Suppl_TDW_II.pdf"
|
|
327
|
+
|
|
328
|
+
# Special handling for ups_create: combine ups_scheduled and ups_relationship models
|
|
329
|
+
if args.content_definition == "ups_create":
|
|
330
|
+
base_model = _extract_ups_create_model(pdf_file, url, logger)
|
|
331
|
+
else:
|
|
332
|
+
# Select config based on argument
|
|
333
|
+
base_config = CONTENT_DEFINITION_CONFIGS[args.content_definition]
|
|
334
|
+
|
|
335
|
+
# Extract base and extension models
|
|
336
|
+
base_model = _extract_module_definition(
|
|
337
|
+
pdf_file=pdf_file,
|
|
338
|
+
url=url,
|
|
339
|
+
page_numbers=base_config["page_numbers"],
|
|
340
|
+
table_indices=base_config["table_indices"],
|
|
341
|
+
table_header_rowspan=base_config.get("table_header_rowspan", None),
|
|
342
|
+
table_id=base_config["table_id"],
|
|
343
|
+
column_to_attr=base_config["column_to_attr"],
|
|
344
|
+
logger=logger,
|
|
345
|
+
force_parse=False,
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
# Apply hard-coded extensions for specific modules when the extension specifications is not structured
|
|
349
|
+
# as a single table in the PDF document
|
|
350
|
+
_apply_hard_coded_extensions(base_model, args.content_definition)
|
|
351
|
+
|
|
352
|
+
# Cache the final model (base or combined, with hard-coded extensions)
|
|
353
|
+
json_store = JSONSpecStore(logger=logger)
|
|
354
|
+
cache_file_name = f"{getattr(base_model.metadata, 'table_id', 'unknown')}.json"
|
|
355
|
+
cache_path = f"./cache/model/{cache_file_name}"
|
|
356
|
+
json_store.save(base_model, cache_path)
|
|
357
|
+
logger.info(f"Final model saved as JSON to {cache_path}")
|
|
358
|
+
|
|
359
|
+
# Print the model
|
|
360
|
+
printer = SpecPrinter(base_model, logger=logger)
|
|
361
|
+
printer.print_table(colorize=True)
|
|
362
|
+
printer.print_tree(attr_names=["elem_name", "elem_tag"], colorize=True)
|
|
363
|
+
|
|
364
|
+
if __name__ == "__main__":
|
|
365
|
+
main()
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""CLI for extracting, caching, and printing DICOM Unique Identifiers (UIDs) from Part 6.
|
|
2
|
+
|
|
3
|
+
Features:
|
|
4
|
+
- Download and parse DICOM UID Values table from Part 6 of the DICOM standard.
|
|
5
|
+
- Cache the model as a JSON file for future runs and as a structured representation of the standard.
|
|
6
|
+
- Print the resulting UID Values as a table.
|
|
7
|
+
- Supports caching, configuration files, and command-line options for flexible workflows.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
poetry run python -m src.dcmspec.apps.cli.uidvalues [options]
|
|
11
|
+
|
|
12
|
+
For more details, use the --help option.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
import argparse
|
|
17
|
+
from dcmspec.config import Config
|
|
18
|
+
|
|
19
|
+
from dcmspec.spec_factory import SpecFactory
|
|
20
|
+
from dcmspec.spec_printer import SpecPrinter
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main():
|
|
24
|
+
"""CLI for parsing, caching, and printing DICOM Unique Identifiers (UIDs) from Part 6.
|
|
25
|
+
|
|
26
|
+
This CLI downloads, caches, and prints the list of DICOM UIDs from Part 6 of the DICOM standard.
|
|
27
|
+
|
|
28
|
+
The tool parses the UID Values table to extract UID values, names, keywords, types, and additional information.
|
|
29
|
+
The output can be printed as a table.
|
|
30
|
+
|
|
31
|
+
The resulting model is cached as a JSON file. The primary purpose of this cache file is to provide a structured,
|
|
32
|
+
machine-readable representation of the DICOM UIDs, which can be used for further processing or integration in other
|
|
33
|
+
tools. As a secondary benefit, the cache file is also used to speed up subsequent runs of the CLI scripts.
|
|
34
|
+
|
|
35
|
+
Usage:
|
|
36
|
+
poetry run python -m src.dcmspec.apps.cli.uidvalues [options]
|
|
37
|
+
|
|
38
|
+
Options:
|
|
39
|
+
--config (str): Path to the configuration file.
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
poetry run python -m src.dcmspec.apps.cli.uidvalues
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
# Parse command-line arguments
|
|
46
|
+
parser = argparse.ArgumentParser()
|
|
47
|
+
parser.add_argument("--config", help="Path to the configuration file")
|
|
48
|
+
args = parser.parse_args()
|
|
49
|
+
|
|
50
|
+
# Determine config file location
|
|
51
|
+
config_file = args.config or os.getenv("DCMSPEC_CONFIG", None)
|
|
52
|
+
|
|
53
|
+
# Initialize configuration
|
|
54
|
+
config = Config(app_name="dcmspec", config_file=config_file)
|
|
55
|
+
|
|
56
|
+
url = "https://dicom.nema.org/medical/dicom/current/output/html/part06.html"
|
|
57
|
+
cache_file_name = "Part6.xhtml"
|
|
58
|
+
model_file_name = "UIDValues.json"
|
|
59
|
+
|
|
60
|
+
table_id = "table_A-1"
|
|
61
|
+
|
|
62
|
+
# Create the factory
|
|
63
|
+
factory = SpecFactory(
|
|
64
|
+
column_to_attr={0: "uid_value", 1: "uid_name", 2: "uid_keyword", 3: "uid_type", 4: "uid_part"},
|
|
65
|
+
name_attr="uid_value",
|
|
66
|
+
config=config
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Download, parse, and cache the model
|
|
70
|
+
model = factory.create_model(
|
|
71
|
+
url=url,
|
|
72
|
+
cache_file_name=cache_file_name,
|
|
73
|
+
json_file_name=model_file_name,
|
|
74
|
+
table_id=table_id,
|
|
75
|
+
force_download=False,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Print the model as a table
|
|
79
|
+
printer = SpecPrinter(model)
|
|
80
|
+
printer.print_table(colorize=True)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == "__main__":
|
|
84
|
+
main()
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""CLI for extracting, caching, and printing DICOM UPS DIMSE Service Attribute tables from Part 4.
|
|
2
|
+
|
|
3
|
+
Features:
|
|
4
|
+
- Download and parse UPS DIMSE Service Attribute table from Part 4 of the DICOM standard.
|
|
5
|
+
- Select a specific DIMSE service and role for filtering.
|
|
6
|
+
- Cache the model as a JSON file for future runs and as a structured representation of the standard.
|
|
7
|
+
- Print the resulting attributes as a table.
|
|
8
|
+
- Supports caching, configuration files, and command-line options for flexible workflows.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
poetry run python -m src.dcmspec.apps.cli.upsdimseattributes [options]
|
|
12
|
+
|
|
13
|
+
For more details, use the --help option.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
import argparse
|
|
18
|
+
from dcmspec.config import Config
|
|
19
|
+
|
|
20
|
+
from dcmspec.service_attribute_model import ServiceAttributeModel
|
|
21
|
+
from dcmspec.spec_factory import SpecFactory
|
|
22
|
+
from dcmspec.spec_printer import SpecPrinter
|
|
23
|
+
from dcmspec.ups_xhtml_doc_handler import UPSXHTMLDocHandler
|
|
24
|
+
from dcmspec.service_attribute_defaults import UPS_DIMSE_MAPPING, UPS_COLUMNS_MAPPING, UPS_NAME_ATTR
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def main():
|
|
28
|
+
"""CLI for parsing, caching, and printing DICOM UPS DIMSE Service Attribute tables from Part 4.
|
|
29
|
+
|
|
30
|
+
This CLI downloads, caches, and prints the attributes for the UPS DIMSE services from Part 4 of the DICOM standard.
|
|
31
|
+
|
|
32
|
+
The tool parses the UPS Service Attribute table and allows selection of a specific DIMSE service (e.g., N-CREATE,
|
|
33
|
+
N-SET, N-GET, C-FIND, FINAL) and role (SCU or SCP). The output can be printed as a table.
|
|
34
|
+
|
|
35
|
+
The resulting model is cached as a JSON file. The primary purpose of this cache file is to provide a structured,
|
|
36
|
+
machine-readable representation of the UPS DIMSE service attributes, which can be used for further processing or
|
|
37
|
+
integration in other tools. As a secondary benefit, the cache file is also used to speed up subsequent runs of the
|
|
38
|
+
CLI scripts.
|
|
39
|
+
|
|
40
|
+
Usage:
|
|
41
|
+
poetry run python -m src.dcmspec.apps.cli.upsdimseattributes [options]
|
|
42
|
+
|
|
43
|
+
Options:
|
|
44
|
+
--config (str): Path to the configuration file.
|
|
45
|
+
--dimse (str): DIMSE service to select (ALL_DIMSE, N-CREATE, N-SET, N-GET, C-FIND, FINAL).
|
|
46
|
+
--role (str): Role to select (SCU or SCP). Only valid if --dimse is not ALL_DIMSE.
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
poetry run python -m src.dcmspec.apps.cli.upsdimseattributes --dimse N-CREATE --role SCU
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
# Parse command-line arguments
|
|
53
|
+
parser = argparse.ArgumentParser()
|
|
54
|
+
parser.add_argument("--config", help="Path to the configuration file")
|
|
55
|
+
parser.add_argument(
|
|
56
|
+
"--dimse",
|
|
57
|
+
choices=["ALL_DIMSE", "N-CREATE", "N-SET", "N-GET", "C-FIND", "FINAL"],
|
|
58
|
+
default="ALL_DIMSE",
|
|
59
|
+
help="DIMSE service to select (default: ALL_DIMSE)",
|
|
60
|
+
)
|
|
61
|
+
parser.add_argument(
|
|
62
|
+
"--role",
|
|
63
|
+
choices=["SCU", "SCP"],
|
|
64
|
+
help="Role to select (SCU or SCP)",
|
|
65
|
+
)
|
|
66
|
+
args = parser.parse_args()
|
|
67
|
+
|
|
68
|
+
# Determine config file location
|
|
69
|
+
config_file = args.config or os.getenv("DCMSPEC_CONFIG", None)
|
|
70
|
+
|
|
71
|
+
# Initialize configuration
|
|
72
|
+
config = Config(app_name="dcmspec", config_file=config_file)
|
|
73
|
+
|
|
74
|
+
url = "https://dicom.nema.org/medical/dicom/current/output/chtml/part04/sect_CC.2.5.html"
|
|
75
|
+
cache_file_name = "UPSattributes.xhtml"
|
|
76
|
+
table_id = "table_CC.2.5-3"
|
|
77
|
+
|
|
78
|
+
# Create the factory with UPSXHTMLDocHandler for UPS-specific table patching
|
|
79
|
+
factory = SpecFactory(
|
|
80
|
+
model_class=ServiceAttributeModel,
|
|
81
|
+
input_handler=UPSXHTMLDocHandler(config=config),
|
|
82
|
+
column_to_attr=UPS_COLUMNS_MAPPING,
|
|
83
|
+
name_attr=UPS_NAME_ATTR,
|
|
84
|
+
config=config
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Download, parse, and cache the model
|
|
88
|
+
model = factory.create_model(
|
|
89
|
+
url=url,
|
|
90
|
+
cache_file_name=cache_file_name,
|
|
91
|
+
table_id=table_id,
|
|
92
|
+
force_download=False,
|
|
93
|
+
model_kwargs={"dimse_mapping": UPS_DIMSE_MAPPING},
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
if args.dimse!= "ALL_DIMSE":
|
|
97
|
+
model.select_dimse(args.dimse)
|
|
98
|
+
if args.role:
|
|
99
|
+
if args.dimse == "ALL_DIMSE":
|
|
100
|
+
parser.error("--role option can only be used if --dimse is not ALL_DIMSE")
|
|
101
|
+
model.select_role(args.role)
|
|
102
|
+
|
|
103
|
+
# Print the model as a table
|
|
104
|
+
printer = SpecPrinter(model)
|
|
105
|
+
printer.print_table(colorize=True)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
109
|
+
main()
|