dcmspec 0.2.2__tar.gz → 0.2.3__tar.gz
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-0.2.2 → dcmspec-0.2.3}/CHANGELOG.md +7 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/PKG-INFO +2 -2
- {dcmspec-0.2.2 → dcmspec-0.2.3}/README.md +1 -1
- {dcmspec-0.2.2 → dcmspec-0.2.3}/pyproject.toml +1 -1
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/csv_table_spec_parser.py +16 -7
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/doc_handler.py +15 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/dom_table_spec_parser.py +6 -6
- {dcmspec-0.2.2 → dcmspec-0.2.3}/LICENSE +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/.DS_Store +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/__init__.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/.DS_Store +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/__init__.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/cli/__init__.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/cli/dataelements.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/cli/iodattributes.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/cli/iodmodules.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/cli/modattributes.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/cli/tdwiicontent.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/cli/uidvalues.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/cli/upsdimseattributes.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/cli/upsioddimseattributes.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/ui/iod_explorer/README.md +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/ui/iod_explorer/__init__.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/ui/iod_explorer/config/README.md +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/ui/iod_explorer/config/iod_explorer_config.json +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/ui/iod_explorer/config/iod_explorer_config_debug.json +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/ui/iod_explorer/config/iod_explorer_config_example.json +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/ui/iod_explorer/config/iod_explorer_config_minimal_logging.json +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/ui/iod_explorer/iod_explorer.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/config.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/dom_utils.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/iod_spec_builder.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/iod_spec_printer.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/json_spec_store.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/module_registry.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/pdf_doc_handler.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/progress.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/service_attribute_defaults.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/service_attribute_model.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/spec_factory.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/spec_merger.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/spec_model.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/spec_parser.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/spec_printer.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/spec_store.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/ups_xhtml_doc_handler.py +0 -0
- {dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/xhtml_doc_handler.py +0 -0
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
These release notes summarize key changes, improvements, and breaking updates for each version of **dcmspec**.
|
|
4
4
|
|
|
5
|
+
## [0.2.3] - 2025-09-29
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Hotfix: Force UTF-8 decoding for DICOM standard XHTML downloads to prevent mojibake when server omits charset ([#85](https://github.com/dwikler/dcmspec/issues/85)).
|
|
10
|
+
- Hotfix: Add missing `progress_observer` argument to `CSVTableSpecParser.parse` for interface compatibility and to prevent `TypeError` when used with `SpecFactory` ([#86](https://github.com/dwikler/dcmspec/issues/86)).
|
|
11
|
+
|
|
5
12
|
## [0.2.2] - 2025-09-25
|
|
6
13
|
|
|
7
14
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dcmspec
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Toolkit for extracting, parsing, and processing DICOM specifications.
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -45,7 +45,7 @@ Description-Content-Type: text/markdown
|
|
|
45
45
|
[](https://github.com/dwikler/dcmspec/actions/workflows/test.yml)
|
|
46
46
|
[](https://badge.fury.io/py/dcmspec)
|
|
47
47
|
[](https://pypi.org/project/dcmspec/)
|
|
48
|
-
[](https://doi.org/10.5281/zenodo.17233195)
|
|
49
49
|
|
|
50
50
|
# dcmspec
|
|
51
51
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[](https://github.com/dwikler/dcmspec/actions/workflows/test.yml)
|
|
2
2
|
[](https://badge.fury.io/py/dcmspec)
|
|
3
3
|
[](https://pypi.org/project/dcmspec/)
|
|
4
|
-
[](https://doi.org/10.5281/zenodo.17233195)
|
|
5
5
|
|
|
6
6
|
# dcmspec
|
|
7
7
|
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
Provides the CSVTableSpecParser class for parsing DICOM specification tables in CSV format,
|
|
4
4
|
converting them into structured in-memory representations using anytree.
|
|
5
5
|
"""
|
|
6
|
-
from typing import Tuple
|
|
6
|
+
from typing import Dict, List, Tuple, Optional
|
|
7
7
|
from anytree import Node
|
|
8
8
|
|
|
9
9
|
from dcmspec.spec_parser import SpecParser
|
|
10
|
+
from dcmspec.progress import ProgressObserver
|
|
10
11
|
|
|
11
12
|
class CSVTableSpecParser(SpecParser):
|
|
12
13
|
"""Base parser for DICOM Specification IHE tables in CSV-like format."""
|
|
@@ -14,10 +15,11 @@ class CSVTableSpecParser(SpecParser):
|
|
|
14
15
|
def parse(
|
|
15
16
|
self,
|
|
16
17
|
table: dict,
|
|
17
|
-
column_to_attr,
|
|
18
|
-
name_attr="elem_name",
|
|
19
|
-
table_id=None,
|
|
20
|
-
include_depth=None,
|
|
18
|
+
column_to_attr: dict,
|
|
19
|
+
name_attr: str = "elem_name",
|
|
20
|
+
table_id: Optional[str] = None,
|
|
21
|
+
include_depth: Optional[int] = None,
|
|
22
|
+
progress_observer: Optional[ProgressObserver] = None,
|
|
21
23
|
) -> Tuple[Node, Node]:
|
|
22
24
|
"""Parse specification metadata and content from a single table dict.
|
|
23
25
|
|
|
@@ -27,11 +29,18 @@ class CSVTableSpecParser(SpecParser):
|
|
|
27
29
|
name_attr (str): The attribute to use for node names.
|
|
28
30
|
table_id (str, optional): Table identifier for model parsing.
|
|
29
31
|
include_depth (int, optional): The depth to which included tables should be parsed.
|
|
32
|
+
progress_observer (Optional[ProgressObserver]):
|
|
33
|
+
Accepted for interface compatibility, but ignored in this parser.
|
|
34
|
+
Included so that this method can be called with the same arguments as other table parsers.
|
|
30
35
|
|
|
31
36
|
Returns:
|
|
32
37
|
tuple: (metadata_node, content_node)
|
|
33
38
|
|
|
34
39
|
"""
|
|
40
|
+
if progress_observer is not None and hasattr(self, "logger"):
|
|
41
|
+
self.logger.debug(
|
|
42
|
+
"Progress reporting is not supported yet for CSV parsing and will be ignored."
|
|
43
|
+
)
|
|
35
44
|
# Use the header and data from the grouped table dict
|
|
36
45
|
header = table.get("header", [])
|
|
37
46
|
data = table.get("data", [])
|
|
@@ -47,8 +56,8 @@ class CSVTableSpecParser(SpecParser):
|
|
|
47
56
|
|
|
48
57
|
def parse_table(
|
|
49
58
|
self,
|
|
50
|
-
tables:
|
|
51
|
-
column_to_attr:
|
|
59
|
+
tables: List[List[List[str]]], # List of tables, each a list of rows (list of str)
|
|
60
|
+
column_to_attr: Dict[int, str],
|
|
52
61
|
name_attr: str = "elem_name",
|
|
53
62
|
) -> Node:
|
|
54
63
|
"""Build a tree from tables using column mapping and '>' nesting logic.
|
|
@@ -94,6 +94,8 @@ class DocHandler:
|
|
|
94
94
|
try:
|
|
95
95
|
with requests.get(url, timeout=30, stream=True, headers={"Accept-Encoding": "identity"}) as response:
|
|
96
96
|
response.raise_for_status()
|
|
97
|
+
self._set_response_encoding(response)
|
|
98
|
+
|
|
97
99
|
total = int(response.headers.get('content-length', 0))
|
|
98
100
|
chunk_size = 8192
|
|
99
101
|
if binary:
|
|
@@ -109,6 +111,19 @@ class DocHandler:
|
|
|
109
111
|
self.logger.error(f"Failed to save file {file_path}: {e}")
|
|
110
112
|
raise RuntimeError(f"Failed to save file {file_path}: {e}") from e
|
|
111
113
|
|
|
114
|
+
def _set_response_encoding(self, response):
|
|
115
|
+
"""Set response.encoding to UTF-8 only if the Content-Type header does not specify a charset.
|
|
116
|
+
|
|
117
|
+
Force utf-8 decoding if the web server does not specify the charset in the HTTP response
|
|
118
|
+
Content-Type header (DICOM standard XHTML files are always UTF-8 encoded).
|
|
119
|
+
"""
|
|
120
|
+
content_type = response.headers.get("Content-Type", "")
|
|
121
|
+
if "charset=" not in content_type.lower():
|
|
122
|
+
response.encoding = "utf-8"
|
|
123
|
+
self.logger.debug("No charset in Content-Type header; forcing UTF-8 decoding.")
|
|
124
|
+
else:
|
|
125
|
+
self.logger.debug(f"Using server-specified encoding from Content-Type: {content_type}")
|
|
126
|
+
|
|
112
127
|
def _report_progress(self, downloaded, total, progress_observer, last_percent):
|
|
113
128
|
"""Report progress if percent changed.
|
|
114
129
|
|
|
@@ -13,7 +13,7 @@ from typing import Any, Dict, Optional, Union
|
|
|
13
13
|
from dcmspec.spec_parser import SpecParser
|
|
14
14
|
|
|
15
15
|
from dcmspec.dom_utils import DOMUtils
|
|
16
|
-
from dcmspec.progress import Progress, ProgressStatus, calculate_percent
|
|
16
|
+
from dcmspec.progress import Progress, ProgressObserver, ProgressStatus, calculate_percent
|
|
17
17
|
|
|
18
18
|
class DOMTableSpecParser(SpecParser):
|
|
19
19
|
"""Parser for DICOM specification tables in XHTML DOM format.
|
|
@@ -43,7 +43,7 @@ class DOMTableSpecParser(SpecParser):
|
|
|
43
43
|
column_to_attr: Dict[int, str],
|
|
44
44
|
name_attr: str,
|
|
45
45
|
include_depth: Optional[int] = None, # None means unlimited
|
|
46
|
-
progress_observer: Optional[
|
|
46
|
+
progress_observer: Optional[ProgressObserver] = None,
|
|
47
47
|
skip_columns: Optional[list[int]] = None,
|
|
48
48
|
unformatted: Optional[Union[bool, Dict[int, bool]]] = True,
|
|
49
49
|
) -> tuple[Node, Node]:
|
|
@@ -59,7 +59,7 @@ class DOMTableSpecParser(SpecParser):
|
|
|
59
59
|
name_attr (str): The attribute name to use for building node names.
|
|
60
60
|
include_depth (Optional[int], optional): The depth to which included tables should be parsed.
|
|
61
61
|
None means unlimited.
|
|
62
|
-
progress_observer (Optional[
|
|
62
|
+
progress_observer (Optional[ProgressObserver]): Optional observer to report parsing progress.
|
|
63
63
|
skip_columns (Optional[list[int]]): List of column indices to skip if the row is missing a column.
|
|
64
64
|
This argument is typically set via `parser_kwargs` when using SpecFactory.
|
|
65
65
|
unformatted (Optional[Union[bool, Dict[int, bool]]]):
|
|
@@ -132,7 +132,7 @@ class DOMTableSpecParser(SpecParser):
|
|
|
132
132
|
name_attr: str,
|
|
133
133
|
table_nesting_level: int = 0,
|
|
134
134
|
include_depth: Optional[int] = None, # None means unlimited
|
|
135
|
-
progress_observer: Optional[
|
|
135
|
+
progress_observer: Optional[ProgressObserver] = None,
|
|
136
136
|
skip_columns: Optional[list[int]] = None,
|
|
137
137
|
visited_tables: Optional[set] = None,
|
|
138
138
|
unformatted_list: Optional[list[bool]] = None,
|
|
@@ -151,7 +151,7 @@ class DOMTableSpecParser(SpecParser):
|
|
|
151
151
|
name_attr: tree node attribute name to use to build node name
|
|
152
152
|
table_nesting_level: The nesting level of the table (used for recursion call only).
|
|
153
153
|
include_depth: The depth to which included tables should be parsed.
|
|
154
|
-
progress_observer (Optional[
|
|
154
|
+
progress_observer (Optional[ProgressObserver]): Optional observer to report parsing progress.
|
|
155
155
|
skip_columns (Optional[list[int]]): List of column indices to skip if the row is missing a column.
|
|
156
156
|
visited_tables (Optional[set]): Set of table IDs that have been visited to prevent infinite recursion.
|
|
157
157
|
unformatted_list (Optional[list[bool]]): List of booleans indicating whether to extract each column as
|
|
@@ -287,7 +287,7 @@ class DOMTableSpecParser(SpecParser):
|
|
|
287
287
|
unformatted_list: list[bool],
|
|
288
288
|
level_nodes: Dict[int, Node],
|
|
289
289
|
root: Node,
|
|
290
|
-
progress_observer: Optional[
|
|
290
|
+
progress_observer: Optional[ProgressObserver] = None
|
|
291
291
|
) -> None:
|
|
292
292
|
"""Process all rows in the table, handling recursion, nesting, and node creation."""
|
|
293
293
|
rows = table.find_all("tr")[1:]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dcmspec-0.2.2 → dcmspec-0.2.3}/src/dcmspec/apps/ui/iod_explorer/config/iod_explorer_config.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|