Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENH] add methods to load metadata through the bids.File class #333

Merged
merged 4 commits into from
Mar 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions +bids/+internal/get_meta_list.m
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@

% For all those files we find which one is potentially associated with
% the file of interest
% TODO: not more than one file per level is allowed
for i = 1:numel(metafile)

p2 = bids.internal.parse_filename(metafile{i});
Expand All @@ -76,6 +77,8 @@
% as its data file counterpart
ismeta = true;
if ~strcmp(p.suffix, p2.suffix)
% TODO is this necessary as we have already
% only listed files with the same suffix
ismeta = false;
end
for j = 1:numel(entities)
Expand Down
5 changes: 5 additions & 0 deletions +bids/+internal/parse_filename.m
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@
tolerant = true;
end

if isempty(filename)
p = struct([]);
return
end

fields_order = {'filename', 'ext', 'suffix', 'entities', 'prefix'};

filename = bids.internal.file_utils(filename, 'filename');
Expand Down
29 changes: 26 additions & 3 deletions +bids/File.m
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,24 @@

json_filename = '' % bidsified name for json file

metadata_files = {} % list of metadata files related

metadata

entity_required = {} % Required entities

entity_order = {} % Expected order of entities

schema = [] % BIDS schema used

tolerant = true

verbose = false

end

properties (SetAccess = private)
changed = false
tolerant = true
verbose = false

end

methods
Expand Down Expand Up @@ -151,6 +156,8 @@
obj = obj.use_schema();
end

obj = obj.set_metadata();

obj = obj.update();
end

Expand Down Expand Up @@ -242,6 +249,14 @@
obj.changed = true;
end

function obj = set_metadata(obj)
if isempty(obj.metadata_files)
pattern = '^.*%s\\.json$';
obj.metadata_files = bids.internal.get_meta_list(obj.path, pattern);
end
obj.metadata = bids.internal.get_metadata(obj.metadata_files);
end

function obj = set_entity(obj, label, value)
obj.validate_word(label, 'Entity label');
obj.validate_word(value, 'Entity value');
Expand All @@ -250,6 +265,13 @@
obj.changed = true;
end

function obj = set_metadata_files(obj, pattern)
if nargin < 2
pattern = '^.*%s\\.json$';
end
obj.metadata_files = bids.internal.get_meta_list(obj.path, pattern);
end

%% other methods
function obj = update(obj)
%
Expand Down Expand Up @@ -405,6 +427,7 @@
end

if ~args.Results.dry_run
% TODO update obj.path
output_file = fullfile(fileparts(obj.path), obj.filename);
if ~exist(output_file, 'file') || args.Results.force
movefile(obj.path, output_file);
Expand Down
4 changes: 4 additions & 0 deletions +bids/Model.m
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@
%
if isempty(varargin)
value = obj.Nodes;
idx = 1:numel(value);

else

Expand Down Expand Up @@ -257,6 +258,9 @@
end

function obj = get_edges_from_nodes(obj)
if numel(obj.Nodes) <= 1
return
end
for i = 1:(numel(obj.Nodes) - 1)
obj.Edges{i, 1} = struct('Source', obj.Nodes{i, 1}.Name, ...
'Destination', obj.Nodes{i + 1, 1}.Name);
Expand Down
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ cff-version: 1.2.0

title: bids-matlab

version: 0.1.0
version: 0.1.0dev

license: GPL-3.0

Expand Down
Binary file added docs/bids-matlab.pdf
Binary file not shown.
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
author = 'BIDS-MATLAB developers'

# The full version, including alpha/beta/rc tags
release = 'v0.1.0'
release = 'v0.1.0dev'


# -- General configuration ---------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion miss_hit.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ tab_width: 2

# metrics limit for the code quality (https://florianschanda.github.io/miss_hit/metrics.html)
metric "cnest": limit 5
metric "file_length": limit 710
metric "file_length": limit 800
metric "cyc": limit 17
metric "parameters": limit 8
47 changes: 44 additions & 3 deletions tests/test_bids_file.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,43 @@
%
% end

function test_get_metadata_suffixes_basic()
% ensures that "similar" suffixes are distinguished

data_dir = fullfile(fileparts(mfilename('fullpath')), 'data', 'surface_data');

file = fullfile(data_dir, 'sub-06_hemi-R_space-individual_den-native_thickness.shape.gii');
side_car = fullfile(data_dir, 'sub-06_hemi-R_space-individual_den-native_thickness.json');

