Skip to content

Commit

Permalink
Merge pull request #115 from brainlife/enh/more_metadata
Browse files Browse the repository at this point in the history
[ENH] Expand metadata editing capabilities
  • Loading branch information
dlevitas authored Feb 14, 2024
2 parents 295759b + b8e0e4b commit 8684b56
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 2 deletions.
8 changes: 7 additions & 1 deletion ui/src/Objects.vue
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,13 @@
>
</p>
<el-form-item
v-if="['perf/asl', 'perf/m0scan'].includes(so._type) || so._type.startsWith('pet/')"
v-if="
['perf/asl', 'perf/m0scan'].includes(so._type) ||
so._type.startsWith('pet') ||
so._type.startsWith('func') ||
so._type.startsWith('fmap') ||
so._type.startsWith('dwi')
"
label="Relevant Metadata"
>
<ModalityForm :ss="so" :ezbids="ezbids" @form-submitted="submitForm" />
Expand Down
8 changes: 7 additions & 1 deletion ui/src/SeriesPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,13 @@
</el-form-item>
<br />
<el-form-item
v-if="['perf/asl', 'perf/m0scan'].includes(ss.type) || ss.type.startsWith('pet/')"
v-if="
['perf/asl', 'perf/m0scan'].includes(ss.type) ||
ss.type.startsWith('pet') ||
ss.type.startsWith('func') ||
ss.type.startsWith('fmap') ||
ss.type.startsWith('dwi')
"
label="Relevant Metadata"
>
<ModalityForm :ss="ss" :ezbids="ezbids" @form-submitted="submitForm" />
Expand Down
20 changes: 20 additions & 0 deletions ui/src/assets/schema/rules/sidecars/dwi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
# Multipart (split) DWI schemes
# NOTE: I don't think this can be schemafied, since it depends on owner intent.
MRIDiffusionMultipart:
selectors:
- datatype == "dwi"
- suffix == "dwi"
- match(extension, "^\.nii(\.gz)?$")
fields:
MultipartID: optional

# Other recommended metadata
MRIDiffusionOtherMetadata:
selectors:
- datatype == "dwi"
- suffix == "dwi"
- match(extension, "^\.nii(\.gz)?$")
fields:
PhaseEncodingDirection: recommended
TotalReadoutTime: recommended
71 changes: 71 additions & 0 deletions ui/src/assets/schema/rules/sidecars/fmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#
# Groups of related metadata fields
#
# Assumptions: never need disjunction of selectors
# Assumptions: top-to-bottom overrides is sufficient logic

---
# Fieldmap data
MRIFieldmapIntendedFor:
selectors:
- datatype == "fmap"
- match(extension, '\.nii(\.gz)?$')
fields:
IntendedFor:
level: optional
description_addendum: |
This field is optional, and in case the fieldmaps do not correspond
to any particular scans, it does not have to be filled.
MRIFieldmapB0FieldIdentifier:
selectors:
- datatype == "fmap"
- match(extension, '\.nii(\.gz)?$')
- '!("IntendedFor" in sidecar)'
fields:
B0FieldIdentifier: recommended

# Case 1: Phase-difference map and at least one magnitude image
MRIFieldmapPhaseDifferencePhasediff:
selectors:
- datatype == "fmap"
- suffix == "phasediff"
- match(extension, '\.nii(\.gz)?$')
fields:
EchoTime1: required
EchoTime2: required

# Case 2: Two phase maps and two magnitude images
# NOTE: Need to check for presence of related files.
# For example, magnitude1 needs EchoTime__fmap only if phase1 file exists,
# but EchoTime1 if phasediff exists.
MRIFieldmapTwoPhase:
selectors:
- datatype == "fmap"
- intersects([suffix], ["phase1", "phase2"])
- match(extension, '\.nii(\.gz)?$')
fields:
EchoTime__fmap: required

# Case 3: Direct field mapping
MRIFieldmapDirectFieldMapping:
selectors:
- datatype == "fmap"
- suffix == "fieldmap"
- match(extension, '\.nii(\.gz)?$')
fields:
Units:
level: required
description_addendum: |
Fieldmaps must be in units of Hertz (`"Hz"`),
radians per second (`"rad/s"`), or Tesla (`"T"`).
# Case 4: Multiple phase encoded directions ("pepolar")
MRIFieldmapPepolar:
selectors:
- datatype == "fmap"
- suffix == "epi"
- match(extension, '\.nii(\.gz)?$')
fields:
PhaseEncodingDirection: required
TotalReadoutTime: required
88 changes: 88 additions & 0 deletions ui/src/assets/schema/rules/sidecars/func.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#
# Groups of related metadata fields
#
# Assumptions: never need disjunction of selectors
# Assumptions: top-to-bottom overrides is sufficient logic

