jbrowse-plugin-msaview 2.8.0 → 2.8.2
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/LaunchMsaViewExtensionPoint/index.js +11 -4
- package/dist/MsaViewPanel/doLaunchOrthologs.js +44 -21
- package/dist/MsaViewPanel/model.d.ts +18 -7
- package/dist/jbrowse-plugin-msaview.umd.production.min.js +26 -26
- package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
- package/dist/utils/ncbiOrthologs.js +8 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/src/LaunchMsaViewExtensionPoint/index.ts +31 -4
- package/src/MsaViewPanel/doLaunchOrthologs.ts +49 -20
- package/src/MsaViewPanel/model.ts +14 -3
- package/src/utils/ncbiOrthologs.ts +8 -2
- package/src/version.ts +1 -1
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
export default function LaunchMsaViewExtensionPointF(pluginManager) {
|
|
2
2
|
pluginManager.addToExtensionPoint('LaunchView-MsaView', (args) => {
|
|
3
3
|
const { session, data, msaFileLocation, msaIndexedLocation, msaName, treeFileLocation, querySeqName, ...rest } = args;
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
// `orthologParams` is a fourth source, and unlike the other three it names
|
|
5
|
+
// no alignment at all — the view builds one from NCBI at launch, which is
|
|
6
|
+
// the dialog's Orthologs tab reached declaratively.
|
|
7
|
+
if (!data &&
|
|
8
|
+
!msaFileLocation &&
|
|
9
|
+
!msaIndexedLocation &&
|
|
10
|
+
!rest.orthologParams) {
|
|
11
|
+
throw new Error('No MSA data, file location or orthologParams provided when launching MSA view');
|
|
6
12
|
}
|
|
7
13
|
// inline data and the tree URL are native react-msaview snapshot props, set
|
|
8
|
-
// directly
|
|
9
|
-
//
|
|
14
|
+
// directly, and so is orthologParams (the model's own autorun picks it up).
|
|
15
|
+
// Only sources needing launch-time resolution go through `init`: msaUrl
|
|
16
|
+
// (AlphaFold sniff) and the name-indexed bgzip block (no native loader).
|
|
10
17
|
session.addView('MsaView', {
|
|
11
18
|
type: 'MsaView',
|
|
12
19
|
...rest,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cleanProteinSequence } from '../LaunchMsaView/util';
|
|
2
2
|
import { launchMSA } from '../utils/msa';
|
|
3
|
-
import { fetchOrthologRows, fetchProteinForGene, resolveGeneId, } from '../utils/ncbiOrthologs';
|
|
3
|
+
import { COMMON_SPECIES, fetchOrthologRows, fetchProteinForGene, resolveGeneId, } from '../utils/ncbiOrthologs';
|
|
4
4
|
/**
|
|
5
5
|
* The no-search-job alternative to doLaunchBlast.
|
|
6
6
|
*
|
|
@@ -18,7 +18,6 @@ import { fetchOrthologRows, fetchProteinForGene, resolveGeneId, } from '../utils
|
|
|
18
18
|
*/
|
|
19
19
|
export async function doLaunchOrthologs({ self, }) {
|
|
20
20
|
const { taxId, taxa, geneCandidates, msaAlgorithm, proteinSequence } = self.orthologParams;
|
|
21
|
-
const cleanedSeq = cleanProteinSequence(proteinSequence);
|
|
22
21
|
const onProgress = (arg) => {
|
|
23
22
|
self.setProgress(arg);
|
|
24
23
|
};
|
|
@@ -27,15 +26,33 @@ export async function doLaunchOrthologs({ self, }) {
|
|
|
27
26
|
if (!resolved) {
|
|
28
27
|
throw new Error(`Could not resolve any of ${geneCandidates.join(', ')} to an NCBI gene in taxon ${taxId}. Try the NCBI BLAST tab, which needs no gene identifier.`);
|
|
29
28
|
}
|
|
30
|
-
//
|
|
31
|
-
|
|
29
|
+
// The query row. The dialog always supplies it — it is the user's OWN
|
|
30
|
+
// selected transcript, which is what makes `connectedFeature` map genome
|
|
31
|
+
// coordinates through this row. A launch that has no transcript to translate
|
|
32
|
+
// (a session spec naming only a gene) falls back to NCBI's representative
|
|
33
|
+
// protein for the resolved gene, which is the same choice made for every
|
|
34
|
+
// other row, so the alignment is the one NCBI would build for that gene.
|
|
35
|
+
const representative = await fetchRepresentativeQueryProtein(resolved.geneId);
|
|
36
|
+
const cleanedSeq = proteinSequence
|
|
37
|
+
? cleanProteinSequence(proteinSequence)
|
|
38
|
+
: representative?.sequence;
|
|
39
|
+
if (!cleanedSeq) {
|
|
40
|
+
throw new Error(`No query protein: none was supplied and NCBI returned no representative protein for gene ${resolved.geneId}.`);
|
|
41
|
+
}
|
|
42
|
+
// Every species the panel offers, when a launch names none. A spec that wants
|
|
43
|
+
// a narrower comparison says so; one that just wants "this gene across
|
|
44
|
+
// species" should not have to enumerate the list the dialog would have
|
|
45
|
+
// checked for it.
|
|
46
|
+
const wantedTaxa = taxa ?? COMMON_SPECIES.map(s => s.taxId);
|
|
47
|
+
// the query species is represented by the query row above
|
|
48
|
+
const wanted = new Set(wantedTaxa.filter(t => t !== taxId));
|
|
32
49
|
const rows = await fetchOrthologRows({
|
|
33
50
|
geneId: resolved.geneId,
|
|
34
51
|
taxa: wanted,
|
|
35
52
|
onProgress,
|
|
36
53
|
});
|
|
37
54
|
const treeMetadata = {
|
|
38
|
-
QUERY:
|
|
55
|
+
QUERY: buildQueryMetadata(self, resolved.geneId, cleanedSeq, representative),
|
|
39
56
|
};
|
|
40
57
|
for (const row of rows) {
|
|
41
58
|
treeMetadata[row.label] = buildRowMetadata(row);
|
|
@@ -54,31 +71,37 @@ export async function doLaunchOrthologs({ self, }) {
|
|
|
54
71
|
};
|
|
55
72
|
}
|
|
56
73
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
|
|
74
|
+
* A failed lookup only costs the query row its domain overlay and, for a launch
|
|
75
|
+
* that supplied no sequence of its own, the alignment — so it is reported by
|
|
76
|
+
* returning nothing rather than by throwing here.
|
|
77
|
+
*/
|
|
78
|
+
async function fetchRepresentativeQueryProtein(geneId) {
|
|
79
|
+
try {
|
|
80
|
+
return await fetchProteinForGene(geneId);
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
console.warn('[msaview-orthologs] query protein lookup failed:', e);
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The query row carries an Accession — which is what drives the automatic CDD
|
|
89
|
+
* overlay (afterCreateAutoruns.autoLoadProteinDomains -> loadProteinDomains) —
|
|
90
|
+
* ONLY when its sequence is byte-identical to the RefSeq protein that accession
|
|
61
91
|
* names. Attaching it unconditionally would put every domain box at an offset
|
|
62
92
|
* whenever the user picked a non-representative isoform, which is a silently
|
|
63
|
-
* wrong figure rather than a missing one.
|
|
93
|
+
* wrong figure rather than a missing one. A launch that took the representative
|
|
94
|
+
* protein as its query row passes that test by construction.
|
|
64
95
|
*/
|
|
65
|
-
|
|
96
|
+
function buildQueryMetadata(self, geneId, proteinSequence, representative) {
|
|
66
97
|
const transcript = self.orthologParams?.selectedTranscript;
|
|
67
98
|
const metadata = { 'Gene ID': geneId };
|
|
68
99
|
const name = transcript?.get('name') ?? transcript?.get('id');
|
|
69
100
|
if (name) {
|
|
70
101
|
metadata.Transcript = name;
|
|
71
102
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (representative?.sequence === proteinSequence) {
|
|
75
|
-
metadata.Accession = representative.accession;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
catch (e) {
|
|
79
|
-
// a failed lookup only costs the query row its domain overlay, so it must
|
|
80
|
-
// not take down an alignment that is otherwise complete
|
|
81
|
-
console.warn('[msaview-orthologs] query protein lookup failed:', e);
|
|
103
|
+
if (representative?.sequence === proteinSequence) {
|
|
104
|
+
metadata.Accession = representative.accession;
|
|
82
105
|
}
|
|
83
106
|
return metadata;
|
|
84
107
|
}
|
|
@@ -23,13 +23,24 @@ export interface BlastParams {
|
|
|
23
23
|
export interface OrthologParams {
|
|
24
24
|
/** NCBI taxon id of the assembly the query gene came from */
|
|
25
25
|
taxId: number;
|
|
26
|
-
/**
|
|
27
|
-
|
|
26
|
+
/**
|
|
27
|
+
* taxon ids to include as rows (the query taxon is represented by QUERY).
|
|
28
|
+
* Omitted means every species the launch dialog offers, which is what a
|
|
29
|
+
* launch that just wants "this gene across species" wants.
|
|
30
|
+
*/
|
|
31
|
+
taxa?: number[];
|
|
28
32
|
/** candidate gene identifiers off the feature, tried in order */
|
|
29
33
|
geneCandidates: string[];
|
|
30
34
|
msaAlgorithm: MsaAlgorithm;
|
|
31
35
|
selectedTranscript?: Feature;
|
|
32
|
-
|
|
36
|
+
/**
|
|
37
|
+
* The QUERY row. The launch dialog always supplies it, translated from the
|
|
38
|
+
* transcript the user picked, which is what `connectedFeature` maps genome
|
|
39
|
+
* coordinates through. Omitted — a session spec naming a gene and nothing
|
|
40
|
+
* else — the query row becomes NCBI's representative protein for the resolved
|
|
41
|
+
* gene, the same choice every other row makes.
|
|
42
|
+
*/
|
|
43
|
+
proteinSequence?: string;
|
|
33
44
|
}
|
|
34
45
|
/**
|
|
35
46
|
* #stateModel MsaViewPlugin
|
|
@@ -53,7 +64,7 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
|
|
|
53
64
|
bgColor: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
54
65
|
colorSchemeName: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
55
66
|
showColumnStats: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
56
|
-
msaFormat: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<import("
|
|
67
|
+
msaFormat: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<import("react-msaview").MSAFormat>>;
|
|
57
68
|
}, "height" | "id" | "type" | "allowedGappyness" | "colWidth" | "collapsed" | "currentAlignment" | "data" | "drawMsaLetters" | "featureFilters" | "gffFilehandle" | "hideGaps" | "highlightColumns" | "msaFilehandle" | "relativeTo" | "rowHeight" | "scrollX" | "scrollY" | "scrollZoom" | "showDomains" | "showOnly" | "subFeatureRows" | "treeFilehandle" | "treeMetadataFilehandle" | "turnedOffTracks"> & {
|
|
58
69
|
id: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
59
70
|
showDomains: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
@@ -507,7 +518,7 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
|
|
|
507
518
|
setColorSchemeName(name: string): void;
|
|
508
519
|
setBgColor(arg: boolean): void;
|
|
509
520
|
setShowColumnStats(arg: boolean): void;
|
|
510
|
-
setMSAFormat(arg?: import("
|
|
521
|
+
setMSAFormat(arg?: import("react-msaview").MSAFormat): void;
|
|
511
522
|
} & {
|
|
512
523
|
headerHeight: number;
|
|
513
524
|
status: {
|
|
@@ -594,7 +605,7 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
|
|
|
594
605
|
readonly noDomains: boolean;
|
|
595
606
|
menuItems(): never[];
|
|
596
607
|
readonly treeMetadata: Record<string, Record<string, string> | undefined>;
|
|
597
|
-
readonly MSA: import("
|
|
608
|
+
readonly MSA: import("react-msaview").MSAParserType | null;
|
|
598
609
|
readonly numColumns: number;
|
|
599
610
|
readonly tree: import("react-msaview").NodeWithIds;
|
|
600
611
|
readonly rowNames: string[];
|
|
@@ -956,7 +967,7 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
|
|
|
956
967
|
bgColor: boolean;
|
|
957
968
|
colorSchemeName: string;
|
|
958
969
|
showColumnStats: boolean;
|
|
959
|
-
msaFormat: import("
|
|
970
|
+
msaFormat: import("react-msaview").MSAFormat | undefined;
|
|
960
971
|
drawLabels: boolean;
|
|
961
972
|
labelsAlignRight: boolean;
|
|
962
973
|
treeAreaWidth: number;
|