+
OddsPath – Normal
{{
roundOddsPath(
- scoreSet.scoreRanges.investigatorProvided.ranges.find((r) => r.classification === 'normal').oddsPath
+ primaryScoreRange?.ranges.find((r) => r.classification === 'normal').oddsPath
?.ratio
)
}}
{{
- scoreSet.scoreRanges.investigatorProvided.ranges.find((r) => r.classification === 'normal').oddsPath
+ primaryScoreRange?.ranges.find((r) => r.classification === 'normal').oddsPath
?.evidence
}}
+
OddsPath normal not provided
OddsPath – Abnormal
{{
roundOddsPath(
- scoreSet.scoreRanges.investigatorProvided.ranges.find((r) => r.classification === 'abnormal').oddsPath
+ primaryScoreRange?.ranges.find((r) => r.classification === 'abnormal').oddsPath
?.ratio
)
}}
{{
- scoreSet.scoreRanges.investigatorProvided.ranges.find((r) => r.classification === 'abnormal').oddsPath
+ primaryScoreRange?.ranges.find((r) => r.classification === 'abnormal').oddsPath
?.evidence
}}
+
OddsPath abnormal not provided
+
+
+
*OddsPath data from non-primary source(s):
+
+ (
+
+ {{ s.url }},
+
+ ).
+
+
.
OddsPath values are not provided for this score set.
@@ -130,7 +145,7 @@
import _ from 'lodash'
import {defineComponent, PropType} from 'vue'
-import {getScoreSetFirstAuthor} from '@/lib/score-sets'
+import {getScoreSetFirstAuthor, matchSources} from '@/lib/score-sets'
import type {components} from '@/schema/openapi'
export default defineComponent({
@@ -204,13 +219,38 @@ export default defineComponent({
default:
return null
}
+ },
+
+ primaryScoreRange: function () {
+ if (this.scoreSet.scoreRanges == null) {
+ return null
+ }
+
+ return Object.values(this.scoreSet.scoreRanges).filter(
+ (sr) => sr?.primary
+ )[0] || this.scoreSet.scoreRanges?.investigatorProvided || null
+ },
+
+ primaryScoreRangeIsInvestigatorProvided: function () {
+ if (this.scoreSet.scoreRanges == null) {
+ return false
+ }
+
+ return this.primaryScoreRange === this.scoreSet.scoreRanges?.investigatorProvided
+ },
+ oddsPathSources() {
+ console.log(this.primaryScoreRange)
+ return matchSources(this.primaryScoreRange?.oddsPathSource, this.sources)
+ },
+ sources: function () {
+ return this.scoreSet.primaryPublicationIdentifiers.concat(this.scoreSet.secondaryPublicationIdentifiers)
}
},
methods: {
roundOddsPath: function (oddsPath: number | undefined) {
return oddsPath?.toPrecision(5)
- }
+ },
}
})
diff --git a/src/components/RangeTable.vue b/src/components/RangeTable.vue
index 0566c376..7b9ae3d7 100644
--- a/src/components/RangeTable.vue
+++ b/src/components/RangeTable.vue
@@ -136,6 +136,10 @@ import Button from 'primevue/button'
import {defineComponent, PropType} from 'vue'
import {EVIDENCE_STRENGTHS_REVERSED, ScoreRanges, ScoreRange} from '@/lib/ranges'
+import {matchSources} from '@/lib/score-sets'
+import {components} from '@/schema/openapi'
+
+type PublicationIdentifiers = components['schemas']['ScoreSet']['primaryPublicationIdentifiers'][0]
export default defineComponent({
name: 'RangeTable',
@@ -152,7 +156,7 @@ export default defineComponent({
required: true
},
sources: {
- type: Array as PropType<{dbName: string; identifier: string; url: string}[]>,
+ type: Array as PropType
,
required: false,
default: () => []
}
@@ -198,10 +202,10 @@ export default defineComponent({
return [...this.scoreRanges.ranges].sort(this.compareScoreRanges)
},
thresholdSources() {
- return this.matchSources(this.scoreRanges.source)
+ return matchSources(this.scoreRanges.source, this.sources)
},
oddsPathSources() {
- return this.matchSources(this.scoreRanges.oddsPathSource)
+ return matchSources(this.scoreRanges.oddsPathSource, this.sources)
}
},
@@ -238,17 +242,6 @@ export default defineComponent({
.replace(/[-_]+(.)/g, (_, c) => ' ' + c.toUpperCase())
.replace(/([a-z])([A-Z])/g, '$1 $2')
},
- matchSources(
- sourceArr: Array<{dbName: string; identifier: string}> | undefined
- ): {dbName: string; identifier: string; url: string}[] | null {
- if (!Array.isArray(sourceArr) || !this.sources) return null
- const matchedSources = []
- for (const source of sourceArr) {
- const match = this.sources.find((s) => s.dbName === source.dbName && s.identifier === source.identifier)
- if (match) matchedSources.push(match)
- }
- return matchedSources.length > 0 ? matchedSources : null
- },
roundOddsPath(rangeBound: number) {
return rangeBound.toPrecision(3)
},
diff --git a/src/components/ScoreSetHistogram.vue b/src/components/ScoreSetHistogram.vue
index 7070af88..3f4a010c 100644
--- a/src/components/ScoreSetHistogram.vue
+++ b/src/components/ScoreSetHistogram.vue
@@ -117,7 +117,7 @@
@@ -251,7 +251,7 @@ export default defineComponent({
activeViz: 0,
showRanges: scoreSetHasRanges,
- activeRangeKey: null as {label: string; value: string} | null,
+ activeRangeKey: {label: 'None', value: null} as {label: string; value: string | null},
clinicalControls: [] as ClinicalControl[],
clinicalControlOptions: [] as ClinicalControlOption[],
@@ -710,7 +710,7 @@ export default defineComponent({
// Line 3: Score and classification
if (variant.score) {
let binClassificationLabel = ''
- if (bin && this.activeRangeKey && this.activeRange) {
+ if (bin && this.activeRangeKey.value && this.activeRange) {
// TODO#491: Refactor this calculation into the creation of variant objects so we may just access the property of the variant which tells us its classification.
const binClassification = this.histogramShaders[this.activeRangeKey.value]?.find(
(range: HistogramShader) => shaderOverlapsBin(range, bin)
@@ -731,7 +731,7 @@ export default defineComponent({
parts.push(`Bin range: ${bin.x0} to ${bin.x1}`)
//Line 6: Bin Classification
- if (this.activeRangeKey && this.activeRange) {
+ if (this.activeRangeKey.value && this.activeRange) {
// TODO#491: Refactor this calculation into the creation of histogram bins so we don't need to repeat it every time we construct a tooltip.
const binClassifications = this.histogramShaders[this.activeRangeKey.value]?.filter(
(range: HistogramShader) => shaderOverlapsBin(range, bin)
@@ -980,7 +980,7 @@ export default defineComponent({
// Only render clinical specific viz options if such features are enabled.
if (this.config.CLINICAL_FEATURES_ENABLED && this.showRanges) {
- this.histogram.renderShader(this.activeRangeKey?.value)
+ this.histogram.renderShader(this.activeRangeKey.value)
} else {
this.histogram.renderShader(null)
}
@@ -1101,18 +1101,18 @@ export default defineComponent({
},
defaultRangeKey: function () {
- if (this.activeRangeKey) {
+ if (this.activeRangeKey.value) {
return this.activeRangeKey
}
- const defaultInvestigatorProvidedIndex = this.activeRangeOptions.findIndex(
- (option) => option.value == 'investigatorProvided'
- )
+ const primaryRange = this.activeRanges ? Object.entries(this.activeRanges).find(([, v]) => v.primary) : null
+ const primaryRangeKey = primaryRange ? primaryRange[0] : null
- if (defaultInvestigatorProvidedIndex >= 0) {
- return this.activeRangeOptions[defaultInvestigatorProvidedIndex]
+ // use the primary range if it exists, otherwise use investigatorProvided if it exists, otherwise none.
+ if (primaryRangeKey) {
+ return this.activeRangeOptions.find((option) => option.value === primaryRangeKey) || {label: 'None', value: null}
} else if (this.activeRangeOptions.length > 0) {
- return {label: 'None', value: null} // return this.activeRangeOptions[0]
+ return this.activeRangeOptions.find((option) => option.value === "investigatorProvided") || {label: 'None', value: null}
} else {
return {label: 'None', value: null}
}
diff --git a/src/lib/ranges.ts b/src/lib/ranges.ts
index 243c1081..f8e338c9 100644
--- a/src/lib/ranges.ts
+++ b/src/lib/ranges.ts
@@ -40,13 +40,16 @@ export interface pillarProjectParameterSet {
// but we allow their implicit possibility as well.
export interface ScoreSetRanges {
investigatorProvided: ScoreRanges
- pillarProject: ScoreRanges
+ zeibergCalibration: ScoreRanges
+ scottCalibration: ScoreRanges
+ fayerCalibration: ScoreRanges
[key: string]: ScoreRanges
}
export interface ScoreRanges {
title: string
researchUseOnly: boolean
+ primary?: boolean
baselineScore?: number
baselineScoreDescription?: string | undefined
oddsPathSource?: [{ identifier: string; dbName: string }] | undefined
diff --git a/src/lib/score-sets.ts b/src/lib/score-sets.ts
index cd30b4be..9ed2d2b3 100644
--- a/src/lib/score-sets.ts
+++ b/src/lib/score-sets.ts
@@ -1,8 +1,9 @@
import _ from 'lodash'
-import type {components} from '@/schema/openapi'
+import type { components } from '@/schema/openapi'
type ScoreSet = components['schemas']['ScoreSet']
+type PublicationIdentifier = components['schemas']['ScoreSet']['primaryPublicationIdentifiers'][0]
type Author = components["schemas"]["PublicationAuthors"]
/**
@@ -51,3 +52,37 @@ export function getScoreSetShortName(scoreSet: ScoreSet): string {
const parts = [authors, gene, year?.toString()].filter((x) => x != null)
return parts.length > 0 ? parts.join(' ') : (scoreSet.title ?? scoreSet.shortDescription ?? 'Score set')
}
+
+
+
+/**
+ * Filters a collection of publication identifier objects and returns only those
+ * that match the dbName + identifier pairs provided in a source array.
+ *
+ * Each element in sourceArr is treated as a (dbName, identifier) criterion. The
+ * function searches the sources list for exact matches (both dbName and
+ * identifier must match) and returns the subset that satisfies at least one
+ * criterion. If no matches are found, or if either input is missing/invalid,
+ * null is returned.
+ *
+ * Matching preserves the original objects from the sources list (it does not
+ * clone or transform them) and maintains the order in which matching criteria
+ * appear in sourceArr (not the original order in sources).
+ *
+ *
+ * @param sourceArr An array of (dbName, identifier) pairs to match against the sources list. If not an array, the function returns null.
+ * @param sources A list of publication identifiers to search (e.g., fetched from an API). Must support Array.prototype.find; if undefined, returns null.
+ * @returns An array of matched publication identifiers (in the order of the matching criteria) or null if there are no matches or inputs are invalid.
+ */
+export function matchSources(
+ sourceArr: Array<{ dbName: string; identifier: string }> | undefined,
+ sources: PublicationIdentifier[] | undefined
+): PublicationIdentifier[] | null {
+ if (!Array.isArray(sourceArr) || !sources) return null
+ const matchedSources = []
+ for (const source of sourceArr) {
+ const match = sources.find((s) => s.dbName === source.dbName && s.identifier === source.identifier)
+ if (match) matchedSources.push(match)
+ }
+ return matchedSources.length > 0 ? matchedSources : null
+}