Skip to content

Commit 822e820

Browse files
committed
Merge remote-tracking branch 'origin/order_of_images' into order_of_images
2 parents 6aa8e23 + 8b862a4 commit 822e820

File tree

5 files changed

+166
-25
lines changed

5 files changed

+166
-25
lines changed

docs/best_practices/nwbfile_metadata.rst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,13 +202,19 @@ Species
202202
~~~~~~~
203203

204204
The ``species`` of a :nwb-schema:ref:`sec-Subject` should be set to the proper
205-
:wikipedia:`Latin binomial <Binomial_nomenclature>`. *E.g.*, a rat would be "Rattus norvegicus". Specific subspecies
206-
may be further specified by a dash, *e.g.*, "Rattus norvegicus - Long Evans".
205+
:wikipedia:`Latin binomial <Binomial_nomenclature>`. *E.g.*, a rat would be "Rattus norvegicus".
207206

208207
Check function: :py:meth:`~nwbinspector.checks.nwbfile_metadata.check_subject_species`
209208

210209

211210

211+
Strain
212+
~~~~~~~
213+
214+
The ``strain`` of a :nwb-schema:ref:`sec-Subject` should be set to further indicate the subspecies or breed or common genetic modification. *E.g.*, common strains for species "Rattus norvegicus" might include "Long Evans", "Sprague-Dawley", "Wistar", or "C57BL/6". If no specific strain is used, then simply indicate "Wild Type".
215+
216+
217+
212218
.. _best_practice_subject_age:
213219

214220
Age

docs/user_guide/command_line_usage.rst

Lines changed: 85 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,34 +17,106 @@ You may then run the NWBInspector via the command line via the following usages
1717
which should quickly display a basic report to your console window.
1818

1919

20-
Common Flags
21-
------------
20+
All available options for the CLI may be viewed by calling :code:`nwbinspector --help`. We will now highlight some of
21+
the most useful of these options.
22+
23+
24+
25+
Formatting the Report
26+
---------------------
27+
28+
The basic report layout organizes by :py:attr:`~nwbinspector.register_check.InspectorMessage.importance` first and
29+
:py:attr:`~nwbinspector.register_check.InspectorMessage.file_path` last.
30+
31+
For example,
32+
33+
::
34+
35+
insert example
36+
37+
however, the NWBInspector supports more general organization as defined by the `--levels` flag. To use this flag,
38+
you must pass a series a comma-separated words that correspond to any attributes of the
39+
:py:class:`~nwbinspector.register_check.InspectorMessage` in any order.
40+
41+
For example,
42+
43+
::
44+
45+
nwbinspector path/to/my/data/dir/ --levels check_function_name,object_type,file_path
46+
47+
.. note::
48+
49+
The :py:attr:`~nwbinspector.register_check.InspectorMessage.message` itself cannot be used in organization.
50+
51+
The ascending or descending order of each of these levels may be additionally controlled with the `--reverse` flag,
52+
which is likewise a comma-separated series of boolean words.
53+
54+
For example,
55+
56+
::
57+
58+
nwbinspector path/to/my/data/dir/ --levels check_function_name,object_type,file_path --reverse false,true,false
59+
60+
.. note::
61+
62+
Valid 'boolean words' are any capitalizations of the words ``"false", "true"`` or characters ``"F", "T"``, and even
63+
numbers like ``"0, 1"``.
64+
65+
66+
The defalt report also aggregates identical outputs into a summary over multiple files;
67+
68+
::
69+
70+
insert example
71+
72+
and it will do this any time the last value of the ``--levels`` list is ``file_path``. To see the full report of every
73+
issue over all files, use the ``--detailed`` flag.
74+
75+
76+
77+
Saving the Report
78+
-----------------
2279

2380
There are many common options you can specify with flags, such as saving the report for future reference...
2481

2582
::
2683

27-
nwbinspector path/to/my/data.nwb --log-file path/to/my/nwbinspector_report.txt
84+
nwbinspector path/to/my/data.nwb --report-file-path path/to/my/nwbinspector_report.txt
2885

