From 7b64e8c4462d7dc25f426214740b13c60a4de169 Mon Sep 17 00:00:00 2001 From: Colin Date: Mon, 26 Aug 2024 16:56:52 -0400 Subject: [PATCH] Add some adapters for uniprot alphafold data --- eslint.config.mjs | 2 +- .../AlphaFoldConfidenceAdapter.ts | 63 ++++++++++ .../configSchema.ts | 21 ++++ src/AlphaFoldConfidenceAdapter/index.ts | 19 +++ .../AlphaMissensePathogenicityAdapter.ts | 109 ++++++++++++++++++ .../configSchema.ts | 21 ++++ .../index.ts | 19 +++ src/index.ts | 4 + 8 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 src/AlphaFoldConfidenceAdapter/AlphaFoldConfidenceAdapter.ts create mode 100644 src/AlphaFoldConfidenceAdapter/configSchema.ts create mode 100644 src/AlphaFoldConfidenceAdapter/index.ts create mode 100644 src/AlphaMissensePathogenicityAdapter/AlphaMissensePathogenicityAdapter.ts create mode 100644 src/AlphaMissensePathogenicityAdapter/configSchema.ts create mode 100644 src/AlphaMissensePathogenicityAdapter/index.ts diff --git a/eslint.config.mjs b/eslint.config.mjs index 7d4702e..5f9f9b2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -61,7 +61,7 @@ export default tseslint.config( markers: ['/'], }, ], - + '@typescript-eslint/require-await': 'off', '@typescript-eslint/no-unsafe-argument': 'off', '@typescript-eslint/no-unsafe-return': 'off', '@typescript-eslint/no-unsafe-call': 'off', diff --git a/src/AlphaFoldConfidenceAdapter/AlphaFoldConfidenceAdapter.ts b/src/AlphaFoldConfidenceAdapter/AlphaFoldConfidenceAdapter.ts new file mode 100644 index 0000000..a1ecc01 --- /dev/null +++ b/src/AlphaFoldConfidenceAdapter/AlphaFoldConfidenceAdapter.ts @@ -0,0 +1,63 @@ +import { + BaseFeatureDataAdapter, + BaseOptions, +} from '@jbrowse/core/data_adapters/BaseAdapter' +import { openLocation } from '@jbrowse/core/util/io' +import { ObservableCreate } from '@jbrowse/core/util/rxjs' +import { + Region, + Feature, + doesIntersect2, + SimpleFeature, +} from '@jbrowse/core/util' + +export default class AlphaFoldConfidenceAdapter extends BaseFeatureDataAdapter { + public static capabilities = ['getFeatures', 'getRefNames'] + + public feats: + | Promise<{ uniqueId: string; start: number; end: number; score: number }[]> + | undefined + + private async loadDataP() { + const scores = JSON.parse( + await openLocation(this.getConf('location')).readFile('utf8'), + ) as { residueNumber: number[]; confidenceScore: number[] } + + return scores.residueNumber.map((value, idx) => ({ + uniqueId: `feat-${idx}`, + start: value, + end: value + 1, + score: scores.confidenceScore[idx]!, + })) + } + + private async loadData(_opts: BaseOptions = {}) { + if (!this.feats) { + this.feats = this.loadDataP().catch((e: unknown) => { + this.feats = undefined + throw e + }) + } + + return this.feats + } + + public async getRefNames(_opts: BaseOptions = {}) { + return [] + } + + public getFeatures(query: Region, opts: BaseOptions = {}) { + return ObservableCreate(async observer => { + const { start, end, refName } = query + const data = await this.loadData() + for (const f of data) { + if (doesIntersect2(f.start, f.end, start, end)) { + observer.next(new SimpleFeature({ ...f, refName })) + } + } + observer.complete() + }, opts.signal) + } + + public freeResources(): void {} +} diff --git a/src/AlphaFoldConfidenceAdapter/configSchema.ts b/src/AlphaFoldConfidenceAdapter/configSchema.ts new file mode 100644 index 0000000..11ac3b0 --- /dev/null +++ b/src/AlphaFoldConfidenceAdapter/configSchema.ts @@ -0,0 +1,21 @@ +import { ConfigurationSchema } from '@jbrowse/core/configuration' + +/** + * #config AlphaFoldConfidenceAdapter + */ +function x() {} // eslint-disable-line @typescript-eslint/no-unused-vars + +const AlphaFoldConfidenceAdapter = ConfigurationSchema( + 'AlphaFoldConfidenceAdapter', + { + /** + * #slot + */ + location: { + type: 'fileLocation', + defaultValue: { uri: '/path/to/my.bed.gz', locationType: 'UriLocation' }, + }, + }, + { explicitlyTyped: true }, +) +export default AlphaFoldConfidenceAdapter diff --git a/src/AlphaFoldConfidenceAdapter/index.ts b/src/AlphaFoldConfidenceAdapter/index.ts new file mode 100644 index 0000000..8812fca --- /dev/null +++ b/src/AlphaFoldConfidenceAdapter/index.ts @@ -0,0 +1,19 @@ +import PluginManager from '@jbrowse/core/PluginManager' +import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType' + +import configSchema from './configSchema' + +export default function AlphaFoldConfidenceAdapterF( + pluginManager: PluginManager, +) { + pluginManager.addAdapterType( + () => + new AdapterType({ + name: 'AlphaFoldConfidenceAdapter', + displayName: 'AlphaFoldConfidence adapter', + configSchema, + getAdapterClass: () => + import('./AlphaFoldConfidenceAdapter').then(r => r.default), + }), + ) +} diff --git a/src/AlphaMissensePathogenicityAdapter/AlphaMissensePathogenicityAdapter.ts b/src/AlphaMissensePathogenicityAdapter/AlphaMissensePathogenicityAdapter.ts new file mode 100644 index 0000000..83b80c5 --- /dev/null +++ b/src/AlphaMissensePathogenicityAdapter/AlphaMissensePathogenicityAdapter.ts @@ -0,0 +1,109 @@ +import { + BaseFeatureDataAdapter, + BaseOptions, +} from '@jbrowse/core/data_adapters/BaseAdapter' +import { openLocation } from '@jbrowse/core/util/io' +import { ObservableCreate } from '@jbrowse/core/util/rxjs' +import { + Region, + Feature, + doesIntersect2, + SimpleFeature, + max, + min, +} from '@jbrowse/core/util' + +export default class AlphaMissensePathogenicityAdapter extends BaseFeatureDataAdapter { + public static capabilities = ['getFeatures', 'getRefNames'] + + public feats: + | Promise< + { + uniqueId: string + start: number + end: number + score: number + ref: string + variant: string + am_class: string + }[] + > + | undefined + + private async loadDataP() { + const scores = await openLocation(this.getConf('location')).readFile('utf8') + return scores + .split('\n') + .slice(1) + .map(f => f.trim()) + .filter(f => !!f) + .map((row, idx) => { + const [protein_variant, score, am_class] = row.split(',') + const ref = protein_variant![0] + const variant = protein_variant!.at(-1) + const coord = protein_variant!.slice(1, -1) + return { + uniqueId: `feat-${idx}`, + ref: ref!, + variant: variant!, + start: +coord, + end: +coord + 1, + score: +score!, + am_class: am_class!, + } + }) + } + + private async loadData(_opts: BaseOptions = {}) { + if (!this.feats) { + this.feats = this.loadDataP().catch((e: unknown) => { + this.feats = undefined + throw e + }) + } + + return this.feats + } + + public async getGlobalStats(_opts?: BaseOptions) { + const data = await this.loadData() + const scoreMin = min(data.map(s => s.score)) + const scoreMax = max(data.map(s => s.score)) + return { scoreMin, scoreMax } + } + + // always render bigwig instead of calculating a feature density for it + async getMultiRegionFeatureDensityStats(_regions: Region[]) { + return { featureDensity: 0 } + } + public async getRefNames(_opts: BaseOptions = {}) { + return [] + } + + public getFeatures(query: Region, opts: BaseOptions = {}) { + return ObservableCreate(async observer => { + const { start, end, refName } = query + const data = await this.loadData() + for (const f of data) { + if (doesIntersect2(f.start, f.end, start, end)) { + observer.next(new SimpleFeature({ ...f, refName, source: f.variant })) + } + } + observer.complete() + }, opts.signal) + } + + public async getSources() { + const sources = new Set() + const data = await this.loadData() + for (const f of data) { + sources.add(f.variant) + } + return [...sources].map(s => ({ + name: s, + __name: s, + })) + } + + public freeResources(): void {} +} diff --git a/src/AlphaMissensePathogenicityAdapter/configSchema.ts b/src/AlphaMissensePathogenicityAdapter/configSchema.ts new file mode 100644 index 0000000..eff1f36 --- /dev/null +++ b/src/AlphaMissensePathogenicityAdapter/configSchema.ts @@ -0,0 +1,21 @@ +import { ConfigurationSchema } from '@jbrowse/core/configuration' + +/** + * #config AlphaMissensePathogenicityAdapter + */ +function x() {} // eslint-disable-line @typescript-eslint/no-unused-vars + +const AlphaMissensePathogenicityAdapter = ConfigurationSchema( + 'AlphaMissensePathogenicityAdapter', + { + /** + * #slot + */ + location: { + type: 'fileLocation', + defaultValue: { uri: '/path/to/my.bed.gz', locationType: 'UriLocation' }, + }, + }, + { explicitlyTyped: true }, +) +export default AlphaMissensePathogenicityAdapter diff --git a/src/AlphaMissensePathogenicityAdapter/index.ts b/src/AlphaMissensePathogenicityAdapter/index.ts new file mode 100644 index 0000000..8205ddd --- /dev/null +++ b/src/AlphaMissensePathogenicityAdapter/index.ts @@ -0,0 +1,19 @@ +import PluginManager from '@jbrowse/core/PluginManager' +import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType' + +import configSchema from './configSchema' + +export default function AlphaMissensePathogenicityAdapterF( + pluginManager: PluginManager, +) { + pluginManager.addAdapterType( + () => + new AdapterType({ + name: 'AlphaMissensePathogenicityAdapter', + displayName: 'AlphaMissensePathogenicity adapter', + configSchema, + getAdapterClass: () => + import('./AlphaMissensePathogenicityAdapter').then(r => r.default), + }), + ) +} diff --git a/src/index.ts b/src/index.ts index 9143c85..7ca1d7f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,8 @@ import { version } from '../package.json' import ProteinViewF from './ProteinView' import LaunchProteinViewF from './LaunchProteinView' import AddHighlightModelF from './AddHighlightModel' +import AlphaFoldConfidenceAdapterF from './AlphaFoldConfidenceAdapter' +import AlphaMissensePathogenicityAdapterF from './AlphaMissensePathogenicityAdapter' export default class ProteinViewer extends Plugin { name = 'ProteinViewer' @@ -15,6 +17,8 @@ export default class ProteinViewer extends Plugin { ProteinViewF(pluginManager) LaunchProteinViewF(pluginManager) AddHighlightModelF(pluginManager) + AlphaFoldConfidenceAdapterF(pluginManager) + AlphaMissensePathogenicityAdapterF(pluginManager) } configure(_pluginManager: PluginManager) {}