Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

invalid sample names from big files found on mds3 init is reported to… #2541

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions client/mass/about.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,20 @@ export class MassAbout {
lst: []
}
})
if (clearOnChange.groups) {
for (const group of state.groups) {
subactions.push({
type: 'delete_group',
name: group.name
})
}
for (const term of state.customTerms) {
subactions.push({
type: 'delete_customTerm',
name: term.name
})
}
}
subactions.push({ type: 'cohort_set', activeCohort: i })
app.dispatch({
type: 'app_refresh',
Expand Down Expand Up @@ -227,6 +241,7 @@ export class MassAbout {
if (!result.cfeatures.length) return
for (const feature of result.features) rows.push([{ value: feature.name }])
for (const cohort of result.cohorts) {
if (cohort.subcohorts?.length) continue
columns.push({ label: cohort.name })
for (const [i, feature] of result.features.entries()) {
const cf = result.cfeatures.find(cf => cf.idfeature === feature.idfeature && cf.cohort === cohort.cohort)
Expand Down
1 change: 1 addition & 0 deletions client/mass/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class TdbStore {
let plot
try {
const _ = await import(`../plots/${savedPlot.chartType}.js`)
// this.state{} is already fully set with initial state, thus okay to pass to getPlotConfig()
plot = await _.getPlotConfig(savedPlot, this.app, this.state.activeCohort)
} catch (e) {
this.app.printError(e)
Expand Down
31 changes: 6 additions & 25 deletions client/plots/genomeBrowser.controls.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { getCompInit } from '#rx'
import { Menu } from '#dom/menu'
import { make_one_checkbox } from '#dom/checkbox'
import { Menu, Tabs, make_one_checkbox } from '#dom'
import { filterInit, getNormalRoot, getFilterItemByTag } from '#filter/filter'
import { Tabs } from '../dom/toggleButtons'
import { appInit } from '#termdb/app'

/*
Expand All @@ -13,7 +11,6 @@ main
render1group
makePrompt2addNewGroup
launchMenu_createGroup
mayGetActiveCohortIdx
render1group_info
render1group_population
render1group_filter
Expand Down Expand Up @@ -51,6 +48,7 @@ class GbControls {
//delete this._partialData

return {
activeCohort: appState.activeCohort,
config,
termdbConfig: appState.termdbConfig,
filter: getNormalRoot(appState.termfilter.filter)
Expand Down Expand Up @@ -395,7 +393,6 @@ async function render1group_filter(self, groupIdx, group, div) {
when initiating the filter ui, must join group's filter with mass global filter and submit the joined filter to main()
this allows tvs edit to show correct number of samples
*/

let span
if (!self.filterUI[groupIdx]) {
self.filterUI[groupIdx] = await filterInit({
Expand Down Expand Up @@ -580,7 +577,10 @@ function launchMenu_createGroup(self, groupIdx, div) {
const arg = {
holder: tab.contentHolder,
vocabApi: self.app.vocabApi,
state: { termfilter: { filter: self.state.filter } },
state: {
activeCohort: self.state.activeCohort,
termfilter: { filter: self.state.filter }
},
tree: {
click_term2select_tvs: tvs => {
/////////////////////////////////
Expand All @@ -605,8 +605,6 @@ function launchMenu_createGroup(self, groupIdx, div) {
}
}
}
const activeCohortIdx = mayGetActiveCohortIdx(self)
if (Number.isInteger(activeCohortIdx)) arg.state.activeCohort = activeCohortIdx
appInit(arg)
continue
}
Expand Down Expand Up @@ -637,20 +635,3 @@ export function mayUpdateGroupTestMethodsIdx(self, d) {
// otherwise, do not change existing method idx
}
}

// from mass filter, find a tvs as cohortFilter, to know its array index in selectCohort.values[]
// return undefined for anything that's not valid
function mayGetActiveCohortIdx(self) {
if (!self.state.config.filter) return // no mass filter
const cohortFilter = getFilterItemByTag(self.config.filter, 'cohortFilter') // the tvs object
if (cohortFilter && self.state.termdbConfig.selectCohort) {
// the tvs is found
const cohortName = cohortFilter.tvs.values
.map(d => d.key)
.sort()
.join(',')
const idx = self.state.termdbConfig.selectCohort.values.findIndex(v => v.keys.sort().join(',') == cohortName)
if (idx == -1) throw 'subcohort key is not in selectCohort.values[]'
return idx
}
}
29 changes: 15 additions & 14 deletions client/plots/genomeBrowser.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,7 @@ this{}
terms[]
geneSearchResult{}
snvindel {}
details {}
groupTypes[]
groups:[]
// each element is a group object
{type='info', infoKey=str}
{type='filter', filter={}}
{type='population', key, label, ..}
groupTestMethod{}
groupTestMethodsIdx
details {} // see type def
populations [{key,label}] // might not be part of state
ld {}
tracks[]
Expand Down Expand Up @@ -76,7 +68,7 @@ class genomeBrowser {
this.type = 'genomeBrowser'
}

async init() {
async init(appState) {
const holder = this.opts.holder.append('div')
this.opts.header
.append('div')
Expand Down Expand Up @@ -430,10 +422,11 @@ export const genomeBrowserInit = getCompInit(genomeBrowser)
// this alias will allow abstracted dynamic imports
export const componentInit = genomeBrowserInit

export async function getPlotConfig(opts, app) {
export async function getPlotConfig(opts, app, activeCohort) {
// 3rd arg is initial active cohort
try {
// request default queries config from dataset, and allows opts to override
return await getDefaultConfig(app.vocabApi, opts)
return await getDefaultConfig(app.vocabApi, opts, activeCohort)
} catch (e) {
throw `${e} [genomeBrowser getPlotConfig()]`
}
Expand Down Expand Up @@ -471,7 +464,7 @@ export function makeChartBtnMenu(holder, chartsInstance) {
// must do this as 'plot_prep' does not call getPlotConfig()
// request default queries config from dataset, and allows opts to override
// this config{} will become this.state.config{}
const config = await getDefaultConfig(chartsInstance.app.vocabApi)
const config = await getDefaultConfig(chartsInstance.app.vocabApi, null, chartsInstance.state.activeCohort)

config.chartType = 'genomeBrowser'
config.geneSearchResult = result
Expand All @@ -494,7 +487,7 @@ export function makeChartBtnMenu(holder, chartsInstance) {
}

// get default config of the app from vocabApi
async function getDefaultConfig(vocabApi, override) {
async function getDefaultConfig(vocabApi, override, activeCohort) {
const config = await vocabApi.getMds3queryDetails()
// request default variant filter (against vcf INFO)
const vf = await vocabApi.get_variantFilter()
Expand All @@ -506,6 +499,14 @@ async function getDefaultConfig(vocabApi, override) {
// test method may be inconsistent with group configuration (e.g. no fisher for INFO fields), update test method here
// 1st arg is a fake "self"
mayUpdateGroupTestMethodsIdx({ state: { config: c2 } }, c2.snvindel.details)
// a type=filter group may use filterByCohort. in such case, modify default state to assign proper filter based on current cohort
const gf = c2.snvindel.details.groups.find(i => i.type == 'filter')
if (gf && gf.filterByCohort) {
// modify and assign
gf.filter = gf.filterByCohort[vocabApi.termdbConfig.selectCohort.values[activeCohort].keys.join(',')]
if (!gf.filter) throw 'unknown filter by current cohort name'
delete gf.filterByCohort
}
}
return c2
}
Expand Down
14 changes: 13 additions & 1 deletion server/src/mds3.init.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ validate_query_snvindel
gdc.validate_query_snvindel_byisoform
gdc.validate_query_snvindel_byrange
snvindelByRangeGetter_bcf
validateSampleHeader2
mayLimitSamples
param2filter
tid2value2filter
Expand Down Expand Up @@ -705,13 +706,24 @@ function mayValidateSampleHeader(ds, samples, where) {
function validateSampleHeader2(ds, samples, where) {
const sampleIds = []
// ds?.cohort?.termdb.q.sampleName2id must be present
const unknownSamples = [] // samples present in big file header but missing from db
for (const s of samples) {
const id = ds.cohort.termdb.q.sampleName2id(s.name)
if (!Number.isInteger(id)) throw 'unknown sample name from ' + where
if (!Number.isInteger(id)) {
unknownSamples.push(s.name)
// TODO if file with unknown sample should still be usable, slot a mock element in sampleIds[]. downstream query should be able to ignore it
continue
}
s.name = id
sampleIds.push(s)
}
console.log(samples.length, 'samples from ' + where + ' of ' + ds.label)
if (unknownSamples.length) {
// unknown samples can be safely reported to server log
console.log('unknown samples: ' + unknownSamples.join(', '))
// later attach a sanitized err msg to ds to report to client
throw 'unknown samples in big file'
}
return sampleIds
}

Expand Down
10 changes: 9 additions & 1 deletion server/src/mds3.variant.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,15 @@ function validateParam(q, ds) {
if (q.details.groups.length > 2) throw 'q.details.groups[] has more than 2'
for (const g of q.details.groups) {
if (g.type == 'filter') {
if (typeof g.filter != 'object') throw '.filter not an object for group type=filter'
if (g.filter) {
if (typeof g.filter != 'object') throw '.filter not an object for group type=filter'
} else if (g.filterByCohort) {
if (!ds.cohort.termdb.selectCohort) throw 'filterByCohort in use but ds not using subcohort'
if (typeof g.filterByCohort != 'object') throw '.filterByCohort not an object for group type=filter'
// xx
} else {
throw 'unknown structure of group.type=filter'
}
} else if (g.type == 'population') {
if (!g.key) throw '.key missing from group type=population'
if (!ds.queries.snvindel?.populations) throw 'group type=population but this ds does not have populations'
Expand Down
Binary file modified server/test/tp/files/hg38/TermdbTest/db
Binary file not shown.
34 changes: 33 additions & 1 deletion shared/types/src/dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,38 @@ type Population = {
/** if AC/AN of the population is ancestry-stratified, will be multiple elements of this array; otherwise just one */
sets: PopulationINFOset[]
}
/**
accessible to client via termdb.js?for=mds3queryDetails
part of state of genomeBrowser plot
allowing for user modification
*/
type SnvindelComputeDetails = {
/** xx
*/
groupTypes: any
/** xx
*/
groups: (SnvindelComputeGroup_filter | SnvindelComputeGroup_population)[]
/** xx
*/
groupTestMethods: any
/** array index of groupTestMethods[] */
groupTestMethodsIdx: number
}
type SnvindelComputeGroup_filter = {
type: 'filter'
/** a given filter applied to all cohorts */
filter?: any
/** filter per cohort. use either filter or filterByCohort */
filterByCohort?: any
}
type SnvindelComputeGroup_population = {
type: 'population'
key: string
label: string
allowto_adjust_race: boolean
adjust_race: boolean
}

/** a data type under ds.queries{} */
type SnvIndelQuery = {
Expand Down Expand Up @@ -269,7 +301,7 @@ so that it can work for a termdb-less ds, e.g. clinvar, where termdbConfig canno
}
allowSNPs?: boolean
vcfid4skewerName?: boolean
details?: any
details?: SnvindelComputeDetails
}

type SvFusion = {
Expand Down
Loading