29-
# if a report file from a previous run of the inspector is already present at the location
30-
# it can be overwritten with '-o'
31-
nwbinspector path/to/my/data.nwb -o --log-file path/to/my/nwbinspector_report.txt
3286

87+
If a report file from a previous run of the inspector is already present at the location, it can be overwritten with
88+
the ``-o`` or ``--overwrite`` flag...
89+
90+
::
3391

34-
Or enabling parallelization over a directory to allow the NWBInspector to run many times faster...
92+
nwbinspector path/to/my/data.nwb --report-file-path path/to/my/nwbinspector_report.txt -o
93+
94+
95+
96+
Faster Inspection
97+
-----------------
98+
99+
The NWBInspector supports parallelization over a directory to allow the NWBInspector to run many times faster. To use
100+
this feature, simply set the ``--n-jobs`` flag to either a positive integer corresponding to the number of CPUs you
101+
wish to use, or set to ``-1`` to use all available system resources.
102+
103+
For example,
35104

36105
::
37106

38-
# '--n-jobs -1' will automatically use as many resources as are available on your system
39107
nwbinspector path/to/my/data/dir/ --n-jobs -1
40108

41109

42-
And if your file was written using NWB extensions (link to extensions) that may possess their own specific
43-
checks, those checks can be added ...
44110

45-
::
111+
External Modules
112+
----------------
46113

47-
nwbinspector path/to/my/data.nwb -m my_extension_module1 my_extension_module2
114+
If the NWBFiles being inspected require an external module to parse, or have externally defined check registries (such
115+
as those specific to an :nwb-schema:ref:`NWB Extension<extending-the-format>`), these can be specified with the ``-m``
116+
or ``--modules`` flag.
48117

118+
For example,
49119

50-
Other flags may be viewed in the command line by calling :code:`nwbinspector --help`.
120+
::
121+
122+
nwbinspector path/to/my/data.nwb -m my_extension_module1 my_extension_module2

nwbinspector/checks/nwbfile_metadata.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pynwb.file import Subject
77

88
from ..register_checks import register_check, InspectorMessage, Importance
9+
from ..utils import is_module_installed
910