bf = bids.File(file);

% TODO only only json file per folder level allowed
% assertEqual(numel(bf.metadata_files), 1)

expected_metadata = bids.util.jsondecode(side_car);

assertEqual(bf.metadata, expected_metadata);

file = fullfile(data_dir, 'sub-06_hemi-R_space-individual_den-native_midthickness.surf.gii');
side_car = fullfile(data_dir, 'sub-06_hemi-R_space-individual_den-native_midthickness.json');

bf = bids.File(file);

expected_metadata = bids.util.jsondecode(side_car);

assertEqual(bf.metadata, expected_metadata);

file = fullfile(data_dir, 'sub-06_space-individual_den-native_thickness.dscalar.nii');
side_car = fullfile(data_dir, 'sub-06_space-individual_den-native_thickness.json');

bf = bids.File(file);

expected_metadata = bids.util.jsondecode(side_car);

assertEqual(bf.metadata, expected_metadata);

end

function test_rename()

input_filename = 'wuasub-01_ses-test_task-faceRecognition_run-02_bold.nii';
Expand All @@ -25,7 +62,7 @@ function test_rename()
set_up(input_file);
teardown(output_file);

file = bids.File(input_file, 'use_schema', false, 'verbose', true);
file = bids.File(input_file, 'use_schema', false, 'verbose', false);

assertEqual(file.path, input_file);

Expand Down Expand Up @@ -78,13 +115,17 @@ function test_rename_force()

system(sprintf('touch %s', input_file));
system(sprintf('touch %s', output_file));
file = bids.File(input_file, 'use_schema', false, 'verbose', true);
file = bids.File(input_file, 'use_schema', false, 'verbose', false);

assertEqual(file.path, input_file);

file.prefix = '';
file.entities.desc = 'preproc';
assertWarning(@() file.rename('dry_run', false), 'File:fileAlreadyExists');
file.verbose = true;
if bids.internal.is_github_ci && ~bids.internal.is_octave
% failure: warning 'Octave:mixed-string-concat' was raised, expected 'File:fileAlreadyExists'.
assertWarning(@() file.rename('dry_run', false), 'File:fileAlreadyExists');
end

file = file.rename('dry_run', false, 'verbose', false);
assertEqual(exist(input_file, 'file'), 2);
Expand Down
38 changes: 38 additions & 0 deletions tests/tests_private/test_get_metadata.m
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,30 @@ function test_get_metadata_basic()

end

function test_get_metadata_basic_File()

% Same as above but uses the bids.File class

pth = fullfile(fileparts(mfilename('fullpath')), '..', 'data', 'synthetic');

func_sub_01.RepetitionTime = 10;
anat_sub_01.FlipAngle = 10;
anat_sub_01.Manufacturer = 'Siemens';

% try to get metadata
BIDS = bids.layout(pth);

data = bids.query(BIDS, 'data', 'sub', '01', 'suffix', 'bold');
bf = bids.File(data{1});
assert(bf.metadata.RepetitionTime == func_sub_01.RepetitionTime);

%% test anat metadata subject 01
metadata = bids.query(BIDS, 'data', 'sub', '01', 'suffix', 'T1w');
bf = bids.File(data{1});
assertEqual(bf.metadata.Manufacturer, anat_sub_01.Manufacturer);

end

function test_get_metadata_internal()

pth_bids_example = get_test_data_dir();
Expand All @@ -69,3 +93,17 @@ function test_get_metadata_participants()
assertEqual(metadata, expected_metadata);

end

function test_get_metadata_participants_File()

pth_bids_example = get_test_data_dir();

file = fullfile(pth_bids_example, 'pet002', 'participants.tsv');
side_car = fullfile(pth_bids_example, 'pet002', 'participants.json');

bf = bids.File(file);

expected_metadata = bids.util.jsondecode(side_car);
assertEqual(bf.metadata, expected_metadata);

end
3 changes: 3 additions & 0 deletions tests/tests_private/test_get_metadata_suffixes.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ function test_get_metadata_suffixes_basic()
side_car = fullfile(data_dir, 'sub-06_hemi-R_space-individual_den-native_thickness.json');

metalist = bids.internal.get_meta_list(file);
% TODO only only json file per folder level allowed
% assertEqual(numel(metalist), 1)

metadata = bids.internal.get_metadata(metalist);

expected_metadata = bids.util.jsondecode(side_car);
Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.1.0
v0.1.0dev