@@ -619,7 +619,7 @@ def get_key_info_for_fmap_assignment(json_file, matching_parameter='ImagingVolum
619
619
modality = op .basename (op .dirname (json_file ))
620
620
if modality == 'fmap' :
621
621
# extract the <acq> entity:
622
- acq_label = re . search ( '(?<=[/_]acq-)\w+' , json_file ). group ( 0 ). split ( '_' )[ 0 ]
622
+ acq_label = BIDSFile . parse ( op . basename ( json_file ))[ 'acq' ]
623
623
if any (s in acq_label .lower () for s in ['fmri' , 'bold' , 'func' ]):
624
624
key_info = ['func' ]
625
625
elif any (s in acq_label .lower () for s in ['diff' , 'dwi' ]):
@@ -915,3 +915,94 @@ def populate_intended_for(path_to_bids_session, matching_parameters='ImagingVolu
915
915
# Add this intended_for to all fmap files in the fmap_group:
916
916
for fm_json in unique_fmap_groups [fmap_group ]:
917
917
update_json (fm_json , {"IntendedFor" : intended_for }, pretty = True )
918
+
919
+
920
+ class BIDSFile (object ):
921
+ """ as defined in https://bids-specification.readthedocs.io/en/stable/99-appendices/04-entity-table.html
922
+ which might soon become machine readable
923
+ order matters
924
+ """
925
+
926
+ _known_entities = ['sub' , 'ses' , 'task' , 'acq' , 'ce' , 'rec' , 'dir' , 'run' , 'mod' ,
927
+ 'echo' , 'flip' , 'inv' , 'mt' , 'part' , 'recording' ,
928
+ ]
929
+
930
+ def __init__ (self , entities , suffix , extension ):
931
+ self ._entities = entities
932
+ self ._suffix = suffix
933
+ self ._extension = extension
934
+
935
+ def __eq__ (self , other ):
936
+ if not isinstance (other , self .__class__ ):
937
+ return False
938
+ if (
939
+ all ([other [k ] == v for k , v in self ._entities .items ()])
940
+ and self .extension == other .extension
941
+ and self .suffix == other .suffix
942
+ ):
943
+ return True
944
+ else :
945
+ return False
946
+
947
+ @classmethod
948
+ def parse (cls , filename ):
949
+ """ Parse the filename for BIDS entities, suffix and extension """
950
+ # use re.findall to find all lower-case-letters + '-' + alphanumeric + '_' pairs:
951
+ entities_list = re .findall ('([a-z]+)-([a-zA-Z0-9]+)[_]*' , filename )
952
+ # keep only those in the _known_entities list:
953
+ entities = {k : v for k , v in entities_list if k in BIDSFile ._known_entities }
954
+ # get whatever comes after the last key-value pair, and remove any '_' that
955
+ # might come in front:
956
+ ending = filename .split ('-' .join (entities_list [- 1 ]))[- 1 ]
957
+ ending = remove_prefix (ending , '_' )
958
+ # the first dot ('.') separates the suffix from the extension:
959
+ if '.' in ending :
960
+ suffix , extension = ending .split ('.' , 1 )
961
+ else :
962
+ suffix , extension = ending , None
963
+ return BIDSFile (entities , suffix , extension )
964
+
965
+ def __str__ (self ):
966
+ """ reconstitute in a legit BIDS filename using the order from entity table """
967
+ if 'sub' not in self ._entities :
968
+ raise ValueError ('The \' sub-\' entity is mandatory' )
969
+ # reconstitute the ending for the filename:
970
+ suffix = '_' + self .suffix if self .suffix else ''
971
+ extension = '.' + self .extension if self .extension else ''
972
+ return '_' .join (
973
+ ['-' .join ([e , self ._entities [e ]]) for e in self ._known_entities if e in self ._entities ]
974
+ ) + suffix + extension
975
+
976
+ def __getitem__ (self , entity ):
977
+ return self ._entities [entity ] if entity in self ._entities else None
978
+
979
+ def __setitem__ (self , entity , value ): # would puke with some exception if already known
980
+ return self .set (entity , value , overwrite = False )
981
+
982
+ def set (self , entity , value , overwrite = True ):
983
+ if entity not in self ._entities :
984
+ # just set it; no complains here
985
+ self ._entities [entity ] = value
986
+ elif overwrite :
987
+ lgr .warning ("Overwriting the entity %s from %s to %s for file %s" ,
988
+ str (entity ),
989
+ str (self [entity ]),
990
+ str (value ),
991
+ self .__str__ ()
992
+ )
993
+ self ._entities [entity ] = value
994
+ else :
995
+ # if it already exists, and overwrite is false:
996
+ lgr .warning ("Setting the entity %s to %s for file %s failed" ,
997
+ str (entity ),
998
+ str (value ),
999
+ self .__str__ ()
1000
+ )
1001
+
1002
+ @property # as needed make them RW
1003
+ def suffix (self ):
1004
+ return self ._suffix
1005
+
1006
+ @property
1007
+ def extension (self ):
1008
+ return self ._extension
0 commit comments