From a13447227ee0e985537ba5c2a8dee10401d89606 Mon Sep 17 00:00:00 2001 From: gromdimon Date: Fri, 16 Feb 2024 17:29:27 +0100 Subject: [PATCH 1/3] feat: litvar integration --- src/api/litvar/client.ts | 70 ++++++++++++++++++++++++++++++++++++++++ src/api/litvar/index.ts | 2 ++ src/api/litvar/types.ts | 2 ++ src/lib/urlConfig.ts | 2 ++ 4 files changed, 76 insertions(+) create mode 100644 src/api/litvar/client.ts create mode 100644 src/api/litvar/index.ts create mode 100644 src/api/litvar/types.ts diff --git a/src/api/litvar/client.ts b/src/api/litvar/client.ts new file mode 100644 index 0000000..44aaebc --- /dev/null +++ b/src/api/litvar/client.ts @@ -0,0 +1,70 @@ +import { urlConfig } from '../../lib/urlConfig' +import type { SearchResult } from './types' + +/** + * Client for the LitVar API. + */ +export class LitVarClient { + /** API base URL to use in this client instance. */ + private apiBaseUrl: string + + /** + * @param apiBaseUrl + * API base to the backend, excluding trailing `/`. + * The default is declared in '@/lib/urlConfig`. + */ + constructor(apiBaseUrl?: string) { + if (apiBaseUrl !== undefined || urlConfig.baseUrlLitVar !== undefined) { + // @ts-ignore + this.apiBaseUrl = apiBaseUrl ?? urlConfig.baseUrlLitVar + } else { + throw new Error('Configuration error: API base URL not configured') + } + } + + /** + * Perform search for the given HGNC symbol. + * + * @param hgncSymbol HGNC symbol to search for. + * @returns Promise for the search results. + * @throws Error if the search fails. + */ + async performSearch(hgncSymbol: string): Promise<{ [key: string]: SearchResult }> { + const url = `${this.apiBaseUrl}/search/?text=${hgncSymbol}` + const searchRes = await fetch(url, { + method: 'GET' + }) + if (!searchRes.ok) { + throw new Error(`Error running LitVar search: ${searchRes.statusText}`) + } + const searchData = await searchRes.json() + + // Then, extract PMID list and retrieve biocjson for the PMIDs + const pmids: string[] = searchData!.results!.map((doc: any) => doc.pmid) + const exportRes = await fetch( + `${this.apiBaseUrl}/publications/export/biocjson` + `?pmids=${pmids.join(',')}` + ) + if (!exportRes.ok) { + throw new Error(`Error running LitVar export: ${exportRes.statusText}`) + } + const exportDataText = await exportRes.text() + const exportDataLines = exportDataText.split(/\n/) + + // Zip search results and exports into searchResults + const searchResults: { [key: string]: SearchResult } = {} + for (const searchDataRecord of searchData.results) { + searchResults[searchDataRecord.pmid] = { + searchResult: searchDataRecord, + abstract: undefined + } + } + for (const exportDataLine of exportDataLines) { + if (exportDataLine) { + const exportData = JSON.parse(exportDataLine) + searchResults[exportData.pmid].abstract = exportData + } + } + + return searchResults + } +} \ No newline at end of file diff --git a/src/api/litvar/index.ts b/src/api/litvar/index.ts new file mode 100644 index 0000000..edfed4a --- /dev/null +++ b/src/api/litvar/index.ts @@ -0,0 +1,2 @@ +export * from './client' +export * from './types' diff --git a/src/api/litvar/types.ts b/src/api/litvar/types.ts new file mode 100644 index 0000000..8a2c0b2 --- /dev/null +++ b/src/api/litvar/types.ts @@ -0,0 +1,2 @@ +/** Interface for search result entry plus PubMed abstract. */ +export interface SearchResult {} diff --git a/src/lib/urlConfig.ts b/src/lib/urlConfig.ts index c1bedea..a60bad6 100644 --- a/src/lib/urlConfig.ts +++ b/src/lib/urlConfig.ts @@ -26,6 +26,8 @@ export interface UrlConfig { baseUrlVariantValidator?: string /** Base URL to the pubtator backend proxy. */ baseUrlPubtator?: string + /** Base URL to the litVar backend proxy. */ + baseUrlLitVar?: string } /** From 5dcfbdf3ba0a605aa5bf72fce0cfb1101f9e6450 Mon Sep 17 00:00:00 2001 From: gromdimon Date: Mon, 19 Feb 2024 10:13:25 +0100 Subject: [PATCH 2/3] wip --- src/api/litvar/client.ts | 52 +++++++++++++++--------------- src/stores/litvat/index.ts | 2 ++ src/stores/litvat/store.ts | 65 ++++++++++++++++++++++++++++++++++++++ src/stores/litvat/types.ts | 40 +++++++++++++++++++++++ 4 files changed, 133 insertions(+), 26 deletions(-) create mode 100644 src/stores/litvat/index.ts create mode 100644 src/stores/litvat/store.ts create mode 100644 src/stores/litvat/types.ts diff --git a/src/api/litvar/client.ts b/src/api/litvar/client.ts index 44aaebc..b6d5274 100644 --- a/src/api/litvar/client.ts +++ b/src/api/litvar/client.ts @@ -29,8 +29,8 @@ export class LitVarClient { * @returns Promise for the search results. * @throws Error if the search fails. */ - async performSearch(hgncSymbol: string): Promise<{ [key: string]: SearchResult }> { - const url = `${this.apiBaseUrl}/search/?text=${hgncSymbol}` + async performSearch(seqVar: string): Promise<{ [key: string]: SearchResult }> { + const url = `${this.apiBaseUrl}/${seqVar}` const searchRes = await fetch(url, { method: 'GET' }) @@ -40,31 +40,31 @@ export class LitVarClient { const searchData = await searchRes.json() // Then, extract PMID list and retrieve biocjson for the PMIDs - const pmids: string[] = searchData!.results!.map((doc: any) => doc.pmid) - const exportRes = await fetch( - `${this.apiBaseUrl}/publications/export/biocjson` + `?pmids=${pmids.join(',')}` - ) - if (!exportRes.ok) { - throw new Error(`Error running LitVar export: ${exportRes.statusText}`) - } - const exportDataText = await exportRes.text() - const exportDataLines = exportDataText.split(/\n/) + // const pmids: string[] = searchData!.results!.map((doc: any) => doc.pmid) + // const exportRes = await fetch( + // `${this.apiBaseUrl}/publications/export/biocjson` + `?pmids=${pmids.join(',')}` + // ) + // if (!exportRes.ok) { + // throw new Error(`Error running LitVar export: ${exportRes.statusText}`) + // } + // const exportDataText = await exportRes.text() + // const exportDataLines = exportDataText.split(/\n/) - // Zip search results and exports into searchResults - const searchResults: { [key: string]: SearchResult } = {} - for (const searchDataRecord of searchData.results) { - searchResults[searchDataRecord.pmid] = { - searchResult: searchDataRecord, - abstract: undefined - } - } - for (const exportDataLine of exportDataLines) { - if (exportDataLine) { - const exportData = JSON.parse(exportDataLine) - searchResults[exportData.pmid].abstract = exportData - } - } + // // Zip search results and exports into searchResults + // const searchaResults: { [key: string]: SearchResult } = {} + // for (const searchDataRecord of searchData.results) { + // searchResults[searchDataRecord.pmid] = { + // searchResult: searchDataRecord, + // abstract: undefined + // } + // } + // for (const exportDataLine of exportDataLines) { + // if (exportDataLine) { + // const exportData = JSON.parse(exportDataLine) + // searchResults[exportData.pmid].abstract = exportData + // } + // } - return searchResults + return searchData } } \ No newline at end of file diff --git a/src/stores/litvat/index.ts b/src/stores/litvat/index.ts new file mode 100644 index 0000000..9bd8e77 --- /dev/null +++ b/src/stores/litvat/index.ts @@ -0,0 +1,2 @@ +export * from './store' +export * from './types' \ No newline at end of file diff --git a/src/stores/litvat/store.ts b/src/stores/litvat/store.ts new file mode 100644 index 0000000..ffba011 --- /dev/null +++ b/src/stores/litvat/store.ts @@ -0,0 +1,65 @@ +/** + * Store wrapping the LitVar access. + */ +import { defineStore } from 'pinia' +import { ref } from 'vue' + +import { LitVarClient } from '../../api/litvar' +import { StoreState } from '../types' +import { type SearchResults } from './types' + +export const useLitVarStore = defineStore('litvar', () => { + /** The current store state. */ + const storeState = ref(StoreState.Initial) + + /** The Seqvar name currently loaded for. */ + const seqVar = ref(undefined) + + /** Detailed result information. */ + const searchResults = ref({}) + + /** Initialize the store for the given SeqVar name. */ + const initialize = async (seqVar$?: string, force: boolean = false) => { + // Skip if already loaded + if (!force && seqVar$ === seqVar.value) { + return + } + + // Clear against artifacts. + clearData() + + storeState.value = StoreState.Loading + + // Bail out if no SeqVar name is available. + if (!seqVar$) { + storeState.value = StoreState.Active + return + } + + // "Just" lookup via LitVar client. + const client = new LitVarClient() + try { + searchResults.value = await client.performSearch(seqVar$) + storeState.value = StoreState.Active + } catch (err) { + storeState.value = StoreState.Error + throw new Error(`Error running LitVar search: ${err}`) + } + } + + /** Clear the store. */ + const clearData = () => { + storeState.value = StoreState.Initial + seqVar.value = undefined + searchResults.value = {} + } + + /** The current gene Ranking. */ + return { + storeState, + seqVar, + searchResults, + initialize, + clearData + } +}) \ No newline at end of file diff --git a/src/stores/litvat/types.ts b/src/stores/litvat/types.ts new file mode 100644 index 0000000..3985da0 --- /dev/null +++ b/src/stores/litvat/types.ts @@ -0,0 +1,40 @@ +import { type SearchResult } from '../../api/litvar' + +// /** Enumeration for annotation types */ +// export enum AnnotationType { +// /** Disease */ +// Disease = 'Disease', +// /** Gene */ +// Gene = 'Gene', +// /** Chemical */ +// Chemical = 'Chemical', +// /** Species */ +// Species = 'Species', +// /** Variant */ +// Variant = 'Variant', +// /** CellLine */ +// CellLine = 'CellLine' +// } + +// /** Location of an annotation. */ +// export interface AnnotationLocation { +// /** The offset of the annotation. */ +// offset: number +// /** The length of the annotation. */ +// length: number +// } + +// /** One annotation record. */ +// export interface Annotation { +// /** The type of annotation. */ +// type: AnnotationType +// /** The name of the annotation. */ +// name?: string +// /** The text of the annotation. */ +// text?: string +// /** Locations of the annotation */ +// locations: AnnotationLocation[] +// } + +/** Search results type. */ +export type SearchResults = { [key: string]: SearchResult } From 28295c60203d05f2d138acae0a9131d66228fa19 Mon Sep 17 00:00:00 2001 From: gromdimon Date: Mon, 19 Feb 2024 10:40:01 +0100 Subject: [PATCH 3/3] component proto --- src/api/litvar/client.ts | 2 +- .../SeqvarLitvarCard/SeqvarLitvarCard.vue | 369 ++++++++++++++++++ src/stores/{litvat => litvar}/index.ts | 2 +- src/stores/{litvat => litvar}/store.ts | 11 +- src/stores/litvar/types.ts | 40 ++ src/stores/litvat/types.ts | 40 -- 6 files changed, 418 insertions(+), 46 deletions(-) create mode 100644 src/components/SeqvarLitvarCard/SeqvarLitvarCard.vue rename src/stores/{litvat => litvar}/index.ts (50%) rename src/stores/{litvat => litvar}/store.ts (84%) create mode 100644 src/stores/litvar/types.ts delete mode 100644 src/stores/litvat/types.ts diff --git a/src/api/litvar/client.ts b/src/api/litvar/client.ts index b6d5274..fba4f14 100644 --- a/src/api/litvar/client.ts +++ b/src/api/litvar/client.ts @@ -67,4 +67,4 @@ export class LitVarClient { return searchData } -} \ No newline at end of file +} diff --git a/src/components/SeqvarLitvarCard/SeqvarLitvarCard.vue b/src/components/SeqvarLitvarCard/SeqvarLitvarCard.vue new file mode 100644 index 0000000..19b74dc --- /dev/null +++ b/src/components/SeqvarLitvarCard/SeqvarLitvarCard.vue @@ -0,0 +1,369 @@ + + + + + + + diff --git a/src/stores/litvat/index.ts b/src/stores/litvar/index.ts similarity index 50% rename from src/stores/litvat/index.ts rename to src/stores/litvar/index.ts index 9bd8e77..7c17971 100644 --- a/src/stores/litvat/index.ts +++ b/src/stores/litvar/index.ts @@ -1,2 +1,2 @@ export * from './store' -export * from './types' \ No newline at end of file +export * from './types' diff --git a/src/stores/litvat/store.ts b/src/stores/litvar/store.ts similarity index 84% rename from src/stores/litvat/store.ts rename to src/stores/litvar/store.ts index ffba011..483c026 100644 --- a/src/stores/litvat/store.ts +++ b/src/stores/litvar/store.ts @@ -4,6 +4,8 @@ import { defineStore } from 'pinia' import { ref } from 'vue' +import { Seqvar } from '@/lib/genomicVars' + import { LitVarClient } from '../../api/litvar' import { StoreState } from '../types' import { type SearchResults } from './types' @@ -13,13 +15,13 @@ export const useLitVarStore = defineStore('litvar', () => { const storeState = ref(StoreState.Initial) /** The Seqvar name currently loaded for. */ - const seqVar = ref(undefined) + const seqVar = ref(undefined) /** Detailed result information. */ const searchResults = ref({}) /** Initialize the store for the given SeqVar name. */ - const initialize = async (seqVar$?: string, force: boolean = false) => { + const initialize = async (seqVar$?: Seqvar, force: boolean = false) => { // Skip if already loaded if (!force && seqVar$ === seqVar.value) { return @@ -39,7 +41,8 @@ export const useLitVarStore = defineStore('litvar', () => { // "Just" lookup via LitVar client. const client = new LitVarClient() try { - searchResults.value = await client.performSearch(seqVar$) + const seqvarName = seqVar$.toString() + searchResults.value = await client.performSearch(seqvarName) storeState.value = StoreState.Active } catch (err) { storeState.value = StoreState.Error @@ -62,4 +65,4 @@ export const useLitVarStore = defineStore('litvar', () => { initialize, clearData } -}) \ No newline at end of file +}) diff --git a/src/stores/litvar/types.ts b/src/stores/litvar/types.ts new file mode 100644 index 0000000..8dbf0b0 --- /dev/null +++ b/src/stores/litvar/types.ts @@ -0,0 +1,40 @@ +import { type SearchResult } from '../../api/litvar' + +/** Enumeration for annotation types */ +export enum AnnotationType { + /** Disease */ + Disease = 'Disease', + /** Gene */ + Gene = 'Gene', + /** Chemical */ + Chemical = 'Chemical', + /** Species */ + Species = 'Species', + /** Variant */ + Variant = 'Variant', + /** CellLine */ + CellLine = 'CellLine' +} + +/** Location of an annotation. */ +export interface AnnotationLocation { + /** The offset of the annotation. */ + offset: number + /** The length of the annotation. */ + length: number +} + +/** One annotation record. */ +export interface Annotation { + /** The type of annotation. */ + type: AnnotationType + /** The name of the annotation. */ + name?: string + /** The text of the annotation. */ + text?: string + /** Locations of the annotation */ + locations: AnnotationLocation[] +} + +/** Search results type. */ +export type SearchResults = { [key: string]: SearchResult } diff --git a/src/stores/litvat/types.ts b/src/stores/litvat/types.ts deleted file mode 100644 index 3985da0..0000000 --- a/src/stores/litvat/types.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { type SearchResult } from '../../api/litvar' - -// /** Enumeration for annotation types */ -// export enum AnnotationType { -// /** Disease */ -// Disease = 'Disease', -// /** Gene */ -// Gene = 'Gene', -// /** Chemical */ -// Chemical = 'Chemical', -// /** Species */ -// Species = 'Species', -// /** Variant */ -// Variant = 'Variant', -// /** CellLine */ -// CellLine = 'CellLine' -// } - -// /** Location of an annotation. */ -// export interface AnnotationLocation { -// /** The offset of the annotation. */ -// offset: number -// /** The length of the annotation. */ -// length: number -// } - -// /** One annotation record. */ -// export interface Annotation { -// /** The type of annotation. */ -// type: AnnotationType -// /** The name of the annotation. */ -// name?: string -// /** The text of the annotation. */ -// text?: string -// /** Locations of the annotation */ -// locations: AnnotationLocation[] -// } - -/** Search results type. */ -export type SearchResults = { [key: string]: SearchResult }