@@ -24,7 +24,150 @@ class BIDSFile:
24
24
_known_entities = _load_entities_order ()
25
25
26
26
27
+
28
+ def __init__ (self , entities , suffix , extension ):
29
+ self ._entities = entities
30
+ self ._suffix = suffix
31
+ self ._extension = extension
32
+
33
+ def __eq__ (self , other ):
34
+ if not isinstance (other , self .__class__ ):
35
+ return False
36
+ if (
37
+ all ([other [k ] == v for k , v in self ._entities .items ()])
38
+ and self .extension == other .extension
39
+ and self .suffix == other .suffix
40
+ ):
41
+ return True
42
+ else :
43
+ return False
44
+
45
+ @classmethod
46
+ def parse (cls , filename ):
47
+ """ Parse the filename for BIDS entities, suffix and extension """
48
+ # use re.findall to find all lower-case-letters + '-' + alphanumeric + '_' pairs:
49
+ entities_list = re .findall ('([a-z]+)-([a-zA-Z0-9]+)[_]*' , filename )
50
+ # keep only those in the _known_entities list:
51
+ entities = {k : v for k , v in entities_list if k in BIDSFile ._known_entities }
52
+ # get whatever comes after the last key-value pair, and remove any '_' that
53
+ # might come in front:
54
+ ending = filename .split ('-' .join (entities_list [- 1 ]))[- 1 ]
55
+ ending = remove_prefix (ending , '_' )
56
+ # the first dot ('.') separates the suffix from the extension:
57
+ if '.' in ending :
58
+ suffix , extension = ending .split ('.' , 1 )
59
+ else :
60
+ suffix , extension = ending , None
61
+ return BIDSFile (entities , suffix , extension )
62
+
63
+ def __str__ (self ):
64
+ """ reconstitute in a legit BIDS filename using the order from entity table """
65
+ if 'sub' not in self ._entities :
66
+ raise ValueError ('The \' sub-\' entity is mandatory' )
67
+ # reconstitute the ending for the filename:
68
+ suffix = '_' + self .suffix if self .suffix else ''
69
+ extension = '.' + self .extension if self .extension else ''
70
+ return '_' .join (
71
+ ['-' .join ([e , self ._entities [e ]]) for e in self ._known_entities if e in self ._entities ]
72
+ ) + suffix + extension
73
+
74
+ def __getitem__ (self , entity ):
75
+ return self ._entities [entity ] if entity in self ._entities else None
76
+
77
+ def __setitem__ (self , entity , value ): # would puke with some exception if already known
78
+ return self .set (entity , value , overwrite = False )
79
+
80
+ def set (self , entity , value , overwrite = True ):
81
+ if entity not in self ._entities :
82
+ # just set it; no complains here
83
+ self ._entities [entity ] = value
84
+ elif overwrite :
85
+ lgr .warning ("Overwriting the entity %s from %s to %s for file %s" ,
86
+ str (entity ),
87
+ str (self [entity ]),
88
+ str (value ),
89
+ self .__str__ ()
90
+ )
91
+ self ._entities [entity ] = value
92
+ else :
93
+ # if it already exists, and overwrite is false:
94
+ lgr .warning ("Setting the entity %s to %s for file %s failed" ,
95
+ str (entity ),
96
+ str (value ),
97
+ self .__str__ ()
98
+ )
99
+
100
+ @property # as needed make them RW
101
+ def suffix (self ):
102
+ return self ._suffix
103
+
104
+ @property
105
+ def extension (self ):
106
+ return self ._extension
107
+
27
108
# TEMP: just for now, could be moved/removed
28
109
def test_BIDSFile ():
110
+ """ Tests for the BIDSFile class """
111
+
112
+ # define entities in the correct order:
113
+ sorted_entities = [
114
+ ('sub' , 'Jason' ),
115
+ ('acq' , 'Treadstone' ),
116
+ ('run' , '2' ),
117
+ ('echo' , '1' ),
118
+ ]
119
+ # 'sub-Jason_acq-Treadstone_run-2_echo-1':
120
+ expected_sorted_str = '_' .join (['-' .join (e ) for e in sorted_entities ])
121
+ # expected BIDSFile:
122
+ suffix = 'T1w'
123
+ extension = 'nii.gz'
124
+ expected_bids_file = BIDSFile (OrderedDict (sorted_entities ), suffix , extension )
125
+
126
+ # entities in random order:
127
+ idcs = list (range (len (sorted_entities )))
128
+ shuffle (idcs )
129
+ shuffled_entities = [sorted_entities [i ] for i in idcs ]
130
+ shuffled_str = '_' .join (['-' .join (e ) for e in shuffled_entities ])
131
+
132
+ # Test __eq__ method.
133
+ # It should consider equal BIDSFiles with the same entities even in different order:
134
+ assert BIDSFile (OrderedDict (shuffled_entities ), suffix , extension ) == expected_bids_file
135
+
136
+ # Test __getitem__:
137
+ assert all ([expected_bids_file [k ] == v for k , v in shuffled_entities ])
138
+
139
+ # Test filename parser and __str__ method:
140
+ # Note: the __str__ method should return entities in the correct order
141
+ ending = '_T1w.nii.gz' # suffix + extension
142
+ my_bids_file = BIDSFile .parse (shuffled_str + ending )
143
+ assert my_bids_file == expected_bids_file
144
+ assert str (my_bids_file ) == expected_sorted_str + ending
145
+
146
+ ending = '.json' # just extension
147
+ my_bids_file = BIDSFile .parse (shuffled_str + ending )
148
+ assert my_bids_file .suffix == ''
149
+ assert str (my_bids_file ) == expected_sorted_str + ending
150
+
151
+ ending = '_T1w' # just suffix
152
+ my_bids_file = BIDSFile .parse (shuffled_str + ending )
153
+ assert my_bids_file .extension is None
154
+ assert str (my_bids_file ) == expected_sorted_str + ending
155
+
156
+ # Complain if entity 'sub' is not set:
157
+ with pytest .raises (ValueError ) as e_info :
158
+ assert str (BIDSFile .parse ('dir-reversed.json' ))
159
+ assert 'sub-' in e_info .value
160
+
161
+ # Test set method:
162
+ # -for a new entity, just set it without a complaint:
163
+ my_bids_file ['dir' ] = 'AP'
164
+ assert my_bids_file ['dir' ] == 'AP'
165
+ # -for an existing entity, don't change it by default:
166
+ my_bids_file ['echo' ] = '2'
167
+ assert my_bids_file ['echo' ] == expected_bids_file ['echo' ] # still the original value
168
+ # -for an existing entity, you can overwrite it with "set":
169
+ my_bids_file .set ('echo' , '2' )
170
+ assert my_bids_file ['echo' ] == '2'
171
+
29
172
assert BIDSFile ._known_entities [:2 ] == ['sub' , 'ses' ]
30
173
print (BIDSFile ._known_entities )
0 commit comments