Skip to content
46 changes: 43 additions & 3 deletions dandi/files/bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@

from .bases import GenericAsset, LocalFileAsset, NWBAsset
from .zarr import ZarrAsset
from ..consts import ZARR_MIME_TYPE
from ..consts import ZARR_MIME_TYPE, dandiset_metadata_file
from ..metadata.core import add_common_metadata, prepare_metadata
from ..misctypes import Digest
from ..validate_types import ValidationResult
from ..validate_types import (
ORIGIN_VALIDATION_DANDI_LAYOUT,
Scope,
Severity,
ValidationResult,
)

BIDS_ASSET_ERRORS = ("BIDS.NON_BIDS_PATH_PLACEHOLDER",)
BIDS_DATASET_ERRORS = ("BIDS.MANDATORY_FILE_MISSING_PLACEHOLDER",)
Expand Down Expand Up @@ -112,7 +117,42 @@ def _validate(self) -> None:

# Obtain BIDS validation results of the entire dataset through the
# deno-compiled BIDS validator
self._dataset_errors = bids_validate(self.bids_root)
v_results = bids_validate(self.bids_root)

# Validation results from the deno BIDS validator with an additional
# hint, represented as a `ValidationResult` object, following
# each `dandiset.yaml` error, suggesting to add the `dandiset.yaml` file
# to `.bidsignore`.
v_results_extended: list[ValidationResult] = []

for result in v_results:
v_results_extended.append(result)
if (
result.path is not None
and result.dataset_path is not None
and result.path.relative_to(result.dataset_path).as_posix()
== dandiset_metadata_file
):
hint = ValidationResult(
id="DANDI.BIDSIGNORE_DANDISET_YAML",
origin=ORIGIN_VALIDATION_DANDI_LAYOUT,
scope=Scope.DATASET,
origin_result=result,
severity=Severity.HINT,
dandiset_path=result.dandiset_path,
dataset_path=result.dataset_path,
path=result.path,
message=(
f"Consider creating or updating a `.bidsignore` file "
f"in the root of your BIDS dataset to ignore "
f"`{dandiset_metadata_file}`. "
f"Add the following line to `.bidsignore`:\n"
f"{dandiset_metadata_file}"
),
)
v_results_extended.append(hint)

self._dataset_errors = v_results_extended

# Categorized validation results related to individual assets by the
# path of the asset in the BIDS dataset
Expand Down
25 changes: 22 additions & 3 deletions dandi/tests/test_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,30 @@ def mock_bids_validate(*args: Any, **kwargs: Any) -> list[ValidationResult]:
assert err.dataset_path is not None
assert err.path.relative_to(err.dataset_path).as_posix() == dandiset_metadata_file

# The error message should be the original BIDS error, not modified
assert err.message is not None
assert err.message.startswith(
f"The dandiset metadata file, `{dandiset_metadata_file}`, is not a part of "
f"BIDS specification."
# We just check that it's about dandiset.yaml, not checking exact message
# since it comes from BIDS validator

# Check that there is also a HINT about .bidsignore
validation_hints = [
r
for r in validation_results
if r.severity is not None and r.severity == Severity.HINT
]

# Assert that there is at least one hint
assert len(validation_hints) >= 1

# Find the hint about .bidsignore for dandiset.yaml
bidsignore_hint = next(
(h for h in validation_hints if h.id == "DANDI.BIDSIGNORE_DANDISET_YAML"),
None,
)
assert bidsignore_hint is not None
assert bidsignore_hint.message is not None
assert ".bidsignore" in bidsignore_hint.message
assert dandiset_metadata_file in bidsignore_hint.message


def test_validate_bids_onefile(bids_error_examples: Path, tmp_path: Path) -> None:
Expand Down
16 changes: 0 additions & 16 deletions dandi/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,22 +186,6 @@ def validate(
):
r_id = id(r)
if r_id not in df_result_ids:
# If the error is about the dandiset metadata file, modify
# the message in the validation to give the context of DANDI
if (
r.path is not None
and r.dataset_path is not None
and r.path.relative_to(r.dataset_path).as_posix()
== dandiset_metadata_file
):
r.message = (
f"The dandiset metadata file, `{dandiset_metadata_file}`, "
f"is not a part of BIDS specification. Please include a "
f"`.bidsignore` file with specification to ignore the "
f"metadata file in your dataset. For more details, see "
f"https://github.com/bids-standard/bids-specification/"
f"issues/131#issuecomment-461060166."
)
df_results.append(r)
df_result_ids.add(r_id)
yield r