Skip to content

Commit

Permalink
[FIX] Correct func/sbref and corresponding func/bold linking
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan Levitas committed Sep 4, 2023
1 parent 4ff9b67 commit 82863ab
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 41 deletions.
2 changes: 1 addition & 1 deletion ui/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export default defineComponent({
this.$store.commit("setPage", this.pages[idx+1]);
switch(this.page) {
case "event":
// setVolumeThreshold(this.ezbids);
setVolumeThreshold(this.ezbids);
setSectionIDs(this.ezbids);
funcQA(this.ezbids);
fmapQA(this.ezbids);
Expand Down
72 changes: 38 additions & 34 deletions ui/src/Objects.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
<span v-if="o_sub.sub != ''" class="hierarchy">
<i class="el-icon-user-solid" style="margin-right: 2px;"/>
<small>sub-</small><b>{{o_sub.sub}}</b>
<!-- <el-form-item label="Volume Threshold" prop="Volume Threshold">
<el-input v-model="ezbids._organized.setVolumeThreshold"
placeholder="Specify a volume threshold for all 4D dataset sequences" size="small"
@change="validateAll()" style="width: 80%">
<!-- &nbsp;
<el-form-item label="Volume Threshold" prop="Volume Threshold">
<el-input v-model="ezbids.funcBoldVoumeThreshold"
placeholder="Specify a volume threshold for all 4D dataset sequences" size="small"
@change="setVolumeThreshold(ezbids._organized)" style="width: 100%">
</el-input>
<p style="margin-top: 0">
<small>* Recommended/Optional: You can specify a volume threshold where any sequences in this series will be set to "exclude" if they don't reach the threshold. Useful in instances where a sequence needed to be restated. If no threshold is specified, ezBIDS will default to the expected number of volumes collected in a 1-min period.</small>
Expand Down Expand Up @@ -232,7 +233,7 @@
import { defineComponent } from 'vue'
import datatype from './components/datatype.vue'
import { IObject, Session, OrganizedSession, OrganizedSubject } from './store'
import { IObject, Session, OrganizedSession, OrganizedSubject, volumeThresholdNum } from './store'
import { prettyBytes } from './filters'
import { deepEqual, setRun, setIntendedFor, align_entities, validateEntities, validate_B0FieldIdentifier_B0FieldSource } from './libUnsafe'
Expand All @@ -255,6 +256,7 @@
return {
so: null as IObject|null, //selected object
sess: null as OrganizedSession|null, //selected session for IntendedFor handling
volumeThreshold: 0 as volumeThreshold,
}
},
Expand Down Expand Up @@ -305,31 +307,33 @@
this.validateAll();
},
setVolumeThreshold(root:OrganizedSubject) {
/*
Determine volume threshold for all func/bold acquisitions in dataset and set
to exclude if the number of volumes does not meet the volume threshold. Threshold
calculated based on the expected number of volumes collected in a 1-minute time frame,
with the formula (60-sec / tr), where tr == RepetitionTime
*/
root.sess.forEach(sessGroup=>{
sessGroup.objects.forEach(o=>{
//update analysisResults.warnings in case user went back to Series and adjusted things
if(["func/bold", "func/cbv", "dwi/dwi"].includes(o._type)) {
let tr = o.items[0].sidecar.RepetitionTime
let numVolumes = o.analysisResults.NumVolumes
let numVolumes1min = Math.floor(60 / tr)
if(numVolumes <= numVolumes1min) {
o.exclude = true
o.analysisResults.warnings = [`This 4D sequence contains ${numVolumes} volumes, which is \
less than the threshold value of ${numVolumes1min} volumes, calculated by the expected number of \
volumes in a 1 min time frame. This acquisition will thus be excluded from BIDS conversion unless \
unexcluded. Please modify if incorrect.`]
}
}
});
});
},
// setVolumeThreshold(root:OrganizedSubject) {
// /*
// Determine volume threshold for all func/bold acquisitions in dataset and set
// to exclude if the number of volumes does not meet the volume threshold. Threshold
// calculated based on the expected number of volumes collected in a 1-minute time frame,
// with the formula (60-sec / tr), where tr == RepetitionTime
// */
// root.sess.forEach(sessGroup=>{
// sessGroup.objects.forEach(o=>{
// //update analysisResults.warnings in case user went back to Series and adjusted things
// if(["func/bold", "func/cbv", "dwi/dwi"].includes(o._type)) {
// let tr = o.items[0].sidecar.RepetitionTime
// let numVolumes = o.analysisResults.NumVolumes
// let numVolumes1min = Math.floor(60 / tr)
// if(numVolumes <= numVolumes1min) {
// o.exclude = true
// o.analysisResults.warnings = [`This 4D sequence contains ${numVolumes} volumes, which is \
// less than the threshold value of ${numVolumes1min} volumes, calculated by the expected number of \
// volumes in a 1 min time frame. This acquisition will thus be excluded from BIDS conversion unless \
// unexcluded. Please modify if incorrect.`]
// }
// }
// });
// });
// this.$emit("mapObjects");
// this.validateAll();
// },
isExcluded(o: IObject) {
if(o.exclude) return true;
Expand Down Expand Up @@ -500,10 +504,9 @@
Any phase data (part-phase) is linked to the magnitude. If part entity is specified, make sure it's
properly linked and has same entities (except for part) and exclusion criteria.
*/
if(o._entities.part && !["", "mag"].includes(o._entities.part)) {
if(o._entities.part && o._entities.part !== "" && o._entities.part !== "mag") {
let correspondingFuncMag = this.ezbids.objects.filter((object:IObject)=>object._type == o._type &&
object._entities.part === "mag" &&
parseInt(o.SeriesNumber) === parseInt(object.SeriesNumber) + 1 &&
deepEqual(Object.fromEntries(Object.entries(object._entities).filter(([key])=>key !== "part")), Object.fromEntries(Object.entries(o._entities).filter(([key])=>key !== "part"))))
if(correspondingFuncMag) { // should be no more than one
Expand Down Expand Up @@ -534,9 +537,10 @@
}
// func/sbref are implicitly linked to a func/bold; make sure these have same entities and exclusion criteria
if(o._type == "func/sbref") {
if(o._type === "func/sbref") {
let correspondingFuncBold = this.ezbids.objects.filter((object:IObject)=>object._type === "func/bold" &&
parseInt(object.ModifiedSeriesNumber) === parseInt(o.ModifiedSeriesNumber) + 1) //func/sbref [should] always come right before their func/bold
deepEqual(object._entities, o._entities))
if(correspondingFuncBold) { // should be no more than one
correspondingFuncBold.forEach((boldObj:IObject)=>{
o.analysisResults.section_id = boldObj.analysisResults.section_id
Expand Down
12 changes: 6 additions & 6 deletions ui/src/libUnsafe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ export function funcQA($root) {
}
}

//now check for corresponding func/bold == exclude, and go from there
if(o._type == "func/bold" && o.exclude == true && (!o._entities.mag || o._entities.mag == "mag" || o._entities.mag == "part")) {
//now check for corresponding func/bold === exclude, and go from there
if(o._type === "func/bold" && o.exclude && (!o._entities.mag || o._entities.mag == "mag" || o._entities.mag == "part")) {
let funcBoldEntities = o._entities
let badFuncSBRef = $root.objects.filter(e=>e._type == "func/sbref" && deepEqual(e._entities, funcBoldEntities))
let badFuncBoldPhase = $root.objects.filter(e=>e._type == "func/bold" && e._entities.mag == "phase" && deepEqual(Object.keys(e._entities).filter(e=>e != "part"), funcBoldEntities))
Expand All @@ -136,10 +136,10 @@ export function funcQA($root) {
}

// #2
if(o._type == "func/bold" && o.exclude == false && (!o._entities.mag || o._entities.mag == "mag")) {
if(o._type === "func/bold" && !o.exclude && (!o._entities.mag || o._entities.mag == "mag")) {
let boldEntities = o._entities
let boldPED = o.items[0].sidecar.PhaseEncodingDirection
let badSBRef = $root.objects.filter(e=>e._type == "func/sbref" && deepEqual(e._entities, boldEntities) &&
let badSBRef = $root.objects.filter(e=>e._type === "func/sbref" && deepEqual(e._entities, boldEntities) &&
e.items[0].sidecar.PhaseEncodingDirection != boldPED).map(e=>e.idx)
badSBRef.forEach(bad=> {
// $root.objects[bad].exclude = true
Expand Down Expand Up @@ -498,15 +498,15 @@ export function setIntendedFor($root) {

// Loop through sections
sectionIDs.forEach(s=> {
let section = sesGroup.objects.filter(e=>e.analysisResults.section_id == s && !e._exclude && e._type != "exclude")
let section = sesGroup.objects.filter(e=>e.analysisResults.section_id === s && !e._exclude && e._type != "exclude")

section.forEach(obj=>{
//add IntendedFor information
if(obj._type.startsWith("fmap/")) {
Object.assign(obj, {IntendedFor: []})
let correspindingSeriesIntendedFor = $root.series[obj.series_idx].IntendedFor
correspindingSeriesIntendedFor.forEach(i=>{
let IntendedForIDs = section.filter(o=>o.series_idx == i && o._type != "func/events").map(o=>o.idx)
let IntendedForIDs = section.filter(o=>o.series_idx === i && o._type != "func/events").map(o=>o.idx)
obj.IntendedFor = obj.IntendedFor.concat(IntendedForIDs)
});
}
Expand Down

0 comments on commit 82863ab

Please sign in to comment.