-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* sort consequences correctly + show pagination count + make workbench visible to all (but disabled for no kf-investigator) * sort consequences + move file * make sure func name is not misleading (filterThanSort.. vs sort)
- Loading branch information
1 parent
82bb73a
commit c8d8716
Showing
12 changed files
with
331 additions
and
505 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
import { generateConsequencesDataLines, filterThanSortConsequencesByImpact } from '../consequences'; | ||
|
||
describe('Consequences', () => { | ||
describe('sortConsequences', () => {}); | ||
it('should sort correctly (desc impact and canonical comes first)', () => { | ||
const generalCase = [ | ||
{ | ||
node: { biotype: 'protein_coding', impact_score: 4, canonical: true }, | ||
}, | ||
{ | ||
node: { biotype: 'retained_intron', impact_score: 4, canonical: false }, | ||
}, | ||
{ | ||
node: { biotype: 'protein_coding', impact_score: 2, canonical: true }, | ||
}, | ||
{ | ||
node: { biotype: 'protein_coding', impact_score: 2, canonical: false }, | ||
}, | ||
{ | ||
node: { biotype: 'protein_coding', impact_score: 1, canonical: false }, | ||
}, | ||
]; | ||
// @ts-ignore only needed data. | ||
expect(filterThanSortConsequencesByImpact(generalCase)).toEqual([ | ||
{ | ||
node: { | ||
biotype: 'protein_coding', | ||
canonical: true, | ||
impact_score: 4, | ||
}, | ||
}, | ||
{ | ||
node: { | ||
biotype: 'retained_intron', | ||
canonical: false, | ||
impact_score: 4, | ||
}, | ||
}, | ||
{ | ||
node: { | ||
biotype: 'protein_coding', | ||
canonical: true, | ||
impact_score: 2, | ||
}, | ||
}, | ||
{ | ||
node: { | ||
biotype: 'protein_coding', | ||
canonical: false, | ||
impact_score: 2, | ||
}, | ||
}, | ||
{ | ||
node: { | ||
biotype: 'protein_coding', | ||
canonical: false, | ||
impact_score: 1, | ||
}, | ||
}, | ||
]); | ||
}); | ||
|
||
describe('generateConsequencesDataLines', () => { | ||
it('should handle trivial cases ', () => { | ||
const degenerateCase = null; | ||
expect(generateConsequencesDataLines(degenerateCase)).toEqual([]); | ||
const trivialCase = [ | ||
{ | ||
node: { | ||
impact_score: 1, | ||
canonical: false, | ||
}, | ||
}, | ||
]; | ||
|
||
// @ts-ignore only needed data. | ||
expect(generateConsequencesDataLines(trivialCase)).toEqual([ | ||
{ | ||
node: { | ||
impact_score: 1, | ||
canonical: false, | ||
}, | ||
}, | ||
]); | ||
}); | ||
|
||
it( | ||
'should generate one line of data for each symbol' + | ||
'with priority for canonical when scores are equal', | ||
() => { | ||
const case1Canonical = [ | ||
{ | ||
node: { symbol: 'PALB2', impact_score: 1, canonical: true }, | ||
}, | ||
{ | ||
node: { symbol: 'PALB2', impact_score: 1, canonical: false }, | ||
}, | ||
{ | ||
node: { symbol: 'PALB2', impact_score: 1, canonical: false }, | ||
}, | ||
{ | ||
node: { symbol: 'AC008870.4', impact_score: 1, canonical: true }, | ||
}, | ||
]; | ||
|
||
// @ts-ignore only needed data. | ||
expect(generateConsequencesDataLines(case1Canonical)).toEqual([ | ||
{ | ||
node: { | ||
canonical: true, | ||
impact_score: 1, | ||
symbol: 'PALB2', | ||
}, | ||
}, | ||
{ | ||
node: { | ||
canonical: true, | ||
impact_score: 1, | ||
symbol: 'AC008870.4', | ||
}, | ||
}, | ||
]); | ||
}, | ||
); | ||
it( | ||
'should generate one line of data for each symbol' + | ||
'with priority for highest score over canonical', | ||
() => { | ||
const case2NoCanonical = [ | ||
{ | ||
node: { symbol: 'PALB2', impact_score: 5, canonical: false }, | ||
}, | ||
{ | ||
node: { symbol: 'PALB2', impact_score: 1, canonical: false }, | ||
}, | ||
{ | ||
node: { symbol: 'PALB2', impact_score: 1, canonical: false }, | ||
}, | ||
{ | ||
node: { symbol: 'AC008870.4', impact_score: 1, canonical: false }, | ||
}, | ||
]; | ||
|
||
// @ts-ignore only needed data. | ||
expect(generateConsequencesDataLines(case2NoCanonical)).toEqual([ | ||
{ | ||
node: { | ||
canonical: false, | ||
impact_score: 5, | ||
symbol: 'PALB2', | ||
}, | ||
}, | ||
{ | ||
node: { | ||
canonical: false, | ||
impact_score: 1, | ||
symbol: 'AC008870.4', | ||
}, | ||
}, | ||
]); | ||
}, | ||
); | ||
|
||
it('should generate pick first consequence when all is equal', () => { | ||
const case2NoCanonical = [ | ||
{ | ||
node: { | ||
symbol: 'PALB2', | ||
impact_score: 1, | ||
canonical: false, | ||
biotype: 'nonsense_mediated_decay', | ||
}, | ||
}, | ||
{ | ||
node: { | ||
symbol: 'PALB2', | ||
impact_score: 1, | ||
canonical: false, | ||
biotype: 'retained_intron', | ||
}, | ||
}, | ||
{ | ||
node: { symbol: 'PALB2', impact_score: 1, canonical: false }, | ||
}, | ||
{ | ||
node: { symbol: 'AC008870.4', impact_score: 1, canonical: false }, | ||
}, | ||
]; | ||
|
||
// @ts-ignore only needed data. | ||
expect(generateConsequencesDataLines(case2NoCanonical)).toEqual([ | ||
{ | ||
node: { | ||
canonical: false, | ||
impact_score: 1, | ||
symbol: 'PALB2', | ||
biotype: 'nonsense_mediated_decay', | ||
}, | ||
}, | ||
{ | ||
node: { | ||
canonical: false, | ||
impact_score: 1, | ||
symbol: 'AC008870.4', | ||
}, | ||
}, | ||
]); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { Consequence } from 'store/graphql/variants/models'; | ||
|
||
const keyNoSymbol = 'noSymbol_'; | ||
|
||
/* | ||
* Algorithm: | ||
* Input: consequences | ||
* #=====# | ||
* - IF consequence has NO gene (symbol) then keep it; | ||
* - IF consequence has multiple symbols, then keep one consequence per symbol. | ||
* - IF consequence has a symbol filter accordingly to these rules: | ||
* for a given symbol, | ||
* find consequence with highest score s | ||
* IF multiple consequences with same score s then find the one that is canonical | ||
* IF canonical does not exist then grab whatever consequence with score s. | ||
* #=====# | ||
* Output: filtered consequences. | ||
* */ | ||
export const filterThanSortConsequencesByImpact = (consequences: Consequence[]) => { | ||
if (!consequences || consequences.length === 0) { | ||
return []; | ||
} | ||
return consequences | ||
.filter((c) => c.node?.impact_score !== null) | ||
.map((c) => ({ ...c })) | ||
.sort((a, b) => { | ||
const isSameScore = a.node.impact_score! === b.node.impact_score!; | ||
const canonicalIsNotFirst = !a.node.canonical && b.node.canonical; | ||
const canonicalNeedsToBeSwapped = isSameScore && canonicalIsNotFirst; | ||
if (canonicalNeedsToBeSwapped) { | ||
return 1; | ||
} | ||
return b.node.impact_score! - a.node.impact_score!; | ||
}); | ||
}; | ||
type SymbolToConsequences = { [key: string]: Consequence[] }; | ||
|
||
export const generateConsequencesDataLines = ( | ||
rawConsequences: Consequence[] | null, | ||
): Consequence[] => { | ||
if (!rawConsequences || rawConsequences.length === 0) { | ||
return []; | ||
} | ||
|
||
const symbolToConsequences: SymbolToConsequences = rawConsequences.reduce<SymbolToConsequences>( | ||
(dict: SymbolToConsequences, consequence: Consequence) => { | ||
const keyForCurrentConsequence = consequence.node?.symbol || keyNoSymbol; | ||
const oldConsequences = dict[keyForCurrentConsequence] || []; | ||
return { ...dict, [keyForCurrentConsequence]: [...oldConsequences, { ...consequence }] }; | ||
}, | ||
{}, | ||
); | ||
|
||
return Object.entries(symbolToConsequences).reduce((acc: Consequence[], [key, consequences]) => { | ||
// no gene then show | ||
if (key === keyNoSymbol) { | ||
return [...acc, ...consequences]; | ||
} | ||
|
||
const highestRanked = filterThanSortConsequencesByImpact(consequences)[0] || {}; | ||
return [...acc, { ...highestRanked }]; | ||
}, []); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.