From 4fdc459046aff7d52f0d69d0afc7c95f3d581d2a Mon Sep 17 00:00:00 2001 From: Manuel Holtgrewe Date: Wed, 13 Sep 2023 10:52:05 +0200 Subject: [PATCH] fix: adding back default columns (#1127) --- cases/vueapp/src/common.ts | 96 +++++++++++++++++++ .../components/CaseDetail/PaneAnnotations.vue | 11 --- variants/vueapp/src/api/variantClient.ts | 2 +- variants/vueapp/src/components/FilterApp.vue | 40 ++------ .../src/components/FilterResultsTable.vue | 17 +--- .../vueapp/src/stores/variantResultSet.ts | 46 ++++++++- 6 files changed, 155 insertions(+), 57 deletions(-) create mode 100644 cases/vueapp/src/common.ts diff --git a/cases/vueapp/src/common.ts b/cases/vueapp/src/common.ts new file mode 100644 index 000000000..f4a3f2cb9 --- /dev/null +++ b/cases/vueapp/src/common.ts @@ -0,0 +1,96 @@ +import { + useCaseDetailsStore, + CaseVariantStatsEntry, +} from '@cases/stores/caseDetails' +import { useCaseListStore } from '@cases/stores/caseList' +import { State } from '@varfish/storeUtils' +import { computed } from 'vue' + +export const overlayShow = computed(() => { + const caseListStore = useCaseListStore() + const caseDetailsStore = useCaseDetailsStore() + return ( + (caseListStore?.storeState?.serverInteractions ?? 0) > 0 || + (caseDetailsStore?.storeState?.serverInteractions ?? 0) > 0 + ) +}) + +export const overlayMessage = computed(() => { + const caseListStore = useCaseListStore() + switch (caseListStore.storeState.state) { + case State.Initial: + return 'initializing...' + case State.Fetching: + return 'communication with server...' + case State.Active: + return 'all data has been loaded successfully' + case State.Error: + return 'an error occured' + default: + console.error('unknown store state', caseListStore.storeState.state) + return 'UNKNOWN STATE' + } +}) + +export const tsTvRatio = (entry: CaseVariantStatsEntry): number => { + if (!entry.ontarget_transversions) { + return 0.0 + } else { + return entry.ontarget_transitions / entry.ontarget_transversions + } +} + +export const downloadFile = ( + filename: string, + contents: any, + mimeType: string = 'application/octet-stream', +) => { + const element = document.createElement('a') + element.setAttribute('href', 'data:' + mimeType + ';base64,' + btoa(contents)) + element.setAttribute('download', filename) + element.style.display = 'none' + + document.body.appendChild(element) + element.click() + document.body.removeChild(element) +} + +export const downloadPerSampleMetrics = (varStats: CaseVariantStatsEntry[]) => { + const result = [ + ['Sample', 'Ts', 'Tv', 'Ts/Tv', 'SNVs', 'InDels', 'MNVs', 'X hom./het.'], + ] + for (const entry of varStats) { + result.push( + [ + entry.sample_name, + entry.ontarget_transitions, + entry.ontarget_transversions, + tsTvRatio(entry), + entry.ontarget_snvs, + entry.ontarget_indels, + entry.ontarget_mnvs, + entry.chrx_het_hom, + ].map((x) => x.toString()), + ) + } + const text = result.map((row) => row.map(String).join('\t')).join('\n') + downloadFile('per-sample-metrics.tsv', text) +} + +export interface Relatedness { + sample0: string + sample1: string + ibs0: number + ibs1: number + rel: number +} + +export const downloadRelatedness = (relData: Relatedness[]) => { + const result = [['Sample 1', 'Sample 2', 'IBS0', 'Relatedness']] + for (const entry of relData) { + const { sample0, sample1, ibs0, rel } = entry + result.push([sample0, sample1, ibs0, rel].map((x) => x.toString())) + } + const text = result.map((row) => row.map(String).join('\t')).join('\n') + downloadFile('relatedness.tsv', text) +} diff --git a/cases/vueapp/src/components/CaseDetail/PaneAnnotations.vue b/cases/vueapp/src/components/CaseDetail/PaneAnnotations.vue index 5365dc818..9632cc4d1 100644 --- a/cases/vueapp/src/components/CaseDetail/PaneAnnotations.vue +++ b/cases/vueapp/src/components/CaseDetail/PaneAnnotations.vue @@ -20,17 +20,6 @@ const props = defineProps({ caseUuid: String, }) -const caseDetailsStore = useCaseDetailsStore() - -/** The details columns to show. */ -const displayDetails = ref(DisplayDetails.Coordinates.value) -/** The frequency columns to show. */ -const displayFrequency = ref(DisplayFrequencies.GnomadExomes.value) -/** The constraint columns to show. */ -const displayConstraint = ref(DisplayConstraints.GnomadPli.value) -/** The additional columns to display. */ -const displayColumns = ref([DisplayColumns.Effect.value]) - const showSmallVariantDetails = async (event) => { router.push({ name: 'variant-details', diff --git a/variants/vueapp/src/api/variantClient.ts b/variants/vueapp/src/api/variantClient.ts index 99652c987..e285caf0c 100644 --- a/variants/vueapp/src/api/variantClient.ts +++ b/variants/vueapp/src/api/variantClient.ts @@ -335,7 +335,7 @@ export class VariantClient extends ClientBase { ) } - async fetchExtraAnnoFields(csrfToken: string) { + async fetchExtraAnnoFields() { return await this.fetchHelper(`/variants/ajax/extra-anno-fields/`, 'GET') } diff --git a/variants/vueapp/src/components/FilterApp.vue b/variants/vueapp/src/components/FilterApp.vue index 88e3ba3a2..6fa8906c2 100644 --- a/variants/vueapp/src/components/FilterApp.vue +++ b/variants/vueapp/src/components/FilterApp.vue @@ -78,7 +78,7 @@ const displayConstraint = ref( const displayColumns = ref( variantResultSetStore.displayColumns === null ? [DisplayColumns.Effect.value] - : variantResultSetStore.displayColumns.split(','), + : variantResultSetStore.displayColumns, ) // Toggle visibility of the form. @@ -160,37 +160,13 @@ const refreshStores = async () => { appContext.project?.sodar_uuid, caseDetailsStore.caseObj.sodar_uuid, ), - variantQueryStore - .initialize( - appContext.csrf_token, - appContext?.project?.sodar_uuid, - props.caseUuid, - appContext, - ) - .then(() => { - // Show the SpliceAI comments by default. - if (!props.displayColumns) { - const maybeAdd = [ - 'SpliceAI-acc-gain', - 'SpliceAI-acc-loss', - 'SpliceAI-don-loss', - 'SpliceAI-don-gain', - 'CADD-PHRED', - ] - variantQueryStore.extraAnnoFields - .filter((value) => maybeAdd.includes(value.label)) - .forEach((value) => { - displayColumns.value.push(`extra_anno${value.field}`) - }) - } - variantResultSetStore - .initialize(appContext.csrf_token) - .then(async () => { - await variantResultSetStore.loadResultSetViaQuery( - variantQueryStore.queryUuid, - ) - }) - }), + variantQueryStore.initialize( + appContext.csrf_token, + appContext?.project?.sodar_uuid, + props.caseUuid, + appContext, + ), + variantResultSetStore.initialize(appContext.csrf_token), ]) } diff --git a/variants/vueapp/src/components/FilterResultsTable.vue b/variants/vueapp/src/components/FilterResultsTable.vue index 2c3cf18da..50d46d52f 100644 --- a/variants/vueapp/src/components/FilterResultsTable.vue +++ b/variants/vueapp/src/components/FilterResultsTable.vue @@ -56,8 +56,6 @@ const commentsStore = useVariantCommentsStore() const acmgRatingStore = useVariantAcmgRatingStore() const variantResultSetStore = useVariantResultSetStore() -const router = useRouter() - /** The details columns to show. */ const displayDetails = computed({ get() { @@ -99,7 +97,7 @@ const displayConstraint = computed({ /** The additional columns to display. */ const displayColumns = computed({ get() { - return variantResultSetStore.displayColumns || [DisplayColumns.Effect.value] + return variantResultSetStore.displayColumns ?? [DisplayColumns.Effect.value] }, set(newValue) { variantResultSetStore.displayColumns = newValue @@ -146,9 +144,6 @@ const coordinatesClinvarColumns = () => { const optionalColumns = () => { const optionalColumnTexts = copy(DisplayColumnsToText) - if (!extraAnnoFields.value) { - return [] - } for (const { field, label } of extraAnnoFields.value) { optionalColumnTexts[`extra_anno${field}`] = label } @@ -464,14 +459,12 @@ const loadFromServer = async () => { } } -const extraAnnoFields = ref() +const extraAnnoFields = computed( + () => variantResultSetStore.extraAnnoFields ?? [], +) /** Load data when mounted. */ onBeforeMount(async () => { - const variantClient = new VariantClient(caseDetailsStore.csrfToken) - extraAnnoFields.value = await variantClient.fetchExtraAnnoFields( - caseDetailsStore.csrfToken, - ) if (variantResultSetStore.resultSetUuid) { await loadFromServer() } @@ -504,11 +497,11 @@ watch( diff --git a/variants/vueapp/src/stores/variantResultSet.ts b/variants/vueapp/src/stores/variantResultSet.ts index 9f41037c7..83eddfab1 100644 --- a/variants/vueapp/src/stores/variantResultSet.ts +++ b/variants/vueapp/src/stores/variantResultSet.ts @@ -7,6 +7,7 @@ import { ref, reactive } from 'vue' import { StoreState, State } from '@varfish/storeUtils' import { VariantClient } from '@variants/api/variantClient' +import { DisplayColumns } from '@variants/enums' export const useVariantResultSetStore = defineStore('variantResultSet', () => { // no store dependencies @@ -19,6 +20,8 @@ export const useVariantResultSetStore = defineStore('variantResultSet', () => { const caseUuid = ref(null) /** The current application state. */ const storeState = reactive(new StoreState()) + /** Extra annotation fields available. */ + const extraAnnoFields = ref(null) // other data (loaded via REST API or computed) @@ -56,8 +59,19 @@ export const useVariantResultSetStore = defineStore('variantResultSet', () => { storeState.serverInteractions = 0 storeState.message = null + resultRow.value = null resultSetUuid.value = null resultSet.value = null + query.value = null + + tablePageNo.value = null + tablePageSize.value = null + tableSortBy.value = null + tableSortType.value = null + displayDetails.value = null + displayFrequency.value = null + displayConstraint.value = null + displayColumns.value = null } /** @@ -66,7 +80,10 @@ export const useVariantResultSetStore = defineStore('variantResultSet', () => { * @param csrfToken$ CSRF token to use. * @param forceReload Whether to force reload. */ - const initialize = (csrfToken$: string, forceReload: boolean = false) => { + const initialize = async ( + csrfToken$: string, + forceReload: boolean = false, + ) => { // Initialize only once. if (!forceReload && storeState.state !== State.Initial) { return initializeRes.value @@ -74,6 +91,12 @@ export const useVariantResultSetStore = defineStore('variantResultSet', () => { $reset() + // Load extra annotation fields, if necessary. + if (!extraAnnoFields.value) { + const variantClient = new VariantClient(csrfToken$) + extraAnnoFields.value = await variantClient.fetchExtraAnnoFields() + } + // Set simple properties. csrfToken.value = csrfToken$ @@ -84,6 +107,22 @@ export const useVariantResultSetStore = defineStore('variantResultSet', () => { return initializeRes.value } + const refreshDisplayColumns = () => { + const maybeAdd = [ + 'SpliceAI-acc-gain', + 'SpliceAI-acc-loss', + 'SpliceAI-don-loss', + 'SpliceAI-don-gain', + 'CADD-PHRED', + ] + displayColumns.value = [DisplayColumns.Effect.value] + extraAnnoFields.value + .filter((value) => maybeAdd.includes(value.label)) + .forEach((value) => { + displayColumns.value.push(`extra_anno${value.field}`) + }) + } + const loadResultSetViaQuery = async (queryUuid$: string) => { // Once query is finished, load results, if still for the same query. const variantClient = new VariantClient(csrfToken.value) @@ -96,6 +135,7 @@ export const useVariantResultSetStore = defineStore('variantResultSet', () => { // Still fetching the same query; push to query result set. resultSet.value = responseResultSetList[0] resultSetUuid.value = responseResultSetList[0].sodar_uuid + refreshDisplayColumns() } } @@ -106,6 +146,7 @@ export const useVariantResultSetStore = defineStore('variantResultSet', () => { if (case$.smallvariantqueryresultset) { resultSet.value = case$.smallvariantqueryresultset resultSetUuid.value = case$.smallvariantqueryresultset.sodar_uuid + refreshDisplayColumns() } else { console.error('ERROR: no result set in case response') } @@ -142,6 +183,8 @@ export const useVariantResultSetStore = defineStore('variantResultSet', () => { ) query.value = query$ caseUuid.value = query$.case + + refreshDisplayColumns() } else { // Otherwise, it really should provide us with a Case UUID. if (!resultSet$.case) { @@ -167,6 +210,7 @@ export const useVariantResultSetStore = defineStore('variantResultSet', () => { // data / state csrfToken, storeState, + extraAnnoFields, resultRow, resultSetUuid, resultSet,