Skip to content

Commit

Permalink
feat: implement SV type INS on the library level (#146)
Browse files Browse the repository at this point in the history
  • Loading branch information
holtgrewe authored Feb 23, 2024
1 parent 6cbb057 commit 5125223
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 24 deletions.
9 changes: 6 additions & 3 deletions src/api/annonars/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,14 @@ export class AnnonarsClient {
pageSize: number = 1000,
minOverlap: number = 0.1
): Promise<ClinvarSvQueryResponse> {
const { svType, genomeBuild, chrom, start, stop } = strucvar
if (svType === 'BND') {
// We don't properly support ClinVar BNDs (and there will be few..)
// First, check if we can handle the SV type.
const { svType } = strucvar
if (svType === 'BND' || svType === 'INS') {
// We don't properly support ClinVar INS/BNDs (and there will be few..)
return { records: [] }
}
// Then, extract coordinates of linear strucvar.
const { genomeBuild, chrom, start, stop } = strucvar

const url =
`${this.apiBaseUrl}/clinvar-sv/query?genomeRelease=${genomeBuild}&` +
Expand Down
16 changes: 12 additions & 4 deletions src/api/mehari/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,18 @@ export class MehariClient {
* @throws Error if the API request fails.
*/
async retrieveStrucvarsCsq(strucvar: Strucvar): Promise<StrucvarResult> {
const { genomeBuild, chrom, start, stop, svType } = strucvar
const url =
`${this.apiBaseUrl}/strucvars/csq?genome_release=${genomeBuild}&` +
`chromosome=${chrom}&start=${start}&stop=${stop}&sv_type=${svType}`
let url: string
const { svType, genomeBuild, chrom, start } = strucvar
if (svType === 'BND' || svType === 'INS') {
url =
`${this.apiBaseUrl}/strucvars/csq?genome_release=${genomeBuild}&` +
`chromosome=${chrom}&start=${start}&stop=${start + 1}&sv_type=${svType}`
} else {
const { stop } = strucvar
url =
`${this.apiBaseUrl}/strucvars/csq?genome_release=${genomeBuild}&` +
`chromosome=${chrom}&start=${start}&stop=${stop}&sv_type=${svType}`
}

const response = await fetch(url, {
method: 'GET'
Expand Down
9 changes: 7 additions & 2 deletions src/components/StrucvarClinvarCard/StrucvarClinvarCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,13 @@ const clinvarRange = computed<string>(() => {
return ''
}
const release = props.strucvar.genomeBuild === 'grch37' ? 'GRCh37' : 'GRCh38'
const { chrom, start, stop } = props.strucvar
return `${release}:${chrom.replace('chr', '')}:${start}-${stop}`
const { svType, chrom, start } = props.strucvar
if (svType === 'BND' || svType === 'INS') {
return `${release}:${chrom.replace('chr', '')}:${start}-${start}`
} else {
const { stop } = props.strucvar
return `${release}:${chrom.replace('chr', '')}:${start}-${stop}`
}
})

const sortBy = ref<{ key: string; order: 'asc' | 'desc' }[]>([{ key: 'overlap', order: 'desc' }])
Expand Down
37 changes: 24 additions & 13 deletions src/components/StrucvarToolsCard/StrucvarToolsCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,34 @@ const props = defineProps<{
strucvar?: Strucvar
}>()
const svStop = (strucvar: Strucvar): number => {
return strucvar.svType === 'INS' || strucvar.svType === 'BND' ? strucvar.start + 1 : strucvar.stop
}
const ucscLinkout = computed((): string => {
if (!props.strucvar) {
return '#'
}
const db = props.strucvar?.genomeBuild === 'grch37' ? 'hg19' : 'hg38'
return (
`https://genome-euro.ucsc.edu/cgi-bin/hgTracks?db=${db}&position=` +
`${props.strucvar.chrom}:${props.strucvar.start}-` +
`${props.strucvar.stop}`
)
if (props.strucvar.svType === 'BND' || props.strucvar.svType === 'INS') {
return (
`https://genome-euro.ucsc.edu/cgi-bin/hgTracks?db=${db}&position=` +
`${props.strucvar.chrom}:${props.strucvar.start}-${svStop(props.strucvar)}`
)
} else {
return (
`https://genome-euro.ucsc.edu/cgi-bin/hgTracks?db=${db}&position=` +
`${props.strucvar.chrom}:${props.strucvar.start}-${svStop(props.strucvar)}`
)
}
})
const ensemblLinkout = computed((): string => {
if (!props.strucvar) {
return '#'
}
const loc = `${props.strucvar.chrom}:${props.strucvar.start}-${props.strucvar.stop}`
if (props.strucvar?.genomeBuild === 'grch37') {
const loc = `${props.strucvar.chrom}:${props.strucvar.start}-${svStop(props.strucvar)}`
if (props.strucvar.genomeBuild === 'grch37') {
return `https://grch37.ensembl.org/Homo_sapiens/Location/View?r=${loc}`
} else if (props.strucvar?.genomeBuild === 'grch38') {
return `https://ensembl.org/Homo_sapiens/Location/View?r=${loc}`
Expand All @@ -39,9 +49,10 @@ const dgvLinkout = computed((): string => {
return '#'
}
const db = props.strucvar?.genomeBuild === 'grch37' ? 'hg19' : 'hg38'
return (
`http://dgv.tcag.ca/gb2/gbrowse/dgv2_${db}/?name=${props.strucvar.chrom}:` +
`${props.strucvar.start}-${props.strucvar.stop};search=Search`
`${props.strucvar.start}-${svStop(props.strucvar)};search=Search`
)
})
Expand All @@ -52,7 +63,7 @@ const gnomadLinkout = computed((): string => {
const dataset = props.strucvar?.genomeBuild === 'grch37' ? 'gnomad_r2_1' : 'gnomad_r3'
return (
`https://gnomad.broadinstitute.org/region/${props.strucvar.chrom}:` +
`${props.strucvar.start}-${props.strucvar.stop}?dataset=${dataset}`
`${props.strucvar.start}-${svStop(props.strucvar)}?dataset=${dataset}`
)
})
Expand All @@ -64,16 +75,16 @@ const varsomeLinkout = computed((): string => {
const chrom = props.strucvar.chrom.startsWith('chr')
? props.strucvar.chrom
: `chr${props.strucvar.chrom}`
return `https://varsome.com/cnv/${urlRelease}/${chrom}:${props.strucvar.start}:${props.strucvar.stop}:${props.strucvar.svType}`
return `https://varsome.com/cnv/${urlRelease}/${chrom}:${props.strucvar.start}:${svStop(props.strucvar)}:${props.strucvar.svType}`
})
const franklinLinkout = computed((): string => {
if (!props.strucvar) {
return '#'
}
const { chrom, start, stop, svType } = props.strucvar
const { chrom, start, svType } = props.strucvar
const urlRelease = props.strucvar?.genomeBuild === 'grch37' ? 'hg19' : 'hg38'
return `https://franklin.genoox.com/clinical-db/variant/sv/chr${chrom}-${start}-${stop}-${svType}-${urlRelease}`
return `https://franklin.genoox.com/clinical-db/variant/sv/chr${chrom}-${start}-${svStop(props.strucvar)}-${svType}-${urlRelease}`
})
const jumpToLocus = async () => {
Expand All @@ -82,7 +93,7 @@ const jumpToLocus = async () => {
: `chr${props.strucvar?.chrom}`
// NB: we allow the call to fetch here as it goes to local IGV.
await fetch(
`http://127.0.0.1:60151/goto?locus=${chrPrefixed}:${props.strucvar?.start}-${props.strucvar?.stop}`
`http://127.0.0.1:60151/goto?locus=${chrPrefixed}:${props.strucvar?.start}-${svStop(props.strucvar!)}`
).catch((e) => {
const msg = "Couldn't connect to IGV. Please make sure IGV is running and try again."
alert(msg)
Expand Down
90 changes: 88 additions & 2 deletions src/lib/genomicVars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const REGEX_CNV_HYPHEN =
export type CnvType = 'DEL' | 'DUP'

/** Currently supported SV types. */
export type SvType = CnvType | 'INV' | 'BND'
export type SvType = CnvType | 'INV' | 'INS' | 'BND'

/** Interface for regex groups when parsing with `REGEX_CNV_COLON` or `REGEX_CNV_HYPHEN`. */
export interface RegexCnvGroups {
Expand Down Expand Up @@ -414,6 +414,92 @@ export enum StrandOrientation {
NOT_APPLICABLE = 'NtoN'
}

/**
* Interface that describes a insertion strucvar.
*/
export interface InsertionStrucvar {
/** The type of the SV. */
svType: 'INS'
/** The genome build. */
genomeBuild: GenomeBuild
/**
* Canonical chromomsome name "1", .., "22", "X", "Y", "MT"
* **without** `chr` prefix.
*/
chrom: string
/** The 1-based position. */
start: number
/** The inserted sequence. */
insSeq?: string
/** The length of the inserted sequence. */
insLen?: number
/** The user-facing representation. */
userRepr: string
}

/**
* Implementation of the `InsertionStrucvar` interface.
*/
export class InsertionStrucvarImpl implements InsertionStrucvar {
svType: 'INS'
genomeBuild: GenomeBuild
chrom: string
start: number
insSeq?: string
insLen?: number
userRepr: string

constructor(
genomeBuild: GenomeBuild,
chrom: string,
start: number,
insSeq?: string,
insLen?: number,
userRepr?: string
) {
this.svType = 'INS'
this.genomeBuild = genomeBuild
this.chrom = chrom
this.start = start
this.insSeq = insSeq
this.insLen = insLen
let tmp = `${this.svType}-${this.genomeBuild}-${this.chrom}-${this.start}`
if (insSeq !== undefined) {
tmp += `-${insSeq}`
} else if (insLen !== undefined) {
tmp += `-${insLen}`
}
this.userRepr = userRepr ?? tmp
}

/** Return the "object name" to be used in the API to the backend etc. */
toName(): string {
let tmp = `${this.svType}-${this.genomeBuild}-${this.chrom}-${this.start}`
if (this.insSeq !== undefined) {
tmp += `-${this.insSeq}`
} else if (this.insLen !== undefined) {
tmp += `-${this.insLen}`
}
return tmp
}
}

/**
* Construct a `InsertionStrucvarImpl` from a `InsertionStrucvar`.
*/
export function insertionStrucvarImplFromInsertionStrucvar(
ins: InsertionStrucvar
): InsertionStrucvarImpl {
return new InsertionStrucvarImpl(
ins.genomeBuild,
ins.chrom,
ins.start,
ins.insSeq,
ins.insLen,
ins.userRepr
)
}

/**
* Interface that describes a breakend.
*/
Expand Down Expand Up @@ -501,7 +587,7 @@ export function breakendStrucvarImplFromBreakendStrucvar(
}

/** All supported structural variant types. */
export type Strucvar = LinearStrucvar | BreakendStrucvar
export type Strucvar = LinearStrucvar | InsertionStrucvar | BreakendStrucvar

/**
* Attempt parsing of a colon/hyphen-separated structural variant.
Expand Down

0 comments on commit 5125223

Please sign in to comment.