diff --git a/src/forbids/cli/init.py b/src/forbids/cli/init.py index e2f59b2..b27e9a8 100644 --- a/src/forbids/cli/init.py +++ b/src/forbids/cli/init.py @@ -48,6 +48,7 @@ def initialize( all_datatypes = bids_layout.get_datatype() + # group by instrument tags across subject, (session) and runs excl_ents = ["subject", "run"] + (["session"] if uniform_sessions else []) for datatype in all_datatypes: @@ -127,18 +128,17 @@ def generate_series_model( ) except ValidationError as error: lgr.warning(f"failed to group with {instrument_query_tags}") - lgr.info(error.absolute_path) lgr.warning( f"{error.__class__.__name__} " f"{'.'.join([str(e) for e in error.absolute_path])} : " f"{error.message} found {error.instance if 'required' not in error.message else ''}" ) - continue + continue # move on to next instrument grouping - # one grouping scheme worked ! - non_null_entities["subject"] = "ref" + # one instrument grouping scheme worked! # generate paths and folder + non_null_entities["subject"] = "ref" schema_path = bids_layout.build_path(non_null_entities, absolute_paths=False) schema_path_abs = os.path.join(bids_layout.root, schema.FORBIDS_SCHEMA_FOLDER, schema_path) os.makedirs(os.path.dirname(schema_path_abs), exist_ok=True) @@ -158,3 +158,5 @@ def generate_series_model( lgr.info(f"Successfully generated schema with grouping {instrument_query_tags}") break + else: + raise RuntimeError("failed to generate") diff --git a/src/forbids/cli/run.py b/src/forbids/cli/run.py index 809ff9f..437e2df 100644 --- a/src/forbids/cli/run.py +++ b/src/forbids/cli/run.py @@ -73,15 +73,11 @@ def main() -> None: no_error = True for error in validate(layout, subject=args.participant_label, session=args.session_label): no_error = False - if not isinstance(error, BIDSFileError): - lgr.error(error) - - else: - lgr.error( - f"{error.__class__.__name__} " - f"{'.'.join(error.absolute_path)} : " - f"{error.message} found {error.instance if 'required' not in error.message else ''}" - ) + lgr.error( + f"{error.__class__.__name__} " + f"{'.'.join([str(pp) for pp in error.absolute_path])} : " + f"{error.message} found {error.instance if 'required' not in error.message else ''}" + ) exit(0 if no_error else 1) diff --git a/src/forbids/cli/validation.py b/src/forbids/cli/validation.py index d064d5c..783745b 100644 --- a/src/forbids/cli/validation.py +++ b/src/forbids/cli/validation.py @@ -52,39 +52,46 @@ def validate(bids_layout: bids.BIDSLayout, **entities: dict[str, str | list]): ) for ref_sidecar in ref_sidecars: - lgr.info(f"validating {ref_sidecar.relpath}") + lgr.info(f"validating {ref_sidecar.entities}") # load the schema sidecar_schema = ref_sidecar.get_dict() bidsfile_constraints = sidecar_schema.pop("bids", dict()) query_entities = ref_sidecar.entities.copy() + for entity in schema.ALT_ENTITIES: + if entity not in query_entities: + query_entities[entity] = bids.layout.Query.NONE validator = schema.get_validator(sidecar_schema) for subject in subjects: query_entities["subject"] = subject - sessions = bids_layout.get_session(subject=subject, session=entities["session"]) + sessions = bids_layout.get_session(subject=subject, session=entities["session"]) or [bids.layout.Query.NONE] for session in sessions: + lgr.info(f"validating sub-{subject} {'ses-'+ session if isinstance(session, str) else ''}") query_entities["session"] = session + non_null_entities = {k: v for k, v in query_entities.items() if not isinstance(v, bids.layout.Query)} + expected_sidecar = bids_layout.build_path(non_null_entities, absolute_paths=False) lgr.debug(query_entities) sidecars_to_validate = bids_layout.get(**query_entities) if not sidecars_to_validate and not bidsfile_constraints.get("optional", False): - yield BIDSFileError(f"{ref_sidecar.relpath} found no match") + yield BIDSFileError(f"{expected_sidecar}", instance="no match") continue # no point going further num_sidecars = len(sidecars_to_validate) min_runs = bidsfile_constraints.get("min_runs", 0) max_runs = bidsfile_constraints.get("max_runs", 1e10) + if num_sidecars < min_runs: yield BIDSFileError( - f"Expected at least {min_runs} runs for {ref_sidecar.relpath}, found {num_sidecars}" + f"Expected at least {min_runs} runs for {expected_sidecar}, found {num_sidecars}" ) elif num_sidecars > max_runs: yield BIDSFileError( - f"Expected at most {max_runs} runs for {ref_sidecar.relpath}, found {num_sidecars}" + f"Expected at most {max_runs} runs for {expected_sidecar}, found {num_sidecars}" ) for sidecar in sidecars_to_validate: @@ -92,9 +99,9 @@ def validate(bids_layout: bids.BIDSLayout, **entities: dict[str, str | list]): all_sidecars.remove(sidecar) else: lgr.error("an error occurred") - lgr.info(f"validating {sidecar.path}") + lgr.debug(f"validating {sidecar.path}") sidecar_data = schema.prepare_metadata(sidecar, bidsfile_constraints["instrument_tags"]) yield from validator.iter_errors(sidecar_data) for extra_sidecar in all_sidecars: relpath = extra_sidecar.path - yield BIDSFileError(f"Unexpected BIDS file{extra_sidecar.relpath}") + yield BIDSFileError(f"Unexpected BIDS file {extra_sidecar.relpath}")