diff --git a/handler/convert.js b/handler/convert.js index 30e431a1..d4ccd612 100755 --- a/handler/convert.js +++ b/handler/convert.js @@ -150,6 +150,38 @@ async.forEachOf(info.objects, (o, idx, next_o) => { handleItem(item, suffix + ".nii.gz", derivatives); break; case "json": + //handle IntendedFor + if (o.IntendedFor) { + item.sidecar.IntendedFor = []; + for (let idx of o.IntendedFor) { + const io = info.objects[idx]; + //this should not happen, but ezBIDS_core.json could be corrupted.. + if (!io) { + console.error("can't find object with ", idx); + continue; + } + //if intended object is excluded, skip it + if (io._type == "exclude") continue; + + const iomodality = io._type.split("/")[0]; + const suffix = io._type.split("/")[1]; + //construct a path relative to the subject + let path = ""; + if (io._entities.session) + path += "ses-" + io._entities.session + "/"; + path += iomodality + "/"; + let tokens = []; + //for(let k in io._entities) { + for (let k in info.entityMappings) { + const sk = info.entityMappings[k]; + if (io._entities[k]) + tokens.push(sk + "-" + io._entities[k]); + } + path += tokens.join("_"); + path += "_" + suffix + ".nii.gz"; //TODO - not sure if this is robust enough.. + item.sidecar.IntendedFor.push(path); + } + } handleItem(item, suffix + ".json", derivatives); break; default: diff --git a/handler/convert.ts b/handler/convert.ts index 3895f358..1d14efad 100755 --- a/handler/convert.ts +++ b/handler/convert.ts @@ -166,6 +166,38 @@ async.forEachOf(info.objects, (o, idx, next_o)=>{ handleItem(item, suffix + ".nii.gz", derivatives); break; case "json": + //handle IntendedFor + if (o.IntendedFor) { + item.sidecar.IntendedFor = []; + for (let idx of o.IntendedFor) { + const io = info.objects[idx]; + //this should not happen, but ezBIDS_core.json could be corrupted.. + if (!io) { + console.error("can't find object with ", idx); + continue; + } + //if intended object is excluded, skip it + if (io._type == "exclude") continue; + + const iomodality = io._type.split("/")[0]; + const suffix = io._type.split("/")[1]; + //construct a path relative to the subject + let path = ""; + if (io._entities.session) + path += "ses-" + io._entities.session + "/"; + path += iomodality + "/"; + let tokens = []; + //for(let k in io._entities) { + for (let k in info.entityMappings) { + const sk = info.entityMappings[k]; + if (io._entities[k]) + tokens.push(sk + "-" + io._entities[k]); + } + path += tokens.join("_"); + path += "_" + suffix + ".nii.gz"; //TODO - not sure if this is robust enough.. + item.sidecar.IntendedFor.push(path); + } + } handleItem(item, suffix + ".json", derivatives); break; default: diff --git a/ui/src/SeriesPage.vue b/ui/src/SeriesPage.vue index 511a10b2..ce1eff62 100755 --- a/ui/src/SeriesPage.vue +++ b/ui/src/SeriesPage.vue @@ -328,6 +328,11 @@ export default defineComponent({ }) } } + + // Alert users to check relevant metadata ("Edit Metadata" button for any perf sequences) + if (s.type.startsWith("perf")) { + s.validationWarnings.push("Please check to ensure that all necessary metadata is provided, by clicking on the 'Edit Metadata' button below") + } }, isValid(cb: (v?: string)=>void) { diff --git a/ui/src/assets/schema/rules/sidecars/asl.yaml b/ui/src/assets/schema/rules/sidecars/asl.yaml index f0bff8aa..2dd262d3 100644 --- a/ui/src/assets/schema/rules/sidecars/asl.yaml +++ b/ui/src/assets/schema/rules/sidecars/asl.yaml @@ -31,6 +31,27 @@ MRIASLCommonMetadataFields: LabelingLocationDescription: recommended LookLocker: optional LabelingEfficiency: optional + LabelingDuration: + level: optional + level_addendum: required if `ArterialSpinLabelingType` is in [`PCASL`,`CASL`] + LabelingPulseAverageGradient: + level: optional + level_addendum: recommended if `ArterialSpinLabelingType` is in [`PCASL`,`CASL`] + LabelingPulseMaximumGradient: + level: optional + level_addendum: recommended if `ArterialSpinLabelingType` is in [`PCASL`,`CASL`] + LabelingPulseAverageB1: + level: optional + level_addendum: recommended if `ArterialSpinLabelingType` is in [`PCASL`,`CASL`] + LabelingPulseDuration: + level: optional + level_addendum: required if `ArterialSpinLabelingType` is in [`PCASL`,`CASL`] + LabelingPulseFlipAngle: + level: optional + level_addendum: recommended if `ArterialSpinLabelingType` is in [`PCASL`,`CASL`] + LabelingPulseInterval: + level: optional + level_addendum: recommended if `ArterialSpinLabelingType` is in [`PCASL`,`CASL`] MRIASLCommonMetadataFieldsM0TypeRec: selectors: @@ -120,7 +141,10 @@ MRIASLPcaslSpecific: fields: PCASLType: level: recommended - level_addendum: if `ArterialSpinLabelingType` is `"PCASL"` + level_addendum: recommended if `ArterialSpinLabelingType` is `PCASL` + LabelingDuration: + level: optional + level_addendum: required if `ArterialSpinLabelingType` is `PCASL` MRIASLCaslSpecific: selectors: @@ -130,7 +154,7 @@ MRIASLCaslSpecific: fields: CASLType: level: recommended - level_addendum: if `ArterialSpinLabelingType` is `"CASL"` + level_addendum: recommended if `ArterialSpinLabelingType` is `"CASL"` MRIASLPaslSpecific: selectors: diff --git a/ui/src/components/modalityForm.vue b/ui/src/components/modalityForm.vue index 3cfd6960..17ecd508 100644 --- a/ui/src/components/modalityForm.vue +++ b/ui/src/components/modalityForm.vue @@ -1,162 +1,433 @@ -``` \ No newline at end of file +``` diff --git a/ui/src/libUnsafe.ts b/ui/src/libUnsafe.ts index c41fa49f..bf33ea74 100755 --- a/ui/src/libUnsafe.ts +++ b/ui/src/libUnsafe.ts @@ -427,7 +427,7 @@ export function setIntendedFor($root:IEzbids) { section.forEach((obj:IObject) => { //add IntendedFor information - if (obj._type.startsWith("fmap/")) { + if (obj._type.startsWith("fmap/") || obj._type === "perf/m0scan") { Object.assign(obj, {IntendedFor: []}) let correspindingSeriesIntendedFor = $root.series[obj.series_idx].IntendedFor if (correspindingSeriesIntendedFor !== undefined && correspindingSeriesIntendedFor !== null) {