1011
duration_regex = (
1112
r"^P(?!$)(\d+(?:\.\d+)?Y)?(\d+(?:\.\d+)?M)?(\d+(?:\.\d+)?W)?(\d+(?:\.\d+)?D)?(T(?=\d)(\d+(?:\.\d+)?H)?(\d+(?:\.\d+)"
@@ -38,12 +39,31 @@ def check_session_start_time_future_date(nwbfile: NWBFile):
3839

3940

4041
@register_check(importance=Importance.BEST_PRACTICE_SUGGESTION, neurodata_type=NWBFile)
41-
def check_experimenter(nwbfile: NWBFile):
42+
def check_experimenter_exists(nwbfile: NWBFile):
4243
"""Check if an experimenter has been added for the session."""
4344
if not nwbfile.experimenter:
4445
return InspectorMessage(message="Experimenter is missing.")
4546

4647

48+
@register_check(importance=Importance.BEST_PRACTICE_SUGGESTION, neurodata_type=NWBFile)
49+
def check_experimenter_form(nwbfile: NWBFile):
50+
"""Check the text form of each experimenter to see if it matches the DANDI regex pattern."""
51+
if is_module_installed(module_name="dandi"):
52+
from dandischema.models import NAME_PATTERN # for most up to date version of the regex
53+
else:
54+
NAME_PATTERN = r"^([\w\s\-\.']+),\s+([\w\s\-\.']+)$" # copied on 7/12/22
55+
56+
for experimenter in nwbfile.experimenter:
57+
experimenter = experimenter.decode() if isinstance(experimenter, bytes) else experimenter
58+
if re.match(string=experimenter, pattern=NAME_PATTERN) is None:
59+
yield InspectorMessage(
60+
message=(
61+
f"The name of experimenter '{experimenter}' does not match the DANDI form "
62+
"(Last, First Middle or Last, First M.)."
63+
)
64+
)
65+
66+
4767
@register_check(importance=Importance.BEST_PRACTICE_SUGGESTION, neurodata_type=NWBFile)
4868
def check_experiment_description(nwbfile: NWBFile):
4969
"""Check if a description has been added for the session."""

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
long_description = f.read()
66
setup(
77
name="nwbinspector",
8-
version="0.4.7",
8+
version="0.4.9",
99
description="Tool to inspect NWB files for best practices compliance.",
1010
long_description=long_description,
1111
long_description_content_type="text/markdown",

tests/unit_tests/test_nwbfile_metadata.py

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
from nwbinspector import (
88
InspectorMessage,
99
Importance,
10-
check_experimenter,
10+
check_experimenter_exists,
11+
check_experimenter_form,
1112
check_experiment_description,
1213
check_institution,
1314
check_keywords,
@@ -70,37 +71,79 @@ def test_check_session_start_time_future_date_fail():
7071
)
7172

7273

73-
def test_check_experimenter_pass():
74+
def test_check_experimenter_exists_pass():
7475
nwbfile = NWBFile(
7576
session_description="",
7677
identifier=str(uuid4()),
7778
session_start_time=datetime.now().astimezone(),
7879
experimenter=["Last, First"],
7980
)
80-
assert check_experimenter(nwbfile) is None
81+
assert check_experimenter_exists(nwbfile) is None
8182

8283

83-
def test_check_experimenter_bytestring_pass():
84+
def test_check_experimenter_exists_bytestring_pass():
8485
nwbfile = NWBFile(
8586
session_description="",
8687
identifier=str(uuid4()),
8788
session_start_time=datetime.now().astimezone(),
8889
experimenter=[b"Last, First"],
8990
)
90-
assert check_experimenter(nwbfile) is None
91+
assert check_experimenter_exists(nwbfile) is None
9192

9293

93-
def test_check_experimenter_fail():
94-
assert check_experimenter(minimal_nwbfile) == InspectorMessage(
94+
def test_check_experimenter_exists_fail():
95+
assert check_experimenter_exists(minimal_nwbfile) == InspectorMessage(
9596
message="Experimenter is missing.",
9697
importance=Importance.BEST_PRACTICE_SUGGESTION,
97-
check_function_name="check_experimenter",
98+
check_function_name="check_experimenter_exists",
9899
object_type="NWBFile",
99100
object_name="root",
100101
location="/",
101102
)
102103

103104

105+
def test_check_experimenter_form_pass():
106+
nwbfile = NWBFile(
107+
session_description="",
108+
identifier=str(uuid4()),
109+
session_start_time=datetime.now().astimezone(),
110+
experimenter=["Last, First"],
111+
)
112+
assert check_experimenter_form(nwbfile=nwbfile) is None
113+
114+
115+
def test_check_experimenter_form_bytestring_pass():
116+
nwbfile = NWBFile(
117+
session_description="",
118+
identifier=str(uuid4()),
119+
session_start_time=datetime.now().astimezone(),
120+
experimenter=[b"Last, First"],
121+
)
122+
assert check_experimenter_form(nwbfile=nwbfile) is None
123+
124+
125+
def test_check_experimenter_form_fail():
126+
nwbfile = NWBFile(
127+
session_description="",
128+
identifier=str(uuid4()),
129+
session_start_time=datetime.now().astimezone(),
130+
experimenter=["First Middle Last"],
131+
)
132+
assert check_experimenter_form(nwbfile=nwbfile) == [
133+
InspectorMessage(
134+
message=(
135+
"The name of experimenter 'First Middle Last' does not match the DANDI form "
136+
"(Last, First Middle or Last, First M.)."
137+
),
138+
importance=Importance.BEST_PRACTICE_SUGGESTION,
139+
check_function_name="check_experimenter_form",
140+
object_type="NWBFile",
141+
object_name="root",
142+
location="/",
143+
)
144+
]
145+
146+
104147
def test_check_experiment_description():
105148
assert check_experiment_description(minimal_nwbfile) == InspectorMessage(
106149
message="Experiment description is missing.",

0 commit comments

Comments
 (0)