jbrowse-plugin-protein3d 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AddHighlightModel/GenomeTo1DProteinHoverHighlight.js +5 -2
- package/dist/AddHighlightModel/ProteinToMsaHoverSync.js +4 -0
- package/dist/AlphaFoldConfidenceAdapter/AlphaFoldConfidenceAdapter.d.ts +17 -17
- package/dist/AlphaFoldConfidenceAdapter/AlphaFoldConfidenceAdapter.js +18 -37
- package/dist/AlphaMissensePathogenicityAdapter/AlphaMissensePathogenicityAdapter.d.ts +16 -21
- package/dist/AlphaMissensePathogenicityAdapter/AlphaMissensePathogenicityAdapter.js +21 -60
- package/dist/BaseProteinAnnotationAdapter.d.ts +29 -0
- package/dist/BaseProteinAnnotationAdapter.js +41 -0
- package/dist/LaunchProteinView/components/AlphaFoldDBSearch.js +10 -2
- package/dist/LaunchProteinView/components/FoldseekSearch.js +11 -0
- package/dist/LaunchProteinView/hooks/useAlphaFoldDBSearch.d.ts +3 -0
- package/dist/LaunchProteinView/hooks/useAlphaFoldDBSearch.js +25 -1
- package/dist/LaunchProteinView/hooks/useUniProtSearch.d.ts +2 -1
- package/dist/LaunchProteinView/hooks/useUniProtSearch.js +4 -1
- package/dist/LaunchProteinView/services/lookupMethods.js +1 -1
- package/dist/LaunchProteinView/utils/launchViewUtils.d.ts +2 -9
- package/dist/LaunchProteinView/utils/launchViewUtils.js +9 -51
- package/dist/LaunchProteinView/utils/structureUrls.d.ts +8 -0
- package/dist/LaunchProteinView/utils/structureUrls.js +48 -0
- package/dist/LaunchProteinView/utils/util.d.ts +8 -0
- package/dist/LaunchProteinView/utils/util.js +18 -0
- package/dist/LaunchProteinViewExtensionPoint/index.js +4 -5
- package/dist/Protein1DViewRegistry/index.d.ts +6 -1
- package/dist/Protein1DViewRegistry/index.js +16 -23
- package/dist/ProteinView/components/AddStructureDialog.js +1 -1
- package/dist/ProteinView/components/FeatureBar.js +14 -24
- package/dist/ProteinView/hooks/useProteinFeatureTrackData.d.ts +12 -0
- package/dist/ProteinView/hooks/useProteinFeatureTrackData.js +8 -0
- package/dist/ProteinView/model.d.ts +12 -38
- package/dist/ProteinView/model.js +31 -82
- package/dist/ProteinView/proteinViewSpec.d.ts +76 -0
- package/dist/ProteinView/proteinViewSpec.js +16 -0
- package/dist/ProteinView/structureModel.d.ts +3 -1
- package/dist/ProteinView/structureModel.js +47 -10
- package/dist/ProteinView/structureSuperposer.d.ts +30 -0
- package/dist/ProteinView/structureSuperposer.js +53 -0
- package/dist/UniProtVariationAdapter/UniProtVariationAdapter.d.ts +77 -13
- package/dist/UniProtVariationAdapter/UniProtVariationAdapter.js +28 -48
- package/dist/jbrowse-plugin-protein3d.umd.production.min.js +15 -15
- package/dist/jbrowse-plugin-protein3d.umd.production.min.js.map +4 -4
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/AddHighlightModel/GenomeTo1DProteinHoverHighlight.tsx +6 -2
- package/src/AddHighlightModel/ProteinToMsaHoverSync.tsx +3 -0
- package/src/AddHighlightModel/msaHoverSyncGuard.test.ts +14 -14
- package/src/AlphaFoldConfidenceAdapter/AlphaFoldConfidenceAdapter.ts +32 -50
- package/src/AlphaFoldConfidenceAdapter/parseAlphaFoldConfidence.test.ts +26 -0
- package/src/AlphaMissensePathogenicityAdapter/AlphaMissensePathogenicityAdapter.ts +30 -74
- package/src/AlphaMissensePathogenicityAdapter/parseAlphaMissense.test.ts +9 -4
- package/src/BaseProteinAnnotationAdapter.ts +70 -0
- package/src/LaunchProteinView/components/AlphaFoldDBSearch.tsx +30 -7
- package/src/LaunchProteinView/components/FoldseekSearch.tsx +12 -0
- package/src/LaunchProteinView/hooks/useAlphaFoldDBSearch.ts +33 -1
- package/src/LaunchProteinView/hooks/useUniProtSearch.ts +7 -0
- package/src/LaunchProteinView/services/lookupMethods.ts +1 -1
- package/src/LaunchProteinView/utils/extractTaxonId.test.ts +28 -0
- package/src/LaunchProteinView/utils/launchViewUtils.ts +29 -70
- package/src/LaunchProteinView/utils/structureUrls.ts +66 -0
- package/src/LaunchProteinView/utils/util.ts +20 -0
- package/src/LaunchProteinViewExtensionPoint/index.ts +28 -23
- package/src/Protein1DViewRegistry/index.ts +20 -35
- package/src/ProteinView/components/AddStructureDialog.tsx +1 -1
- package/src/ProteinView/components/FeatureBar.tsx +14 -26
- package/src/ProteinView/hooks/useProteinFeatureTrackData.ts +12 -0
- package/src/ProteinView/model.ts +48 -117
- package/src/ProteinView/proteinViewSpec.test.ts +48 -0
- package/src/ProteinView/proteinViewSpec.ts +65 -0
- package/src/ProteinView/structureModel.test.ts +48 -0
- package/src/ProteinView/structureModel.ts +56 -17
- package/src/ProteinView/structureSuperposer.test.ts +108 -0
- package/src/ProteinView/structureSuperposer.ts +70 -0
- package/src/UniProtVariationAdapter/UniProtVariationAdapter.ts +33 -62
- package/src/UniProtVariationAdapter/parseUniProtVariants.test.ts +42 -0
- package/src/version.ts +1 -1
|
@@ -20,11 +20,14 @@ const GenomeTo1DProteinHoverHighlight = observer(function GenomeTo1DProteinHover
|
|
|
20
20
|
if (!checkHovered(hovered)) {
|
|
21
21
|
return null;
|
|
22
22
|
}
|
|
23
|
-
const { coord } = hovered.hoverPosition;
|
|
23
|
+
const { coord, refName } = hovered.hoverPosition;
|
|
24
24
|
const feature = new SimpleFeature(protein1DInfo.feature);
|
|
25
25
|
const mapping = genomeToTranscriptSeqMapping(feature);
|
|
26
26
|
const { g2p } = mapping;
|
|
27
|
-
|
|
27
|
+
// g2p is keyed by genomic position on the transcript's own refName; without
|
|
28
|
+
// this gate the same numeric coord on an unrelated chromosome would match a
|
|
29
|
+
// key and light a protein residue for a different locus.
|
|
30
|
+
const proteinPos = refName === mapping.refName ? g2p[coord - 1] : undefined;
|
|
28
31
|
if (proteinPos === undefined) {
|
|
29
32
|
return null;
|
|
30
33
|
}
|
|
@@ -43,6 +43,10 @@ const ProteinToMsaHoverSync = observer(function ProteinToMsaHoverSync({ model, }
|
|
|
43
43
|
: seqPos;
|
|
44
44
|
setMousePos(col);
|
|
45
45
|
}
|
|
46
|
+
else {
|
|
47
|
+
// structure went away (e.g. removed); clear the stale MSA hover
|
|
48
|
+
setMousePos(undefined);
|
|
49
|
+
}
|
|
46
50
|
}));
|
|
47
51
|
}
|
|
48
52
|
disposers.push(autorun(() => {
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import type { Observable } from 'rxjs';
|
|
5
|
-
export default class AlphaFoldConfidenceAdapter extends BaseFeatureDataAdapter {
|
|
6
|
-
static capabilities: string[];
|
|
7
|
-
feats: Promise<{
|
|
8
|
-
uniqueId: string;
|
|
9
|
-
start: number;
|
|
10
|
-
end: number;
|
|
11
|
-
score: number;
|
|
12
|
-
}[]> | undefined;
|
|
13
|
-
private loadDataP;
|
|
14
|
-
private loadData;
|
|
15
|
-
getRefNames(_opts?: BaseOptions): Promise<never[]>;
|
|
16
|
-
getFeatures(query: Region, _opts?: BaseOptions): Observable<Feature>;
|
|
17
|
-
freeResources(): void;
|
|
1
|
+
import { BaseProteinAnnotationAdapter, type ProteinAnnotationRow } from '../BaseProteinAnnotationAdapter';
|
|
2
|
+
export interface AlphaFoldConfidenceRow extends ProteinAnnotationRow {
|
|
3
|
+
score: number;
|
|
18
4
|
}
|
|
5
|
+
interface AlphaFoldConfidenceJson {
|
|
6
|
+
residueNumber: number[];
|
|
7
|
+
confidenceScore: number[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Converts AlphaFold confidence JSON to features. residueNumber is 1-based, so
|
|
11
|
+
* residue n becomes the 0-based half-open interval [n-1, n) to line up with the
|
|
12
|
+
* interbase protein reference sequence.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseAlphaFoldConfidence(json: AlphaFoldConfidenceJson): AlphaFoldConfidenceRow[];
|
|
15
|
+
export default class AlphaFoldConfidenceAdapter extends BaseProteinAnnotationAdapter<AlphaFoldConfidenceRow> {
|
|
16
|
+
protected loadFeatures(): Promise<AlphaFoldConfidenceRow[]>;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -1,40 +1,21 @@
|
|
|
1
|
-
import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
2
|
-
import { SimpleFeature, doesIntersect2 } from '@jbrowse/core/util';
|
|
3
1
|
import { openLocation } from '@jbrowse/core/util/io';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return this.feats;
|
|
23
|
-
}
|
|
24
|
-
async getRefNames(_opts = {}) {
|
|
25
|
-
return [];
|
|
26
|
-
}
|
|
27
|
-
getFeatures(query, _opts = {}) {
|
|
28
|
-
return ObservableCreate(async (observer) => {
|
|
29
|
-
const { start, end, refName } = query;
|
|
30
|
-
const data = await this.loadData();
|
|
31
|
-
for (const f of data) {
|
|
32
|
-
if (doesIntersect2(f.start, f.end, start, end)) {
|
|
33
|
-
observer.next(new SimpleFeature({ ...f, refName }));
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
observer.complete();
|
|
37
|
-
});
|
|
2
|
+
import { BaseProteinAnnotationAdapter, } from '../BaseProteinAnnotationAdapter';
|
|
3
|
+
/**
|
|
4
|
+
* Converts AlphaFold confidence JSON to features. residueNumber is 1-based, so
|
|
5
|
+
* residue n becomes the 0-based half-open interval [n-1, n) to line up with the
|
|
6
|
+
* interbase protein reference sequence.
|
|
7
|
+
*/
|
|
8
|
+
export function parseAlphaFoldConfidence(json) {
|
|
9
|
+
return json.residueNumber.map((residue, idx) => ({
|
|
10
|
+
uniqueId: `feat-${idx}`,
|
|
11
|
+
start: residue - 1,
|
|
12
|
+
end: residue,
|
|
13
|
+
score: json.confidenceScore[idx],
|
|
14
|
+
}));
|
|
15
|
+
}
|
|
16
|
+
export default class AlphaFoldConfidenceAdapter extends BaseProteinAnnotationAdapter {
|
|
17
|
+
async loadFeatures() {
|
|
18
|
+
const json = JSON.parse(await openLocation(this.getConf('location')).readFile('utf8'));
|
|
19
|
+
return parseAlphaFoldConfidence(json);
|
|
38
20
|
}
|
|
39
|
-
freeResources() { }
|
|
40
21
|
}
|
|
@@ -1,28 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BaseProteinAnnotationAdapter, type ProteinAnnotationRow } from '../BaseProteinAnnotationAdapter';
|
|
2
2
|
import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
export interface AlphaMissenseRow {
|
|
6
|
-
uniqueId: string;
|
|
7
|
-
start: number;
|
|
8
|
-
end: number;
|
|
3
|
+
import type { Region } from '@jbrowse/core/util';
|
|
4
|
+
export interface AlphaMissenseRow extends ProteinAnnotationRow {
|
|
9
5
|
score: number;
|
|
10
6
|
ref: string;
|
|
11
7
|
variant: string;
|
|
12
8
|
am_class: string;
|
|
13
9
|
}
|
|
14
|
-
/**
|
|
15
|
-
* Parses AlphaMissense CSV text (protein_variant,score,am_class). The
|
|
16
|
-
* protein_variant column looks like "V123L": a ref AA, a 1-based residue
|
|
17
|
-
* coordinate, and a variant AA. Rows that don't parse to a numeric coordinate
|
|
18
|
-
* are skipped rather than emitted as bogus position-0 features.
|
|
19
|
-
*/
|
|
20
10
|
export declare function parseAlphaMissense(text: string): AlphaMissenseRow[];
|
|
21
|
-
export default class AlphaMissensePathogenicityAdapter extends
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
11
|
+
export default class AlphaMissensePathogenicityAdapter extends BaseProteinAnnotationAdapter<AlphaMissenseRow> {
|
|
12
|
+
protected loadFeatures(): Promise<AlphaMissenseRow[]>;
|
|
13
|
+
protected featureData(row: AlphaMissenseRow, refName: string): {
|
|
14
|
+
refName: string;
|
|
15
|
+
source: string;
|
|
16
|
+
score: number;
|
|
17
|
+
ref: string;
|
|
18
|
+
variant: string;
|
|
19
|
+
am_class: string;
|
|
20
|
+
uniqueId: string;
|
|
21
|
+
start: number;
|
|
22
|
+
end: number;
|
|
23
|
+
};
|
|
26
24
|
getGlobalStats(_opts?: BaseOptions): Promise<{
|
|
27
25
|
scoreMin: number;
|
|
28
26
|
scoreMax: number;
|
|
@@ -30,11 +28,8 @@ export default class AlphaMissensePathogenicityAdapter extends BaseFeatureDataAd
|
|
|
30
28
|
getMultiRegionFeatureDensityStats(_regions: Region[]): Promise<{
|
|
31
29
|
featureDensity: number;
|
|
32
30
|
}>;
|
|
33
|
-
getRefNames(_opts?: BaseOptions): Promise<never[]>;
|
|
34
|
-
getFeatures(query: Region, _opts?: BaseOptions): Observable<Feature>;
|
|
35
31
|
getSources(): Promise<{
|
|
36
32
|
name: string;
|
|
37
33
|
__name: string;
|
|
38
34
|
}[]>;
|
|
39
|
-
freeResources(): void;
|
|
40
35
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { SimpleFeature, doesIntersect2, max, min } from '@jbrowse/core/util';
|
|
1
|
+
import { max, min } from '@jbrowse/core/util';
|
|
3
2
|
import { openLocation } from '@jbrowse/core/util/io';
|
|
4
|
-
import {
|
|
3
|
+
import { BaseProteinAnnotationAdapter, } from '../BaseProteinAnnotationAdapter';
|
|
5
4
|
/**
|
|
6
5
|
* Parses AlphaMissense CSV text (protein_variant,score,am_class). The
|
|
7
6
|
* protein_variant column looks like "V123L": a ref AA, a 1-based residue
|
|
8
|
-
* coordinate, and a variant AA
|
|
9
|
-
*
|
|
7
|
+
* coordinate, and a variant AA, converted here to a 0-based half-open interval
|
|
8
|
+
* to match the interbase protein reference sequence. Rows that don't match the
|
|
9
|
+
* ref/coord/variant shape are skipped rather than emitted as bogus features (a
|
|
10
|
+
* short string like "VL" would otherwise parse to a position-0 feature).
|
|
10
11
|
*/
|
|
12
|
+
const VARIANT_RE = /^([A-Za-z])(\d+)([A-Za-z])$/;
|
|
11
13
|
export function parseAlphaMissense(text) {
|
|
12
14
|
return text
|
|
13
15
|
.split('\n')
|
|
@@ -16,21 +18,15 @@ export function parseAlphaMissense(text) {
|
|
|
16
18
|
.filter(f => !!f)
|
|
17
19
|
.flatMap((row, idx) => {
|
|
18
20
|
const [protein_variant = '', score, am_class] = row.split(',');
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
const coord = +protein_variant.slice(1, -1);
|
|
22
|
-
return ref !== undefined &&
|
|
23
|
-
variant !== undefined &&
|
|
24
|
-
!Number.isNaN(coord) &&
|
|
25
|
-
score !== undefined &&
|
|
26
|
-
am_class !== undefined
|
|
21
|
+
const match = VARIANT_RE.exec(protein_variant);
|
|
22
|
+
return match && score !== undefined && am_class !== undefined
|
|
27
23
|
? [
|
|
28
24
|
{
|
|
29
25
|
uniqueId: `feat-${idx}`,
|
|
30
|
-
ref,
|
|
31
|
-
variant,
|
|
32
|
-
start:
|
|
33
|
-
end:
|
|
26
|
+
ref: match[1],
|
|
27
|
+
variant: match[3],
|
|
28
|
+
start: +match[2] - 1,
|
|
29
|
+
end: +match[2],
|
|
34
30
|
score: +score,
|
|
35
31
|
am_class,
|
|
36
32
|
},
|
|
@@ -38,58 +34,23 @@ export function parseAlphaMissense(text) {
|
|
|
38
34
|
: [];
|
|
39
35
|
});
|
|
40
36
|
}
|
|
41
|
-
export default class AlphaMissensePathogenicityAdapter extends
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
async loadDataP() {
|
|
45
|
-
const scores = await openLocation(this.getConf('location')).readFile('utf8');
|
|
46
|
-
return parseAlphaMissense(scores);
|
|
37
|
+
export default class AlphaMissensePathogenicityAdapter extends BaseProteinAnnotationAdapter {
|
|
38
|
+
async loadFeatures() {
|
|
39
|
+
return parseAlphaMissense(await openLocation(this.getConf('location')).readFile('utf8'));
|
|
47
40
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
this.feats = undefined;
|
|
51
|
-
throw e;
|
|
52
|
-
});
|
|
53
|
-
return this.feats;
|
|
41
|
+
featureData(row, refName) {
|
|
42
|
+
return { ...row, refName, source: row.variant };
|
|
54
43
|
}
|
|
55
44
|
async getGlobalStats(_opts) {
|
|
56
|
-
const
|
|
57
|
-
const scores = data.map(s => s.score);
|
|
45
|
+
const scores = (await this.loadData()).map(s => s.score);
|
|
58
46
|
return { scoreMin: min(scores), scoreMax: max(scores) };
|
|
59
47
|
}
|
|
60
48
|
// always render bigwig instead of calculating a feature density for it
|
|
61
49
|
async getMultiRegionFeatureDensityStats(_regions) {
|
|
62
50
|
return { featureDensity: 0 };
|
|
63
51
|
}
|
|
64
|
-
async getRefNames(_opts = {}) {
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
getFeatures(query, _opts = {}) {
|
|
68
|
-
return ObservableCreate(async (observer) => {
|
|
69
|
-
const { start, end, refName } = query;
|
|
70
|
-
const data = await this.loadData();
|
|
71
|
-
for (const f of data) {
|
|
72
|
-
if (doesIntersect2(f.start, f.end, start, end)) {
|
|
73
|
-
observer.next(new SimpleFeature({
|
|
74
|
-
...f,
|
|
75
|
-
refName,
|
|
76
|
-
source: f.variant,
|
|
77
|
-
}));
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
observer.complete();
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
52
|
async getSources() {
|
|
84
|
-
const sources = new Set();
|
|
85
|
-
|
|
86
|
-
for (const f of data) {
|
|
87
|
-
sources.add(f.variant);
|
|
88
|
-
}
|
|
89
|
-
return [...sources].map(s => ({
|
|
90
|
-
name: s,
|
|
91
|
-
__name: s,
|
|
92
|
-
}));
|
|
53
|
+
const sources = new Set((await this.loadData()).map(f => f.variant));
|
|
54
|
+
return [...sources].map(s => ({ name: s, __name: s }));
|
|
93
55
|
}
|
|
94
|
-
freeResources() { }
|
|
95
56
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
2
|
+
import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
3
|
+
import type { Feature, Region, SimpleFeatureSerialized } from '@jbrowse/core/util';
|
|
4
|
+
import type { Observable } from 'rxjs';
|
|
5
|
+
export interface ProteinAnnotationRow {
|
|
6
|
+
[key: string]: unknown;
|
|
7
|
+
uniqueId: string;
|
|
8
|
+
start: number;
|
|
9
|
+
end: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Shared plumbing for the protein-annotation adapters (AlphaFold confidence,
|
|
13
|
+
* AlphaMissense, UniProt variation). Each lives on the temporary protein
|
|
14
|
+
* assembly, exposes no ref names, caches its parsed rows once, and emits the
|
|
15
|
+
* rows that intersect a query region. Subclasses supply the parsing
|
|
16
|
+
* (loadFeatures) and may decorate the emitted feature (featureData).
|
|
17
|
+
*/
|
|
18
|
+
export declare abstract class BaseProteinAnnotationAdapter<T extends ProteinAnnotationRow> extends BaseFeatureDataAdapter {
|
|
19
|
+
static capabilities: string[];
|
|
20
|
+
private feats;
|
|
21
|
+
/** Parse the configured source into rows. */
|
|
22
|
+
protected abstract loadFeatures(): Promise<T[]>;
|
|
23
|
+
/** Fields for the emitted feature; override to add extras (e.g. `source`). */
|
|
24
|
+
protected featureData(row: T, refName: string): SimpleFeatureSerialized;
|
|
25
|
+
protected loadData(): Promise<T[]>;
|
|
26
|
+
getRefNames(_opts?: BaseOptions): Promise<never[]>;
|
|
27
|
+
getFeatures(query: Region, _opts?: BaseOptions): Observable<Feature>;
|
|
28
|
+
freeResources(): void;
|
|
29
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
2
|
+
import { SimpleFeature, doesIntersect2 } from '@jbrowse/core/util';
|
|
3
|
+
import { ObservableCreate } from '@jbrowse/core/util/rxjs';
|
|
4
|
+
/**
|
|
5
|
+
* Shared plumbing for the protein-annotation adapters (AlphaFold confidence,
|
|
6
|
+
* AlphaMissense, UniProt variation). Each lives on the temporary protein
|
|
7
|
+
* assembly, exposes no ref names, caches its parsed rows once, and emits the
|
|
8
|
+
* rows that intersect a query region. Subclasses supply the parsing
|
|
9
|
+
* (loadFeatures) and may decorate the emitted feature (featureData).
|
|
10
|
+
*/
|
|
11
|
+
export class BaseProteinAnnotationAdapter extends BaseFeatureDataAdapter {
|
|
12
|
+
static capabilities = ['getFeatures', 'getRefNames'];
|
|
13
|
+
feats;
|
|
14
|
+
/** Fields for the emitted feature; override to add extras (e.g. `source`). */
|
|
15
|
+
featureData(row, refName) {
|
|
16
|
+
return { ...row, refName };
|
|
17
|
+
}
|
|
18
|
+
// Parse once and cache; a failed parse clears the cache so it can retry.
|
|
19
|
+
loadData() {
|
|
20
|
+
this.feats ??= this.loadFeatures().catch((e) => {
|
|
21
|
+
this.feats = undefined;
|
|
22
|
+
throw e;
|
|
23
|
+
});
|
|
24
|
+
return this.feats;
|
|
25
|
+
}
|
|
26
|
+
async getRefNames(_opts = {}) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
getFeatures(query, _opts = {}) {
|
|
30
|
+
return ObservableCreate(async (observer) => {
|
|
31
|
+
const { start, end, refName } = query;
|
|
32
|
+
for (const f of await this.loadData()) {
|
|
33
|
+
if (doesIntersect2(f.start, f.end, start, end)) {
|
|
34
|
+
observer.next(new SimpleFeature(this.featureData(f, refName)));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
observer.complete();
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
freeResources() { }
|
|
41
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui';
|
|
3
|
-
import { DialogActions, DialogContent, Typography } from '@mui/material';
|
|
3
|
+
import { DialogActions, DialogContent, TextField, Typography, } from '@mui/material';
|
|
4
4
|
import { observer } from 'mobx-react';
|
|
5
5
|
import { makeStyles } from 'tss-react/mui';
|
|
6
6
|
import AlphaFoldDBSearchStatus from './AlphaFoldDBSearchStatus';
|
|
@@ -28,6 +28,12 @@ const useStyles = makeStyles()({
|
|
|
28
28
|
gap: 20,
|
|
29
29
|
alignItems: 'flex-start',
|
|
30
30
|
},
|
|
31
|
+
endRow: {
|
|
32
|
+
display: 'flex',
|
|
33
|
+
flexDirection: 'row',
|
|
34
|
+
gap: 12,
|
|
35
|
+
alignItems: 'flex-start',
|
|
36
|
+
},
|
|
31
37
|
});
|
|
32
38
|
const AlphaFoldDBSearch = observer(function AlphaFoldDBSearch({ feature, session, view, handleClose, alignmentAlgorithm, onAlignmentAlgorithmChange, }) {
|
|
33
39
|
const { classes } = useStyles();
|
|
@@ -35,7 +41,9 @@ const AlphaFoldDBSearch = observer(function AlphaFoldDBSearch({ feature, session
|
|
|
35
41
|
return (React.createElement(React.Fragment, null,
|
|
36
42
|
React.createElement(DialogContent, { className: classes.dialogContent },
|
|
37
43
|
state.error ? React.createElement(ErrorMessage, { error: state.error }) : null,
|
|
38
|
-
React.createElement(UniProtIdInput, { lookupMode: state.lookupMode, onLookupModeChange: state.setLookupMode, manualUniprotId: state.manualUniprotId, onManualUniprotIdChange: state.setManualUniprotId, featureUniprotId: state.featureUniprotId, hasProteinSequence: !!state.userSelectedProteinSequence?.seq, sequenceSearchType: state.sequenceSearchType, onSequenceSearchTypeChange: state.setSequenceSearchType, endContent: state.showIdentifierSelector ? (React.createElement(
|
|
44
|
+
React.createElement(UniProtIdInput, { lookupMode: state.lookupMode, onLookupModeChange: state.setLookupMode, manualUniprotId: state.manualUniprotId, onManualUniprotIdChange: state.setManualUniprotId, featureUniprotId: state.featureUniprotId, hasProteinSequence: !!state.userSelectedProteinSequence?.seq, sequenceSearchType: state.sequenceSearchType, onSequenceSearchTypeChange: state.setSequenceSearchType, endContent: state.showIdentifierSelector ? (React.createElement("div", { className: classes.endRow },
|
|
45
|
+
React.createElement(IdentifierSelector, { recognizedIds: state.recognizedIds, geneName: state.geneName, selectedId: state.selectedQueryId, onSelectedIdChange: state.setSelectedQueryId }),
|
|
46
|
+
React.createElement(TextField, { size: "small", label: "Organism (NCBI taxon)", helperText: "Scopes the gene-name search", value: state.taxonId, onChange: event => { state.setTaxonId(event.target.value); }, placeholder: String(state.effectiveTaxonId), slotProps: { inputLabel: { shrink: true } }, sx: { width: 180 } }))) : null }),
|
|
39
47
|
state.loadingStatuses.map(status => (React.createElement(LoadingEllipses, { key: status, variant: "subtitle2", message: status }))),
|
|
40
48
|
state.showUniprotResults && (React.createElement(React.Fragment, null,
|
|
41
49
|
React.createElement(Typography, { variant: "body2", color: "textSecondary" },
|
|
@@ -36,9 +36,19 @@ const FoldseekSearch = observer(function FoldseekSearch({ feature, session, view
|
|
|
36
36
|
? stripStopCodon(selectedIsoformData.seq)
|
|
37
37
|
: '';
|
|
38
38
|
const sequence = userEditedSequence ?? cleanedSequence;
|
|
39
|
+
// Any change to the input sequence makes an existing 3Di prediction (and any
|
|
40
|
+
// results derived from it) stale. Clearing it returns the UI to the Predict
|
|
41
|
+
// step so a search can't silently run against the previously-predicted
|
|
42
|
+
// sequence after the user switches transcript or edits the residues.
|
|
43
|
+
const invalidatePrediction = () => {
|
|
44
|
+
if (di3Sequence !== undefined || results !== undefined) {
|
|
45
|
+
reset();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
39
48
|
const setUserSelectionWithReset = (id) => {
|
|
40
49
|
setUserSelection(id);
|
|
41
50
|
setUserEditedSequence(undefined);
|
|
51
|
+
invalidatePrediction();
|
|
42
52
|
};
|
|
43
53
|
const canPredict = sequence.trim().length > 0 && !isPredicting && !isLoading;
|
|
44
54
|
const canSearch = !!cleanedAaSequence &&
|
|
@@ -55,6 +65,7 @@ const FoldseekSearch = observer(function FoldseekSearch({ feature, session, view
|
|
|
55
65
|
React.createElement(TranscriptSelector, { val: effectiveSelectedTranscriptId, setVal: setUserSelectionWithReset, isoforms: transcripts, isoformSequences: isoformSequences, feature: feature, disabled: isBusy }),
|
|
56
66
|
React.createElement(TextField, { label: "Protein sequence (amino acids)", multiline: true, rows: 4, value: sequence, onChange: e => {
|
|
57
67
|
setUserEditedSequence(e.target.value);
|
|
68
|
+
invalidatePrediction();
|
|
58
69
|
}, placeholder: `MKTVRQERLKSIVRILERSKEPVSGAQLAEEL...`, disabled: isBusy, InputProps: {
|
|
59
70
|
className: classes.sequenceInput,
|
|
60
71
|
} }))) : null,
|
|
@@ -10,6 +10,9 @@ export default function useAlphaFoldDBSearch({ feature, view, }: {
|
|
|
10
10
|
setLookupMode: import("react").Dispatch<import("react").SetStateAction<LookupMode>>;
|
|
11
11
|
manualUniprotId: string;
|
|
12
12
|
setManualUniprotId: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
13
|
+
taxonId: string;
|
|
14
|
+
setTaxonId: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
15
|
+
effectiveTaxonId: number;
|
|
13
16
|
selectedQueryId: string;
|
|
14
17
|
setSelectedQueryId: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
15
18
|
sequenceSearchType: SequenceSearchType;
|
|
@@ -1,15 +1,34 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
|
+
import { getConf } from '@jbrowse/core/configuration';
|
|
3
|
+
import { getSession } from '@jbrowse/core/util';
|
|
2
4
|
import useAlphaFoldData from './useAlphaFoldData';
|
|
3
5
|
import useAlphaFoldSequenceSearch from './useAlphaFoldSequenceSearch';
|
|
4
6
|
import useDebouncedValue from './useDebouncedValue';
|
|
5
7
|
import useTranscriptIsoformSelection from './useTranscriptIsoformSelection';
|
|
6
8
|
import useUniProtSearch from './useUniProtSearch';
|
|
7
9
|
import getSearchDescription from '../utils/getSearchDescription';
|
|
8
|
-
import { extractFeatureIdentifiers, stripStopCodon } from '../utils/util';
|
|
10
|
+
import { extractFeatureIdentifiers, extractTaxonId, stripStopCodon, } from '../utils/util';
|
|
9
11
|
export default function useAlphaFoldDBSearch({ feature, view, }) {
|
|
10
12
|
const [lookupMode, setLookupMode] = useState('auto');
|
|
11
13
|
const [manualUniprotId, setManualUniprotId] = useState('');
|
|
14
|
+
const [taxonIdInput, setTaxonIdInput] = useState('');
|
|
12
15
|
const geneIds = extractFeatureIdentifiers(feature);
|
|
16
|
+
// The gene-name UniProt search is ambiguous across species, so scope it to
|
|
17
|
+
// the assembly's organism. jb2hubs assemblies carry the NCBI taxon in the
|
|
18
|
+
// reference-sequence track metadata (UCSC: metadata.taxId, GenArk:
|
|
19
|
+
// metadata.ucsc.taxId). Falls back to human via searchUniProtEntries when
|
|
20
|
+
// absent; a user override (taxonIdInput) always wins.
|
|
21
|
+
const assemblyName = view.assemblyNames[0];
|
|
22
|
+
const assembly = assemblyName
|
|
23
|
+
? getSession(view).assemblyManager.get(assemblyName)
|
|
24
|
+
: undefined;
|
|
25
|
+
const assemblyTaxonId = assembly
|
|
26
|
+
? extractTaxonId(getConf(assembly, ['sequence', 'metadata']))
|
|
27
|
+
: undefined;
|
|
28
|
+
const overrideTaxon = Number(taxonIdInput.trim());
|
|
29
|
+
const effectiveTaxonId = taxonIdInput.trim() !== '' && Number.isFinite(overrideTaxon) && overrideTaxon > 0
|
|
30
|
+
? overrideTaxon
|
|
31
|
+
: assemblyTaxonId;
|
|
13
32
|
const [selectedQueryId, setSelectedQueryId] = useState('auto');
|
|
14
33
|
const [sequenceSearchType, setSequenceSearchType] = useState('md5');
|
|
15
34
|
const [selectedUniprotId, setSelectedUniprotId] = useState();
|
|
@@ -21,6 +40,7 @@ export default function useAlphaFoldDBSearch({ feature, view, }) {
|
|
|
21
40
|
recognizedIds: geneIds.recognizedIds,
|
|
22
41
|
geneId: geneIds.geneId,
|
|
23
42
|
geneName: geneIds.geneName,
|
|
43
|
+
organismId: effectiveTaxonId,
|
|
24
44
|
selectedQueryId,
|
|
25
45
|
enabled: isAutoMode,
|
|
26
46
|
});
|
|
@@ -84,6 +104,10 @@ export default function useAlphaFoldDBSearch({ feature, view, }) {
|
|
|
84
104
|
setLookupMode,
|
|
85
105
|
manualUniprotId,
|
|
86
106
|
setManualUniprotId,
|
|
107
|
+
taxonId: taxonIdInput,
|
|
108
|
+
setTaxonId: setTaxonIdInput,
|
|
109
|
+
// shown as the field placeholder so the user sees the organism in effect
|
|
110
|
+
effectiveTaxonId: effectiveTaxonId ?? 9606,
|
|
87
111
|
selectedQueryId,
|
|
88
112
|
setSelectedQueryId,
|
|
89
113
|
sequenceSearchType,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { UniProtEntry } from '../services/lookupMethods';
|
|
2
|
-
export default function useUniProtSearch({ recognizedIds, geneId, geneName, selectedQueryId, enabled, }: {
|
|
2
|
+
export default function useUniProtSearch({ recognizedIds, geneId, geneName, organismId, selectedQueryId, enabled, }: {
|
|
3
3
|
recognizedIds?: string[];
|
|
4
4
|
geneId?: string;
|
|
5
5
|
geneName?: string;
|
|
6
|
+
organismId?: number;
|
|
6
7
|
selectedQueryId?: string;
|
|
7
8
|
enabled?: boolean;
|
|
8
9
|
}): {
|
|
@@ -2,7 +2,7 @@ import useSWR from 'swr';
|
|
|
2
2
|
import { STATIC_SWR_OPTIONS } from './swrOptions';
|
|
3
3
|
import { searchUniProtEntries } from '../services/lookupMethods';
|
|
4
4
|
import { isRecognizedDatabaseId } from '../utils/util';
|
|
5
|
-
export default function useUniProtSearch({ recognizedIds = [], geneId, geneName, selectedQueryId = 'auto', enabled = true, }) {
|
|
5
|
+
export default function useUniProtSearch({ recognizedIds = [], geneId, geneName, organismId, selectedQueryId = 'auto', enabled = true, }) {
|
|
6
6
|
// Determine what to search based on selectedQueryId
|
|
7
7
|
let idsToSearch = [];
|
|
8
8
|
let geneNameToSearch;
|
|
@@ -24,11 +24,14 @@ export default function useUniProtSearch({ recognizedIds = [], geneId, geneName,
|
|
|
24
24
|
selectedQueryId,
|
|
25
25
|
idsToSearch.join(','),
|
|
26
26
|
geneNameToSearch,
|
|
27
|
+
geneId,
|
|
28
|
+
organismId,
|
|
27
29
|
]
|
|
28
30
|
: null, async () => searchUniProtEntries({
|
|
29
31
|
recognizedIds: idsToSearch,
|
|
30
32
|
geneId,
|
|
31
33
|
geneName: geneNameToSearch,
|
|
34
|
+
organismId,
|
|
32
35
|
}), {
|
|
33
36
|
...STATIC_SWR_OPTIONS,
|
|
34
37
|
keepPreviousData: true,
|
|
@@ -53,7 +53,7 @@ export async function searchUniProtEntries({ recognizedIds = [], geneId, geneNam
|
|
|
53
53
|
let geneNameError;
|
|
54
54
|
if (!entries.some(e => e.isReviewed) && geneName) {
|
|
55
55
|
try {
|
|
56
|
-
const query = `gene:${geneName}
|
|
56
|
+
const query = `gene:${geneName} AND organism_id:${organismId} AND reviewed:true`;
|
|
57
57
|
const geneNameResults = await searchUniProt(query, 5);
|
|
58
58
|
entries = deduplicateEntries([...entries, ...geneNameResults]);
|
|
59
59
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ALPHAFOLD_VERSION, getAlphaFoldConfidenceUrl, getAlphaFoldMsaUrl, getAlphaFoldStructureUrl, getConfidenceUrlFromTarget, getPdbStructureUrl, getStructureUrlFromTarget, getUniprotIdFromAlphaFoldTarget } from './structureUrls';
|
|
2
|
+
export { ALPHAFOLD_VERSION, getAlphaFoldConfidenceUrl, getAlphaFoldMsaUrl, getAlphaFoldStructureUrl, getConfidenceUrlFromTarget, getPdbStructureUrl, getStructureUrlFromTarget, getUniprotIdFromAlphaFoldTarget, };
|
|
1
3
|
import type { AbstractSessionModel, Feature, SessionWithAddTracks } from '@jbrowse/core/util';
|
|
2
4
|
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view';
|
|
3
5
|
declare global {
|
|
@@ -5,14 +7,6 @@ declare global {
|
|
|
5
7
|
JBrowsePluginMsaView?: unknown;
|
|
6
8
|
}
|
|
7
9
|
}
|
|
8
|
-
export declare const ALPHAFOLD_VERSION = "v6";
|
|
9
|
-
export declare function getAlphaFoldStructureUrl(uniprotId: string, version?: string): string;
|
|
10
|
-
export declare function getAlphaFoldConfidenceUrl(uniprotId: string, version?: string): string;
|
|
11
|
-
export declare function getAlphaFoldMsaUrl(uniprotId: string, version?: string): string;
|
|
12
|
-
export declare function getPdbStructureUrl(pdbId: string): string;
|
|
13
|
-
export declare function getUniprotIdFromAlphaFoldTarget(target: string): string | undefined;
|
|
14
|
-
export declare function getStructureUrlFromTarget(target: string, db: string): string | undefined;
|
|
15
|
-
export declare function getConfidenceUrlFromTarget(target: string): string | undefined;
|
|
16
10
|
interface LaunchViewParams {
|
|
17
11
|
session: AbstractSessionModel;
|
|
18
12
|
view: LinearGenomeViewModel;
|
|
@@ -48,4 +42,3 @@ export declare function getConditionalProteinLaunches({ session, view, feature,
|
|
|
48
42
|
launchMsa: (() => import("@jbrowse/core/util").AbstractViewModel | undefined) | undefined;
|
|
49
43
|
};
|
|
50
44
|
export declare function launch3DProteinViewWithMsa(params: LaunchViewParams & Launch3DExtraParams): import("@jbrowse/core/util").AbstractViewModel | undefined;
|
|
51
|
-
export {};
|