---
# Task imaging data

# Required fields
MRIFuncRequired:
selectors:
- datatype == "func"
- suffix == "bold"
- match(extension, "^\.nii(\.gz)?$")
fields:
TaskName:
level: required
description_addendum: |
A recommended convention is to name resting state task using labels
beginning with `rest`.
MRIFuncRepetitionTime:
selectors:
- datatype == "func"
- suffix == "bold"
- '!("VolumeTiming" in sidecar)'
- match(extension, "^\.nii(\.gz)?$")
fields:
RepetitionTime:
level: required
level_addendum: mutually exclusive with `VolumeTiming`

MRIFuncVolumeTiming:
selectors:
- datatype == "func"
- suffix == "bold"
- '!("RepetitionTime" in sidecar)'
- match(extension, "^\.nii(\.gz)?$")
fields:
VolumeTiming:
level: required
level_addendum: mutually exclusive with `RepetitionTime`

# Timing Parameters
MRIFuncTimingParameters:
selectors:
- datatype == "func"
- suffix == "bold"
fields:
NumberOfVolumesDiscardedByScanner: recommended
NumberOfVolumesDiscardedByUser: recommended
DelayTime: recommended
AcquisitionDuration:
level: recommended
level_addendum: |
required for sequences that are described with the `VolumeTiming`
field and that do not have the `SliceTiming` field set to allow for
accurate calculation of "acquisition time"
issue:
name: VOLUME_TIMING_MISSING_ACQUISITION_DURATION
message: |
The field 'VolumeTiming' requires 'AcquisitionDuration' or 'SliceTiming' to be defined.
DelayAfterTrigger: recommended

# fMRI task information
MRIFuncTaskInformation:
selectors:
- datatype == "func"
- suffix == "bold"
fields:
Instructions:
level: recommended
description_addendum: |
This is especially important in context of resting state recordings and
distinguishing between eyes open and eyes closed paradigms.
TaskDescription: recommended
CogAtlasID: recommended
CogPOID: recommended

# Should now be `part-phase_bold.nii`, but still require units
PhaseSuffixUnits:
selectors:
- datatype == "func"
- suffix == "phase"
- match(extension, "^\.nii(\.gz)?$")
fields:
Units: required
17 changes: 17 additions & 0 deletions ui/src/components/modalityForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,9 @@
<script lang="ts">
import aslYaml from '../../src/assets/schema/rules/sidecars/asl.yaml';
import petYaml from '../../src/assets/schema/rules/sidecars/pet.yaml';
import funcYaml from '../../src/assets/schema/rules/sidecars/func.yaml';
import dwiYaml from '../../src/assets/schema/rules/sidecars/dwi.yaml';
import fmapYaml from '../../src/assets/schema/rules/sidecars/fmap.yaml';
import metadata_types from '../../src/assets/schema/rules/sidecars/metadata_types.yaml';
import { ElMessageBox, ElMessage, useFocus } from 'element-plus';
Expand Down Expand Up @@ -654,6 +657,12 @@ export default defineComponent({
fileObject = aslYaml;
} else if (type.startsWith('pet')) {
fileObject = petYaml;
} else if (type.startsWith('func')) {
fileObject = funcYaml;
} else if (type.startsWith('dwi')) {
fileObject = dwiYaml;
} else if (type.startsWith('fmap')) {
fileObject = fmapYaml;
}
let result = {
required: [],
Expand Down Expand Up @@ -682,6 +691,10 @@ export default defineComponent({
if (type === 'pet/pet' && section.includes('Blood')) {
continue;
}
//TODO - Need to come back to this and make the underlying code better at accounting for these
if (type.startsWith('func') && ['TaskName', 'VolumeTiming', 'Units'].includes(field)) {
continue;
}
// get the metadata from the metadata_types.yaml
const details = metadata_types[field] || {};
Expand Down Expand Up @@ -1125,6 +1138,10 @@ export default defineComponent({
// regex to get CASL
const regex = /objects.enums.(\w+).value/;
const refObject = regex.exec(item['$ref']);
// Spec PED info contains the phrase "Minus" rather than the actual symbol (-). Correct this here
if (refObject[1].endsWith('Minus')) {
refObject[1] = refObject[1].replace('Minus', '-');
}
return { value: refObject[1], label: refObject[1] };
}
}
Expand Down

0 comments on commit 8684b56

Please sign in to comment.