42
42
from nidm .experiment import Project ,Session ,Acquisition ,AcquisitionObject ,DemographicsObject ,AssessmentObject , MRObject
43
43
from nidm .core import BIDS_Constants ,Constants
44
44
from prov .model import PROV_LABEL ,PROV_TYPE
45
- from nidm .experiment .Utils import read_nidm
45
+ from nidm .experiment .Utils import read_nidm , write_json_mapping_file
46
46
from nidm .experiment .Query import GetProjectsUUID , GetProjectLocation , GetParticipantIDFromAcquisition
47
+ from nidm .core .Constants import DD
47
48
48
49
import json
49
50
from pprint import pprint
@@ -152,6 +153,122 @@ def GetImageFromURL(url):
152
153
print ("ERROR! Can't open url: %s" % url )
153
154
return - 1
154
155
156
+ def GetDataElementMetadata (nidm_graph ,de_uuid ):
157
+ '''
158
+ This function will query the nidm_graph for the DataElement de_uuid and return all the metadata as a BIDS-compliant
159
+ participants sidecar file dictionary
160
+ '''
161
+
162
+ # query nidm_graph for Constants.NIIRI[de_uuid] rdf:type PersonalDataElement
163
+ query = """
164
+ PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
165
+ PREFIX prov: <http://www.w3.org/ns/prov#>
166
+ PREFIX niiri: <http://iri.nidash.org/>
167
+ PREFIX nidm: <http://purl.org/nidash/nidm#>
168
+
169
+ select distinct ?p ?o
170
+ where {
171
+
172
+ <%s> rdf:type nidm:PersonalDataElement ;
173
+ ?p ?o .
174
+ }
175
+ """ % Constants .NIIRI [de_uuid ]
176
+
177
+ # print(query)
178
+ qres = nidm_graph .query (query )
179
+
180
+ # set up a dictionary entry for this column
181
+ #current_tuple = str(DD(source="participants.tsv", variable=column))
182
+
183
+ # temporary dictionary of metadata
184
+ temp_dict = {}
185
+ # add info to BIDS-formatted json sidecar file
186
+ for row in qres :
187
+ temp_dict [str (row [0 ])] = str (row [1 ])
188
+
189
+ # set up a dictionary entry for this column
190
+ current_tuple = str (DD (source = "participants.tsv" , variable =
191
+ temp_dict ['http://purl.org/nidash/nidm#sourceVariable' ]))
192
+
193
+ de = {}
194
+ de [current_tuple ] = {}
195
+ # now look for label entry in temp_dict and set up a proper NIDM-style JSON data structure
196
+ # see Utils.py function map_variables_to_terms for example (column_to_terms[current_tuple])
197
+ for key ,value in temp_dict .items ():
198
+ if key == 'http://purl.org/nidash/nidm#sourceVariable' :
199
+ de [current_tuple ]['source_variable' ] = value
200
+ elif key == 'http://purl.org/dc/terms/description' :
201
+ de [current_tuple ]['description' ] = value
202
+ elif key == 'http://purl.org/nidash/nidm#isAbout' :
203
+ # here we need to do an additional query to see if there's a label associated with the isAbout value
204
+ de [current_tuple ]['isAbout' ] = []
205
+
206
+ # check whether there are multiple 'isAbout' entries
207
+ if type (value ) == 'list' :
208
+ # if this is a list we have to loop through the entries and store the url and labels
209
+ for entry in value :
210
+ # query for label for this isAbout URL
211
+ query = '''
212
+
213
+ prefix prov: <http://www.w3.org/ns/prov#>
214
+ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
215
+ prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
216
+
217
+ select distinct ?label
218
+ where {
219
+ <%s> rdf:type prov:Entity ;
220
+ rdfs:label ?label .
221
+ }
222
+ ''' % entry
223
+ #print(query)
224
+ qres = nidm_graph .query (query )
225
+
226
+ for row in qres :
227
+ de [current_tuple ]['isAbout' ].append ({'@id' : value , 'label' : row [0 ]})
228
+ else :
229
+ # only 1 isAbout entry
230
+ # query for label for this isAbout URL
231
+ query = '''
232
+
233
+ prefix prov: <http://www.w3.org/ns/prov#>
234
+ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
235
+ prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
236
+
237
+ select distinct ?label
238
+ where {
239
+ <%s> rdf:type prov:Entity ;
240
+ rdfs:label ?label .
241
+ }
242
+ ''' % value
243
+ # print(query)
244
+ qres = nidm_graph .query (query )
245
+ for row in qres :
246
+ de [current_tuple ]['isAbout' ].append ({'@id' : value , 'label' : row [0 ]})
247
+
248
+ elif key == 'http://www.w3.org/2000/01/rdf-schema#label' :
249
+ de [current_tuple ]['label' ] = value
250
+ elif key == 'http://purl.org/nidash/nidm#valueType' :
251
+ if 'responseOptions' not in de [current_tuple ].keys ():
252
+ de [current_tuple ]['responseOptions' ] = {}
253
+ de [current_tuple ]['responseOptions' ]['valueType' ] = value
254
+ else :
255
+ de [current_tuple ]['responseOptions' ]['valueType' ] = value
256
+ elif key == 'http://purl.org/nidash/nidm#levels' :
257
+ if 'responseOptions' not in de [current_tuple ].keys ():
258
+ de [current_tuple ]['responseOptions' ] = {}
259
+ de [current_tuple ]['responseOptions' ]['levels' ] = value
260
+ else :
261
+ de [current_tuple ]['responseOptions' ]['levels' ] = value
262
+ elif key == 'http://uri.interlex.org/ilx_0739289' :
263
+ de [current_tuple ]['associatedWith' ] = value
264
+ elif key == Constants .NIDM ['minValue' ]:
265
+ de [current_tuple ]['responseOptions' ]['minValue' ] = value
266
+ elif key == Constants .NIDM ['maxValue' ]:
267
+ de [current_tuple ]['responseOptions' ]['maxValue' ] = value
268
+ elif key == Constants .NIDM ['url' ]:
269
+ de [current_tuple ]['url' ] = value
270
+
271
+ return de
155
272
156
273
157
274
def CreateBIDSParticipantFile (nidm_graph ,output_file ,participant_fields ):
@@ -186,6 +303,9 @@ def CreateBIDSParticipantFile(nidm_graph,output_file,participant_fields):
186
303
#add row to the pandas data frame
187
304
#data.append(obj)
188
305
participants .loc [row_index ,BIDS_Constants .participants [fields ].uri ] = obj
306
+
307
+ # find Data Element and add metadata to participants_json dictionary
308
+
189
309
else :
190
310
#text matching task, remove basepart of URIs and try to fuzzy match the field in the part_fields parameter string
191
311
#to the "term" part of a qname URI...this part let's a user simply ask for "age" for example without knowing the
@@ -219,7 +339,7 @@ def CreateBIDSParticipantFile(nidm_graph,output_file,participant_fields):
219
339
?pred ?value .
220
340
FILTER (regex(str(?pred) ,"%s","i" ))
221
341
}""" % (subj_uri ,fields )
222
- # print(query)
342
+ #print(query)
223
343
qres = nidm_graph .query (query )
224
344
225
345
for row in qres :
@@ -233,8 +353,12 @@ def CreateBIDSParticipantFile(nidm_graph,output_file,participant_fields):
233
353
short_name = path_parts [2 ]
234
354
else :
235
355
short_name = url_parts .fragment
236
- participants_json [short_name ] = {}
237
- participants_json [short_name ]['TermURL' ] = row [0 ]
356
+
357
+ # find Data Element and add metadata to participants_json dictionary
358
+ if 'de' not in locals ():
359
+ de = GetDataElementMetadata (nidm_graph , short_name )
360
+ else :
361
+ de .update (GetDataElementMetadata (nidm_graph , short_name ))
238
362
239
363
participants .loc [row_index ,str (short_name )] = str (row [1 ])
240
364
#data.append(str(row[1]))
@@ -251,6 +375,10 @@ def CreateBIDSParticipantFile(nidm_graph,output_file,participant_fields):
251
375
with open (output_file + ".json" ,'w' ) as f :
252
376
json .dump (participants_json ,f ,sort_keys = True ,indent = 2 )
253
377
378
+
379
+ # save participant sidecar file
380
+ write_json_mapping_file (de , join (splitext (output_file )[0 ] + ".json" ), True )
381
+
254
382
return participants , participants_json
255
383
256
384
@@ -280,7 +408,11 @@ def NIDMProject2BIDSDatasetDescriptor(nidm_graph,output_directory):
280
408
281
409
for key ,value in BIDS_Constants .dataset_description .items ():
282
410
if BIDS_Constants .dataset_description [key ]._uri == proj_key :
283
- project_metadata [key ] = project_metadata [proj_key ]
411
+ # added since BIDS validator validates values of certain keys
412
+ if (key == "Authors" ) or (key == "Funding" ) or (key == "ReferencesAndLinks" ):
413
+ project_metadata [key ] = [project_metadata [proj_key ]]
414
+ else :
415
+ project_metadata [key ] = project_metadata [proj_key ]
284
416
del project_metadata [proj_key ]
285
417
key_found = 1
286
418
continue
@@ -293,6 +425,34 @@ def NIDMProject2BIDSDatasetDescriptor(nidm_graph,output_directory):
293
425
294
426
##############################################################################
295
427
428
+ def AddMetadataToImageSidecar (graph_entity ,graph , output_directory , image_filename ):
429
+ '''
430
+ This function will query the metadata in graph_entity and compare the entries with mappings in
431
+ core/BIDS_Constants.py json_keys where we'll be mapping the value (NIDM entry) to key (BIDS key). It
432
+ will create the appropriate sidecar json file associated with image_filename in output_directory.
433
+ '''
434
+
435
+ # query graph for metadata associated with graph_entity
436
+ query = '''
437
+ Select DISTINCT ?p ?o
438
+ WHERE {
439
+ <%s> ?p ?o .
440
+ }
441
+ ''' % graph_entity
442
+ qres = graph .query (query )
443
+
444
+ # dictionary to store metadata
445
+ json_dict = {}
446
+ for row in qres :
447
+ key = next ((k for k in BIDS_Constants .json_keys if BIDS_Constants .json_keys [k ] == row [0 ]), None )
448
+ if key != None :
449
+ json_dict [key ] = row [1 ]
450
+
451
+ # write json_dict out to appropriate sidecar filename
452
+ with open (join (output_directory ,image_filename + ".json" ),"w" ) as fp :
453
+ json .dump (json_dict ,fp ,indent = 2 )
454
+
455
+
296
456
def ProcessFiles (graph ,scan_type ,output_directory ,project_location ,args ):
297
457
'''
298
458
This function will essentially cycle through the acquisition objects in the NIDM file loaded into graph
@@ -354,6 +514,7 @@ def ProcessFiles(graph,scan_type,output_directory,project_location,args):
354
514
print ("Trying to copy file from %s" % (location ))
355
515
try :
356
516
copyfile (location , join (output_directory , sub_dir , bids_ext , basename (filename )))
517
+
357
518
except :
358
519
print ("ERROR! Failed to find file %s on filesystem..." % location )
359
520
if not args .no_downloads :
@@ -367,10 +528,22 @@ def ProcessFiles(graph,scan_type,output_directory,project_location,args):
367
528
sys .exc_info ()[0 ], location ))
368
529
GetImageFromAWS (location = location , output_file =
369
530
join (output_directory , sub_dir , bids_ext , basename (filename )),args = args )
531
+
370
532
else :
371
533
# copy temporary file to BIDS directory
372
534
copyfile (ret , join (output_directory , sub_dir , bids_ext , basename (filename )))
373
535
536
+ # if we were able to copy the image file then add the json sidecar file with additional metadata
537
+ # available in the NIDM file
538
+ if isfile (join (output_directory , sub_dir , bids_ext , basename (filename ))):
539
+ # get rest of metadata for this acquisition and store in sidecar file
540
+ if "gz" in basename (filename ):
541
+ image_filename = splitext (splitext (basename (filename ))[0 ])[0 ]
542
+ else :
543
+ image_filename = splitext (basename (filename ))[0 ]
544
+ AddMetadataToImageSidecar (graph_entity = acq ,graph = graph ,output_directory = join (output_directory ,
545
+ sub_dir ,bids_ext ),image_filename = image_filename )
546
+
374
547
# if this is a DWI scan then we should copy over the b-value and b-vector files
375
548
if bids_ext == 'dwi' :
376
549
# search for entity uuid with rdf:type nidm:b-value that was generated by activity
@@ -529,6 +702,9 @@ def main(argv):
529
702
print ("Reading RDF file as %s..." % format )
530
703
#load NIDM graph into NIDM-Exp API objects
531
704
nidm_project = read_nidm (rdf_file )
705
+ # temporary save nidm_project
706
+ with open ("/Users/dbkeator/Downloads/nidm.ttl" , 'w' ) as f :
707
+ print (nidm_project .serializeTurtle (), file = f )
532
708
print ("RDF file sucessfully read" )
533
709
format_found = True
534
710
break
0 commit comments