-
Notifications
You must be signed in to change notification settings - Fork 1
/
template_checker.py
115 lines (98 loc) · 3.68 KB
/
template_checker.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
"""
check a header against best template
"""
from typing import TypedDict
from acq2sqlite import DBQuery
from dcmmeta2tsv import DicomTagReader, TagValues
#: Dictionary for mismatches in input (``have`` key) and template (``expect`` key)
ErrorCompare = TypedDict("ErrorCompare", {"have": str, "expect": str})
#: * | ``conforms``: false when a :py:data:`acq2sqlite.DBQuery.CONSTS`
#: | template-parameter between ``input`` and ``template`` mismatch
#: * | ``errors``: nested dict of {``mismatched_param``: ``{'have':...,'expect':...}}``
#: (parameter keyed dictionary with :py:class:`ErrorCompare` values)
#: * | ``input``: dict of all parameters of an input dicom header
#: | (:py:class:`dcmmeta2tsv.TagValues`)
#: * | ``template``: all the parameters of a template (matching Study, SeriesName)
#: | Also a :py:class:`dcmmeta2tsv.TagValues`
#:
#: Here's an example of :py:class:`CheckResult` datastructure in html/javascript
#: on the `static debug-enabled page <../_static/mrqart/index.html>`_
#:
#: .. image:: ../../sphinx/imgs/CheckResults_mrqart.png
#:
CheckResult = TypedDict(
"CheckResult",
{
"conforms": bool,
"input": TagValues,
"template": TagValues,
"errors": dict[str, ErrorCompare],
},
)
def find_errors(template: TagValues, current_hdr: TagValues) -> dict[str, ErrorCompare]:
"""
given a template and hdr, find any mismatches (non-conforming errors)
:param template: expected values
:param current_hdr: values we currently have
:returns: dictionary of tag key names and the have/expect values
"""
errors = {}
for k in DBQuery.CONSTS:
t_k = template.get(k, "null")
h_k = current_hdr.get(k, "null")
# TODO: more checks for specific headers
#: TR is in milliseconds. no need to keep decimals precision
if k == "TR":
if t_k == "null":
t_k = 0
if h_k == "null":
h_k = 0
check = int(float(t_k)) == int(float(h_k))
elif k == "iPAT":
check = t_k == h_k
else:
check = str(t_k) == str(h_k)
if check:
continue
errors[k] = {"expect": t_k, "have": h_k}
return errors
class TemplateChecker:
"""cache db connection and list of tags
read a dicom file and report if it conforms to the expected template
"""
def __init__(self, db=None):
"""
db connection and tag reader (from taglist.txt)
:param db: sql connection passed on to :py:class:`DBQuery`.
``None`` (default) is local sqlite3.
"""
self.db = DBQuery(db)
self.reader = DicomTagReader()
def check_file(self, dcm_path) -> CheckResult:
"""
File disbatch for :py:func:`TemplateChecker.check_header`
:param dcm_path: path to dicom file with header/parameters to read.
:returns: output of check_header
"""
hdr = self.reader.read_dicom_tags(dcm_path)
return self.check_header(hdr)
def check_header(self, hdr) -> CheckResult:
"""
Check acquisition parameters against it's template.
:param hdr: DB row or file dictionary desc. acq. to check against template
:returns: Conforming status, errors, and comparison information
"""
template = self.db.get_template(hdr["Project"], hdr["SequenceName"])
# no template, no errors
if template:
template = dict(template)
errors = find_errors(template, hdr)
else:
template = {}
errors = {}
return {
"conforms": not errors,
"errors": errors,
"input": hdr,
"template": template,
}